Apply PIE to nghttpx
[platform/upstream/nghttp2.git] / third-party / mruby / minirake
1 #!/usr/bin/env ruby
2
3 # Original is https://github.com/jimweirich/rake/
4 # Copyright (c) 2003 Jim Weirich
5 # License: MIT-LICENSE
6
7 require 'getoptlong'
8 require 'fileutils'
9
10 $rake_fiber_table = {}
11 $rake_jobs = 1
12 $rake_failed = []
13
14 class String
15   def ext(newext='')
16     return self.dup if ['.', '..'].include? self
17     if newext != ''
18       newext = (newext =~ /^\./) ? newext : ("." + newext)
19     end
20     self.chomp(File.extname(self)) << newext
21   end
22
23   def pathmap(spec=nil, &block)
24     return self if spec.nil?
25     result = ''
26     spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
27       case frag
28       when '%f'
29         result << File.basename(self)
30       when '%n'
31         result << File.basename(self).ext
32       when '%d'
33         result << File.dirname(self)
34       when '%x'
35         result << File.extname(self)
36       when '%X'
37         result << self.ext
38       when '%p'
39         result << self
40       when '%s'
41         result << (File::ALT_SEPARATOR || File::SEPARATOR)
42       when '%-'
43         # do nothing
44       when '%%'
45         result << "%"
46       when /%(-?\d+)d/
47         result << pathmap_partial($1.to_i)
48       when /^%\{([^}]*)\}(\d*[dpfnxX])/
49         patterns, operator = $1, $2
50         result << pathmap('%' + operator).pathmap_replace(patterns, &block)
51       when /^%/
52         fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
53       else
54         result << frag
55       end
56     end
57     result
58   end
59 end
60
61 module MiniRake
62   class Task
63     TASKS = Hash.new
64     RULES = Array.new
65
66     # List of prerequisites for a task.
67     attr_reader :prerequisites
68
69     # Source dependency for rule synthesized tasks.  Nil if task was not
70     # sythesized from a rule.
71     attr_accessor :source
72
73     # Create a task named +task_name+ with no actions or prerequisites..
74     # use +enhance+ to add actions and prerequisites.
75     def initialize(task_name)
76       @name = task_name
77       @prerequisites = []
78       @actions = []
79     end
80
81     # Enhance a task with prerequisites or actions.  Returns self.
82     def enhance(deps=nil, &block)
83       @prerequisites |= deps if deps
84       @actions << block if block_given?
85       self
86     end
87
88     # Name of the task.
89     def name
90       @name.to_s
91     end
92
93     def done?; @done end
94     def running?; @running end
95
96     # Invoke the task if it is needed. Prerequisites are invoked first.
97     def invoke
98       puts "Invoke #{name} (already=[#{@already_invoked}], needed=[#{needed?}])" if $trace
99       return if @already_invoked
100       prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten
101       prerequisites.each do |n|
102         t = Task[n]
103         unless t.done?
104           return prerequisites.select{|v| v = Task[v]; v && (!v.done? || !v.running?) }
105         end
106       end
107
108       @already_invoked = true
109
110       if needed?
111         @running = true
112         if $rake_root_fiber
113           return Fiber.new do
114             self.execute
115             $rake_root_fiber.transfer
116           end
117         else
118           self.execute
119         end
120       end
121
122       @done = true
123     end
124
125     # Execute the actions associated with this task.
126     def execute
127       puts "Execute #{name}" if $trace
128       self.class.enhance_with_matching_rule(name) if @actions.empty?
129       unless $dryrun
130         @actions.each { |act| act.call(self) }
131       end
132       @done = true
133       @running = false
134     end
135
136     # Is this task needed?
137     def needed?
138       true
139     end
140
141     # Timestamp for this task.  Basic tasks return the current time for
142     # their time stamp.  Other tasks can be more sophisticated.
143     def timestamp
144       Time.now
145     end
146
147     # Class Methods ----------------------------------------------------
148
149     class << self
150
151       # Clear the task list.  This cause rake to immediately forget all
152       # the tasks that have been assigned.  (Normally used in the unit
153       # tests.)
154       def clear
155         TASKS.clear
156         RULES.clear
157       end
158
159       # List of all defined tasks.
160       def tasks
161         TASKS.keys.sort.collect { |tn| Task[tn] }
162       end
163
164       # Return a task with the given name.  If the task is not currently
165       # known, try to synthesize one from the defined rules.  If no
166       # rules are found, but an existing file matches the task name,
167       # assume it is a file task with no dependencies or actions.
168       def [](task_name)
169         task_name = task_name.to_s
170         if task = TASKS[task_name]
171           return task
172         end
173         if task = enhance_with_matching_rule(task_name)
174           return task
175         end
176         if File.exist?(task_name)
177           return FileTask.define_task(task_name)
178         end
179         fail "Don't know how to rake #{task_name}"
180       end
181
182       # Define a task given +args+ and an option block.  If a rule with
183       # the given name already exists, the prerequisites and actions are
184       # added to the existing task.
185       def define_task(args, &block)
186         task_name, deps = resolve_args(args)
187         lookup(task_name).enhance([deps].flatten, &block)
188       end
189
190       # Define a rule for synthesizing tasks.
191       def create_rule(args, &block)
192         pattern, deps = resolve_args(args)
193         pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
194         RULES << [pattern, deps, block]
195       end
196
197
198       # Lookup a task.  Return an existing task if found, otherwise
199       # create a task of the current type.
200       def lookup(task_name)
201         name = task_name.to_s
202         TASKS[name] ||= self.new(name)
203       end
204
205       # If a rule can be found that matches the task name, enhance the
206       # task with the prerequisites and actions from the rule.  Set the
207       # source attribute of the task appropriately for the rule.  Return
208       # the enhanced task or nil of no rule was found.
209       def enhance_with_matching_rule(task_name)
210         RULES.each do |pattern, extensions, block|
211           if pattern.match(task_name)
212             ext = extensions.first
213             deps = extensions[1..-1]
214             case ext
215             when String
216               source = task_name.sub(/\.[^.]*$/, ext)
217             when Proc
218               source = ext.call(task_name)
219             else
220               fail "Don't know how to handle rule dependent: #{ext.inspect}"
221             end
222             if File.exist?(source)
223               task = FileTask.define_task({task_name => [source]+deps}, &block)
224               task.source = source
225               return task
226             end
227           end
228         end
229         nil
230       end
231
232       private
233
234       # Resolve the arguments for a task/rule.
235       def resolve_args(args)
236         case args
237         when Hash
238           fail "Too Many Task Names: #{args.keys.join(' ')}" if args.size > 1
239           fail "No Task Name Given" if args.size < 1
240           task_name = args.keys[0]
241           deps = args[task_name]
242           deps = [deps] if (String===deps) || (Regexp===deps) || (Proc===deps)
243         else
244           task_name = args
245           deps = []
246         end
247         [task_name, deps]
248       end
249     end
250   end
251
252
253   ######################################################################
254   class FileTask < Task
255     # Is this file task needed?  Yes if it doesn't exist, or if its time
256     # stamp is out of date.
257     def needed?
258       return true unless File.exist?(name)
259       prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten
260       latest_prereq = prerequisites.collect{|n| Task[n].timestamp}.max
261       return false if latest_prereq.nil?
262       timestamp < latest_prereq
263     end
264
265     # Time stamp for file task.
266     def timestamp
267       return Time.at(0) unless File.exist?(name)
268       stat = File::stat(name.to_s)
269       stat.directory? ? Time.at(0) : stat.mtime
270     end
271   end
272
273   module DSL
274     # Declare a basic task.
275     def task(args, &block)
276       MiniRake::Task.define_task(args, &block)
277     end
278
279     # Declare a file task.
280     def file(args, &block)
281       MiniRake::FileTask.define_task(args, &block)
282     end
283
284     # Declare a set of files tasks to create the given directories on
285     # demand.
286     def directory(args, &block)
287       MiniRake::FileTask.define_task(args) do |t|
288         block.call(t) unless block.nil?
289         dir = args.is_a?(Hash) ? args.keys.first : args
290         (dir.split(File::SEPARATOR) + ['']).inject do |acc, part|
291           (acc + File::SEPARATOR).tap do |d|
292             Dir.mkdir(d) unless File.exists? d
293           end + part
294         end
295       end
296     end
297
298     # Declare a rule for auto-tasks.
299     def rule(args, &block)
300       MiniRake::Task.create_rule(args, &block)
301     end
302
303     # Write a message to standard out if $verbose is enabled.
304     def log(msg)
305       print "  " if $trace && $verbose
306       puts msg if $verbose
307     end
308
309     # Run the system command +cmd+.
310     def sh(cmd)
311       puts cmd if $verbose
312
313       if !$rake_root_fiber || Fiber.current == $rake_root_fiber
314         system(cmd) or fail "Command Failed: [#{cmd}]"
315         return
316       end
317
318       pid = Process.spawn(cmd)
319       $rake_fiber_table[pid] = {
320         fiber: Fiber.current,
321         command: cmd,
322         process_waiter: Process.detach(pid)
323       }
324       $rake_root_fiber.transfer
325     end
326
327     def desc(text)
328     end
329   end
330 end
331
332 Rake = MiniRake
333 extend MiniRake::DSL
334
335
336 ######################################################################
337 # Task Definition Functions ...
338
339 ######################################################################
340 # Rake main application object.  When invoking +rake+ from the command
341 # line, a RakeApp object is created and run.
342 #
343 class RakeApp
344   RAKEFILES = ['rakefile', 'Rakefile']
345
346   OPTIONS = [
347     ['--dry-run',  '-n', GetoptLong::NO_ARGUMENT,
348       "Do a dry run without executing actions."],
349     ['--help',     '-H', GetoptLong::NO_ARGUMENT,
350       "Display this help message."],
351     ['--libdir',   '-I', GetoptLong::REQUIRED_ARGUMENT,
352       "Include LIBDIR in the search path for required modules."],
353     ['--nosearch', '-N', GetoptLong::NO_ARGUMENT,
354       "Do not search parent directories for the Rakefile."],
355     ['--quiet',    '-q', GetoptLong::NO_ARGUMENT,
356       "Do not log messages to standard output (default)."],
357     ['--rakefile', '-f', GetoptLong::REQUIRED_ARGUMENT,
358       "Use FILE as the rakefile."],
359     ['--require',  '-r', GetoptLong::REQUIRED_ARGUMENT,
360       "Require MODULE before executing rakefile."],
361     ['--tasks',    '-T', GetoptLong::NO_ARGUMENT,
362       "Display the tasks and dependencies, then exit."],
363     ['--pull-gems','-p', GetoptLong::NO_ARGUMENT,
364       "Pull all git mrbgems."],
365     ['--trace',    '-t', GetoptLong::NO_ARGUMENT,
366       "Turn on invoke/execute tracing."],
367     ['--usage',    '-h', GetoptLong::NO_ARGUMENT,
368       "Display usage."],
369     ['--verbose',  '-v', GetoptLong::NO_ARGUMENT,
370       "Log message to standard output."],
371     ['--directory', '-C', GetoptLong::REQUIRED_ARGUMENT,
372       "Change executing directory of rakefiles."],
373     ['--jobs', '-j', GetoptLong::REQUIRED_ARGUMENT,
374       'Execute rake with parallel jobs.']
375   ]
376
377   # Create a RakeApp object.
378   def initialize
379     @rakefile = nil
380     @nosearch = false
381   end
382
383   # True if one of the files in RAKEFILES is in the current directory.
384   # If a match is found, it is copied into @rakefile.
385   def have_rakefile
386     RAKEFILES.each do |fn|
387       if File.exist?(fn)
388         @rakefile = fn
389         return true
390       end
391     end
392     return false
393   end
394
395   # Display the program usage line.
396   def usage
397       puts "rake [-f rakefile] {options} targets..."
398   end
399
400   # Display the rake command line help.
401   def help
402     usage
403     puts
404     puts "Options are ..."
405     puts
406     OPTIONS.sort.each do |long, short, mode, desc|
407       if mode == GetoptLong::REQUIRED_ARGUMENT
408         if desc =~ /\b([A-Z]{2,})\b/
409           long = long + "=#{$1}"
410         end
411       end
412       printf "  %-20s (%s)\n", long, short
413       printf "      %s\n", desc
414     end
415   end
416
417   # Display the tasks and dependencies.
418   def display_tasks
419     MiniRake::Task.tasks.each do |t|
420       puts "#{t.class} #{t.name}"
421       t.prerequisites.each { |pre| puts "    #{pre}" }
422     end
423   end
424
425   # Return a list of the command line options supported by the
426   # program.
427   def command_line_options
428     OPTIONS.collect { |lst| lst[0..-2] }
429   end
430
431   # Do the option defined by +opt+ and +value+.
432   def do_option(opt, value)
433     case opt
434     when '--dry-run'
435       $dryrun = true
436       $trace = true
437     when '--help'
438       help
439       exit
440     when '--libdir'
441       $:.push(value)
442     when '--nosearch'
443       @nosearch = true
444     when '--quiet'
445       $verbose = false
446     when '--rakefile'
447       RAKEFILES.clear
448       RAKEFILES << value
449     when '--require'
450       require value
451     when '--tasks'
452       $show_tasks = true
453     when '--pull-gems'
454       $pull_gems = true
455     when '--trace'
456       $trace = true
457     when '--usage'
458       usage
459       exit
460     when '--verbose'
461       $verbose = true
462     when '--version'
463       puts "rake, version #{RAKEVERSION}"
464       exit
465     when '--directory'
466       Dir.chdir value
467     when '--jobs'
468       $rake_jobs = [value.to_i, 1].max
469     else
470       fail "Unknown option: #{opt}"
471     end
472   end
473
474   # Read and handle the command line options.
475   def handle_options
476     $verbose = false
477     $pull_gems = false
478     opts = GetoptLong.new(*command_line_options)
479     opts.each { |opt, value| do_option(opt, value) }
480   end
481
482   # Run the +rake+ application.
483   def run
484     handle_options
485
486     unless $rake_root_fiber
487       require 'fiber'
488       $rake_root_fiber = Fiber.current
489     end
490
491     begin
492       here = Dir.pwd
493       while ! have_rakefile
494         Dir.chdir("..")
495         if Dir.pwd == here || @nosearch
496           fail "No Rakefile found (looking for: #{RAKEFILES.join(', ')})"
497         end
498         here = Dir.pwd
499       end
500       root_tasks = []
501       ARGV.each do |task_name|
502         if /^(\w+)=(.*)/.match(task_name)
503           ENV[$1] = $2
504         else
505           root_tasks << task_name
506         end
507       end
508       puts "(in #{Dir.pwd})"
509       $rakefile = @rakefile
510       load @rakefile
511       if $show_tasks
512         display_tasks
513       else
514         root_tasks.push("default") if root_tasks.empty?
515         # revese tasks for popping
516         root_tasks.reverse!
517
518         tasks = []
519         until root_tasks.empty?
520           root_name = root_tasks.pop
521           tasks << root_name
522           until tasks.empty?
523             task_name = tasks.pop
524             t = MiniRake::Task[task_name]
525             f = t.invoke
526
527             # append additional tasks to task queue
528             if f.kind_of?(Array)
529               tasks.push(*f)
530               tasks.uniq!
531             end
532
533             unless f.kind_of? Fiber
534               tasks.insert 0, task_name unless t.done?
535               if root_name == task_name
536                 wait_process
537               end
538               next
539             end
540
541             wait_process while $rake_fiber_table.size >= $rake_jobs
542
543             f.transfer
544           end
545         end
546
547         wait_process until $rake_fiber_table.empty?
548       end
549     rescue Exception => e
550       begin
551         $rake_failed << e
552         wait_process until $rake_fiber_table.empty?
553       rescue Exception => next_e
554         e = next_e
555         retry
556       end
557     end
558
559     return if $rake_failed.empty?
560
561     puts "rake aborted!"
562     $rake_failed.each do |ex|
563       puts ex.message
564       if $trace || $verbose
565         puts ex.backtrace.join("\n")
566       else
567         puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
568       end
569     end
570     exit 1
571   end
572
573   def wait_process(count = 0)
574     dur = [0.0001 * (10 ** count), 1].min
575     sleep dur
576
577     exited = []
578     $rake_fiber_table.each do |pid, v|
579       exited << pid unless v[:process_waiter].alive?
580     end
581
582     exited.each do |pid|
583       ent = $rake_fiber_table.delete pid
584       st = ent[:process_waiter].value
585
586       # ignore process that isn't created by `sh` method
587       return if ent.nil?
588
589       if st.exitstatus != 0
590         raise "Command Failed: [#{ent[:command]}]"
591       end
592
593       fail 'task scheduling bug!' if $rake_fiber_table.size >= $rake_jobs
594
595       ent[:fiber].transfer
596     end
597
598     wait_process(count + 1) if !$rake_fiber_table.empty? && exited.empty?
599   end
600
601 end
602
603 if __FILE__ == $0 then
604   RakeApp.new.run
605 end