Upgrade to 1.46.0
[platform/upstream/nghttp2.git] / third-party / mruby / lib / mruby / build / command.rb
1 require 'forwardable'
2
3 module MRuby
4   class Command
5     include Rake::DSL
6     extend Forwardable
7     def_delegators :@build, :filename, :objfile, :libfile, :exefile
8     attr_accessor :build, :command
9
10     def initialize(build)
11       @build = build
12     end
13
14     # clone is deep clone without @build
15     def clone
16       target = super
17       excepts = %w(@build)
18       instance_variables.each do |attr|
19         unless excepts.include?(attr.to_s)
20           val = Marshal::load(Marshal.dump(instance_variable_get(attr))) # deep clone
21           target.instance_variable_set(attr, val)
22         end
23       end
24       target
25     end
26
27     def shellquote(s)
28       if ENV['OS'] == 'Windows_NT'
29         "\"#{s}\""
30       else
31         "#{s}"
32       end
33     end
34
35     NotFoundCommands = {}
36
37     private
38     def _run(options, params={})
39       return sh command + ' ' + ( options % params ) if NotFoundCommands.key? @command
40       begin
41         sh build.filename(command) + ' ' + ( options % params )
42       rescue RuntimeError
43         NotFoundCommands[@command] = true
44         _run options, params
45       end
46     end
47   end
48
49   class Command::Compiler < Command
50     attr_accessor :flags, :include_paths, :defines, :source_exts
51     attr_accessor :compile_options, :option_define, :option_include_path, :out_ext
52     attr_accessor :cxx_compile_flag, :cxx_exception_flag, :cxx_invalid_flags
53
54     def initialize(build, source_exts=[])
55       super(build)
56       @command = ENV['CC'] || 'cc'
57       @flags = [ENV['CFLAGS'] || []]
58       @source_exts = source_exts
59       @include_paths = ["#{MRUBY_ROOT}/include"]
60       @defines = %w()
61       @option_include_path = %q[-I"%s"]
62       @option_define = %q[-D"%s"]
63       @compile_options = %q[%{flags} -o "%{outfile}" -c "%{infile}"]
64       @cxx_invalid_flags = []
65     end
66
67     alias header_search_paths include_paths
68     def search_header_path(name)
69       header_search_paths.find do |v|
70         File.exist? build.filename("#{v}/#{name}").sub(/^"(.*)"$/, '\1')
71       end
72     end
73
74     def search_header(name)
75       path = search_header_path name
76       path && build.filename("#{path}/#{name}").sub(/^"(.*)"$/, '\1')
77     end
78
79     def all_flags(_defines=[], _include_paths=[], _flags=[])
80       define_flags = [defines, _defines].flatten.map{ |d| option_define % d }
81       include_path_flags = [include_paths, _include_paths].flatten.map do |f|
82         option_include_path % filename(f)
83       end
84       [flags, define_flags, include_path_flags, _flags].flatten.join(' ')
85     end
86
87     def run(outfile, infile, _defines=[], _include_paths=[], _flags=[])
88       mkdir_p File.dirname(outfile)
89       _pp "CC", infile.relative_path, outfile.relative_path
90       _run compile_options, { :flags => all_flags(_defines, _include_paths, _flags),
91                               :infile => filename(infile), :outfile => filename(outfile) }
92     end
93
94     def define_rules(build_dir, source_dir='')
95       @out_ext = build.exts.object
96       gemrake = File.join(source_dir, "mrbgem.rake")
97       rakedep = File.exist?(gemrake) ? [ gemrake ] : []
98
99       if build_dir.include? "mrbgems/"
100         generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(.*)#{Regexp.escape out_ext}$")
101       else
102         generated_file_matcher = Regexp.new("^#{Regexp.escape build_dir}/(?!mrbgems/.+/)(.*)#{Regexp.escape out_ext}$")
103       end
104       source_exts.each do |ext, compile|
105         rule generated_file_matcher => [
106           proc { |file|
107             file.sub(generated_file_matcher, "#{source_dir}/\\1#{ext}")
108           },
109           proc { |file|
110             get_dependencies(file) + rakedep
111           }
112         ] do |t|
113           run t.name, t.prerequisites.first
114         end
115
116         rule generated_file_matcher => [
117           proc { |file|
118             file.sub(generated_file_matcher, "#{build_dir}/\\1#{ext}")
119           },
120           proc { |file|
121             get_dependencies(file) + rakedep
122           }
123         ] do |t|
124           run t.name, t.prerequisites.first
125         end
126       end
127     end
128
129     private
130
131     #
132     # === Example of +.d+ file
133     #
134     # ==== Without <tt>-MP</tt> compiler flag
135     #
136     #   /build/host/src/array.o: \
137     #     /src/array.c \
138     #     /include/mruby/common.h \
139     #     /include/mruby/value.h \
140     #     /src/value_array.h
141     #
142     # ==== With <tt>-MP</tt> compiler flag
143     #
144     #   /build/host/src/array.o: \
145     #     /src/array.c \
146     #     /include/mruby/common.h \
147     #     /include/mruby/value.h \
148     #     /src/value_array.h
149     #
150     #   /include/mruby/common.h:
151     #
152     #   /include/mruby/value.h:
153     #
154     #   /src/value_array.h:
155     #
156     def get_dependencies(file)
157       file = file.ext('d') unless File.extname(file) == '.d'
158       deps = []
159       if File.exist?(file)
160         File.foreach(file){|line| deps << $1 if /^ +(.*?)(?: *\\)?$/ =~ line}
161         deps.uniq!
162       end
163       deps << MRUBY_CONFIG
164     end
165   end
166
167   class Command::Linker < Command
168     attr_accessor :flags, :library_paths, :flags_before_libraries, :libraries, :flags_after_libraries
169     attr_accessor :link_options, :option_library, :option_library_path
170
171     def initialize(build)
172       super
173       @command = ENV['LD'] || 'ld'
174       @flags = (ENV['LDFLAGS'] || [])
175       @flags_before_libraries, @flags_after_libraries = [], []
176       @libraries = []
177       @library_paths = []
178       @option_library = %q[-l"%s"]
179       @option_library_path = %q[-L"%s"]
180       @link_options = %Q[%{flags} -o "%{outfile}" %{objs} %{flags_before_libraries} %{libs} %{flags_after_libraries}]
181     end
182
183     def all_flags(_library_paths=[], _flags=[])
184       library_path_flags = [library_paths, _library_paths].flatten.map do |f|
185         option_library_path % filename(f)
186       end
187       [flags, library_path_flags, _flags].flatten.join(' ')
188     end
189
190     def library_flags(_libraries)
191       [libraries, _libraries].flatten.map{ |d| option_library % d }.join(' ')
192     end
193
194     def run(outfile, objfiles, _libraries=[], _library_paths=[], _flags=[], _flags_before_libraries=[], _flags_after_libraries=[])
195       mkdir_p File.dirname(outfile)
196       library_flags = [libraries, _libraries].flatten.map { |d| option_library % d }
197
198       _pp "LD", outfile.relative_path
199       _run link_options, { :flags => all_flags(_library_paths, _flags),
200                             :outfile => filename(outfile) , :objs => filename(objfiles).map{|f| %Q["#{f}"]}.join(' '),
201                             :flags_before_libraries => [flags_before_libraries, _flags_before_libraries].flatten.join(' '),
202                             :flags_after_libraries => [flags_after_libraries, _flags_after_libraries].flatten.join(' '),
203                             :libs => library_flags.join(' ') }
204     end
205   end
206
207   class Command::Archiver < Command
208     attr_accessor :archive_options
209
210     def initialize(build)
211       super
212       @command = ENV['AR'] || 'ar'
213       @archive_options = 'rs "%{outfile}" %{objs}'
214     end
215
216     def run(outfile, objfiles)
217       mkdir_p File.dirname(outfile)
218       _pp "AR", outfile.relative_path
219       _run archive_options, { :outfile => filename(outfile), :objs => filename(objfiles).map{|f| %Q["#{f}"]}.join(' ') }
220     end
221   end
222
223   class Command::Yacc < Command
224     attr_accessor :compile_options
225
226     def initialize(build)
227       super
228       @command = 'bison'
229       @compile_options = %q[-o "%{outfile}" "%{infile}"]
230     end
231
232     def run(outfile, infile)
233       mkdir_p File.dirname(outfile)
234       _pp "YACC", infile.relative_path, outfile.relative_path
235       _run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) }
236     end
237   end
238
239   class Command::Gperf < Command
240     attr_accessor :compile_options
241
242     def initialize(build)
243       super
244       @command = 'gperf'
245       @compile_options = %q[-L ANSI-C -C -p -j1 -i 1 -g -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"]
246     end
247
248     def run(outfile, infile)
249       mkdir_p File.dirname(outfile)
250       _pp "GPERF", infile.relative_path, outfile.relative_path
251       _run compile_options, { :outfile => filename(outfile) , :infile => filename(infile) }
252     end
253   end
254
255   class Command::Git < Command
256     attr_accessor :flags
257     attr_accessor :clone_options, :pull_options, :checkout_options, :checkout_detach_options, :reset_options
258
259     def initialize(build)
260       super
261       @command = 'git'
262       @flags = %w[]
263       @clone_options = "clone %{flags} %{url} %{dir}"
264       @pull_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} pull"
265       @checkout_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} checkout %{checksum_hash}"
266       @checkout_detach_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} checkout --detach %{checksum_hash}"
267       @reset_options = "--git-dir %{repo_dir}/.git --work-tree %{repo_dir} reset %{checksum_hash}"
268     end
269
270     def run_clone(dir, url, _flags = [])
271       _pp "GIT", url, dir.relative_path
272       _run clone_options, { :flags => [flags, _flags].flatten.join(' '), :url => shellquote(url), :dir => shellquote(filename(dir)) }
273     end
274
275     def run_pull(dir, url)
276       _pp "GIT PULL", url, dir.relative_path
277       _run pull_options, { :repo_dir => shellquote(dir) }
278     end
279
280     def run_checkout(dir, checksum_hash)
281       _pp "GIT CHECKOUT", dir, checksum_hash
282       _run checkout_options, { :checksum_hash => checksum_hash, :repo_dir => shellquote(dir) }
283     end
284
285     def run_checkout_detach(dir, checksum_hash)
286       _pp "GIT CHECKOUT DETACH", dir, checksum_hash
287       _run checkout_detach_options, { :checksum_hash => checksum_hash, :repo_dir => shellquote(dir) }
288     end
289
290     def run_reset_hard(dir, checksum_hash)
291       _pp "GIT RESET", dir, checksum_hash
292       _run reset_options, { :checksum_hash => checksum_hash, :repo_dir => shellquote(dir) }
293     end
294
295     def commit_hash(dir)
296       `#{@command} --git-dir #{shellquote(dir +'/.git')} --work-tree #{shellquote(dir)} rev-parse --verify HEAD`.strip
297     end
298
299     def current_branch(dir)
300       `#{@command} --git-dir #{shellquote(dir + '/.git')} --work-tree #{shellquote(dir)} rev-parse --abbrev-ref HEAD`.strip
301     end
302   end
303
304   class Command::Mrbc < Command
305     attr_accessor :compile_options
306
307     def initialize(build)
308       super
309       @command = nil
310       @compile_options = "-B%{funcname} -o-"
311     end
312
313     def run(out, infiles, funcname)
314       @command ||= @build.mrbcfile
315       infiles = [infiles].flatten
316       infiles.each do |f|
317         _pp "MRBC", f.relative_path, nil, :indent => 2
318       end
319       cmd = %Q["#{filename @command}" #{@compile_options % {:funcname => funcname}} #{filename(infiles).map{|f| %Q["#{f}"]}.join(' ')}]
320       puts cmd if Rake.verbose
321       IO.popen(cmd, 'r+') do |io|
322         out.puts io.read
323       end
324       # if mrbc execution fail, drop the file
325       if $?.exitstatus != 0
326         File.delete(out.path)
327         exit(-1)
328       end
329     end
330   end
331
332   class Command::CrossTestRunner < Command
333     attr_accessor :runner_options
334     attr_accessor :verbose_flag
335     attr_accessor :flags
336
337     def initialize(build)
338       super
339       @command = nil
340       @runner_options = '%{flags} %{infile}'
341       @verbose_flag = ''
342       @flags = []
343     end
344
345     def run(testbinfile)
346       puts "TEST for " + @build.name
347       _run runner_options, { :flags => [flags, verbose_flag].flatten.join(' '), :infile => testbinfile }
348     end
349   end
350
351 end