mirror of https://gitee.com/bigwinds/arangodb
478 lines
13 KiB
Ruby
Executable File
478 lines
13 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
|
|
# Original is https://github.com/jimweirich/rake/
|
|
# Copyright (c) 2003 Jim Weirich
|
|
# License: MIT-LICENSE
|
|
|
|
require 'getoptlong'
|
|
require 'fileutils'
|
|
|
|
class String
|
|
def ext(newext='')
|
|
return self.dup if ['.', '..'].include? self
|
|
if newext != ''
|
|
newext = (newext =~ /^\./) ? newext : ("." + newext)
|
|
end
|
|
self.chomp(File.extname(self)) << newext
|
|
end
|
|
|
|
def pathmap(spec=nil, &block)
|
|
return self if spec.nil?
|
|
result = ''
|
|
spec.scan(/%\{[^}]*\}-?\d*[sdpfnxX%]|%-?\d+d|%.|[^%]+/) do |frag|
|
|
case frag
|
|
when '%f'
|
|
result << File.basename(self)
|
|
when '%n'
|
|
result << File.basename(self).ext
|
|
when '%d'
|
|
result << File.dirname(self)
|
|
when '%x'
|
|
result << File.extname(self)
|
|
when '%X'
|
|
result << self.ext
|
|
when '%p'
|
|
result << self
|
|
when '%s'
|
|
result << (File::ALT_SEPARATOR || File::SEPARATOR)
|
|
when '%-'
|
|
# do nothing
|
|
when '%%'
|
|
result << "%"
|
|
when /%(-?\d+)d/
|
|
result << pathmap_partial($1.to_i)
|
|
when /^%\{([^}]*)\}(\d*[dpfnxX])/
|
|
patterns, operator = $1, $2
|
|
result << pathmap('%' + operator).pathmap_replace(patterns, &block)
|
|
when /^%/
|
|
fail ArgumentError, "Unknown pathmap specifier #{frag} in '#{spec}'"
|
|
else
|
|
result << frag
|
|
end
|
|
end
|
|
result
|
|
end
|
|
end
|
|
|
|
module MiniRake
|
|
class Task
|
|
TASKS = Hash.new
|
|
RULES = Array.new
|
|
|
|
# List of prerequisites for a task.
|
|
attr_reader :prerequisites
|
|
|
|
# Source dependency for rule synthesized tasks. Nil if task was not
|
|
# sythesized from a rule.
|
|
attr_accessor :source
|
|
|
|
# Create a task named +task_name+ with no actions or prerequisites..
|
|
# use +enhance+ to add actions and prerequisites.
|
|
def initialize(task_name)
|
|
@name = task_name
|
|
@prerequisites = []
|
|
@actions = []
|
|
end
|
|
|
|
# Enhance a task with prerequisites or actions. Returns self.
|
|
def enhance(deps=nil, &block)
|
|
@prerequisites |= deps if deps
|
|
@actions << block if block_given?
|
|
self
|
|
end
|
|
|
|
# Name of the task.
|
|
def name
|
|
@name.to_s
|
|
end
|
|
|
|
# Invoke the task if it is needed. Prerequites are invoked first.
|
|
def invoke
|
|
puts "Invoke #{name} (already=[#{@already_invoked}], needed=[#{needed?}])" if $trace
|
|
return if @already_invoked
|
|
@already_invoked = true
|
|
prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten
|
|
prerequisites.each { |n| Task[n].invoke }
|
|
execute if needed?
|
|
end
|
|
|
|
# Execute the actions associated with this task.
|
|
def execute
|
|
puts "Execute #{name}" if $trace
|
|
self.class.enhance_with_matching_rule(name) if @actions.empty?
|
|
unless $dryrun
|
|
@actions.each { |act| act.call(self) }
|
|
end
|
|
end
|
|
|
|
# Is this task needed?
|
|
def needed?
|
|
true
|
|
end
|
|
|
|
# Timestamp for this task. Basic tasks return the current time for
|
|
# their time stamp. Other tasks can be more sophisticated.
|
|
def timestamp
|
|
prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten
|
|
prerequisites.collect { |n| Task[n].timestamp }.max || Time.now
|
|
end
|
|
|
|
# Class Methods ----------------------------------------------------
|
|
|
|
class << self
|
|
|
|
# Clear the task list. This cause rake to immediately forget all
|
|
# the tasks that have been assigned. (Normally used in the unit
|
|
# tests.)
|
|
def clear
|
|
TASKS.clear
|
|
RULES.clear
|
|
end
|
|
|
|
# List of all defined tasks.
|
|
def tasks
|
|
TASKS.keys.sort.collect { |tn| Task[tn] }
|
|
end
|
|
|
|
# Return a task with the given name. If the task is not currently
|
|
# known, try to synthesize one from the defined rules. If no
|
|
# rules are found, but an existing file matches the task name,
|
|
# assume it is a file task with no dependencies or actions.
|
|
def [](task_name)
|
|
task_name = task_name.to_s
|
|
if task = TASKS[task_name]
|
|
return task
|
|
end
|
|
if task = enhance_with_matching_rule(task_name)
|
|
return task
|
|
end
|
|
if File.exist?(task_name)
|
|
return FileTask.define_task(task_name)
|
|
end
|
|
fail "Don't know how to rake #{task_name}"
|
|
end
|
|
|
|
# Define a task given +args+ and an option block. If a rule with
|
|
# the given name already exists, the prerequisites and actions are
|
|
# added to the existing task.
|
|
def define_task(args, &block)
|
|
task_name, deps = resolve_args(args)
|
|
lookup(task_name).enhance([deps].flatten, &block)
|
|
end
|
|
|
|
# Define a rule for synthesizing tasks.
|
|
def create_rule(args, &block)
|
|
pattern, deps = resolve_args(args)
|
|
pattern = Regexp.new(Regexp.quote(pattern) + '$') if String === pattern
|
|
RULES << [pattern, deps, block]
|
|
end
|
|
|
|
|
|
# Lookup a task. Return an existing task if found, otherwise
|
|
# create a task of the current type.
|
|
def lookup(task_name)
|
|
name = task_name.to_s
|
|
TASKS[name] ||= self.new(name)
|
|
end
|
|
|
|
# If a rule can be found that matches the task name, enhance the
|
|
# task with the prerequisites and actions from the rule. Set the
|
|
# source attribute of the task appropriately for the rule. Return
|
|
# the enhanced task or nil of no rule was found.
|
|
def enhance_with_matching_rule(task_name)
|
|
RULES.each do |pattern, extensions, block|
|
|
if pattern.match(task_name)
|
|
ext = extensions.first
|
|
deps = extensions[1..-1]
|
|
case ext
|
|
when String
|
|
source = task_name.sub(/\.[^.]*$/, ext)
|
|
when Proc
|
|
source = ext.call(task_name)
|
|
else
|
|
fail "Don't know how to handle rule dependent: #{ext.inspect}"
|
|
end
|
|
if File.exist?(source)
|
|
task = FileTask.define_task({task_name => [source]+deps}, &block)
|
|
task.source = source
|
|
return task
|
|
end
|
|
end
|
|
end
|
|
nil
|
|
end
|
|
|
|
private
|
|
|
|
# Resolve the arguments for a task/rule.
|
|
def resolve_args(args)
|
|
case args
|
|
when Hash
|
|
fail "Too Many Task Names: #{args.keys.join(' ')}" if args.size > 1
|
|
fail "No Task Name Given" if args.size < 1
|
|
task_name = args.keys[0]
|
|
deps = args[task_name]
|
|
deps = [deps] if (String===deps) || (Regexp===deps) || (Proc===deps)
|
|
else
|
|
task_name = args
|
|
deps = []
|
|
end
|
|
[task_name, deps]
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
######################################################################
|
|
class FileTask < Task
|
|
# Is this file task needed? Yes if it doesn't exist, or if its time
|
|
# stamp is out of date.
|
|
def needed?
|
|
return true unless File.exist?(name)
|
|
prerequisites = @prerequisites.collect{ |n| n.is_a?(Proc) ? n.call(name) : n }.flatten
|
|
latest_prereq = prerequisites.collect{|n| Task[n].timestamp}.max
|
|
return false if latest_prereq.nil?
|
|
timestamp < latest_prereq
|
|
end
|
|
|
|
# Time stamp for file task.
|
|
def timestamp
|
|
File::stat(name.to_s).mtime
|
|
end
|
|
end
|
|
|
|
module DSL
|
|
# Declare a basic task.
|
|
def task(args, &block)
|
|
MiniRake::Task.define_task(args, &block)
|
|
end
|
|
|
|
# Declare a file task.
|
|
def file(args, &block)
|
|
MiniRake::FileTask.define_task(args, &block)
|
|
end
|
|
|
|
# Declare a set of files tasks to create the given directories on
|
|
# demand.
|
|
def directory(dir)
|
|
path = []
|
|
Sys.split_all(dir).each do |p|
|
|
path << p
|
|
FileTask.define_task(File.join(path)) do |t|
|
|
Sys.makedirs(t.name)
|
|
end
|
|
end
|
|
end
|
|
|
|
# Declare a rule for auto-tasks.
|
|
def rule(args, &block)
|
|
MiniRake::Task.create_rule(args, &block)
|
|
end
|
|
|
|
# Write a message to standard out if $verbose is enabled.
|
|
def log(msg)
|
|
print " " if $trace && $verbose
|
|
puts msg if $verbose
|
|
end
|
|
|
|
# Run the system command +cmd+.
|
|
def sh(cmd)
|
|
puts cmd if $verbose
|
|
system(cmd) or fail "Command Failed: [#{cmd}]"
|
|
end
|
|
|
|
def desc(text)
|
|
end
|
|
end
|
|
end
|
|
|
|
Rake = MiniRake
|
|
extend MiniRake::DSL
|
|
|
|
|
|
######################################################################
|
|
# Task Definition Functions ...
|
|
|
|
######################################################################
|
|
# Rake main application object. When invoking +rake+ from the command
|
|
# line, a RakeApp object is created and run.
|
|
#
|
|
class RakeApp
|
|
RAKEFILES = ['rakefile', 'Rakefile']
|
|
|
|
OPTIONS = [
|
|
['--dry-run', '-n', GetoptLong::NO_ARGUMENT,
|
|
"Do a dry run without executing actions."],
|
|
['--help', '-H', GetoptLong::NO_ARGUMENT,
|
|
"Display this help message."],
|
|
['--libdir', '-I', GetoptLong::REQUIRED_ARGUMENT,
|
|
"Include LIBDIR in the search path for required modules."],
|
|
['--nosearch', '-N', GetoptLong::NO_ARGUMENT,
|
|
"Do not search parent directories for the Rakefile."],
|
|
['--quiet', '-q', GetoptLong::NO_ARGUMENT,
|
|
"Do not log messages to standard output."],
|
|
['--rakefile', '-f', GetoptLong::REQUIRED_ARGUMENT,
|
|
"Use FILE as the rakefile."],
|
|
['--require', '-r', GetoptLong::REQUIRED_ARGUMENT,
|
|
"Require MODULE before executing rakefile."],
|
|
['--tasks', '-T', GetoptLong::NO_ARGUMENT,
|
|
"Display the tasks and dependencies, then exit."],
|
|
['--pull-gems','-p', GetoptLong::NO_ARGUMENT,
|
|
"Pull all git mrbgems."],
|
|
['--trace', '-t', GetoptLong::NO_ARGUMENT,
|
|
"Turn on invoke/execute tracing."],
|
|
['--usage', '-h', GetoptLong::NO_ARGUMENT,
|
|
"Display usage."],
|
|
['--verbose', '-v', GetoptLong::NO_ARGUMENT,
|
|
"Log message to standard output (default)."],
|
|
]
|
|
|
|
# Create a RakeApp object.
|
|
def initialize
|
|
@rakefile = nil
|
|
@nosearch = false
|
|
end
|
|
|
|
# True if one of the files in RAKEFILES is in the current directory.
|
|
# If a match is found, it is copied into @rakefile.
|
|
def have_rakefile
|
|
RAKEFILES.each do |fn|
|
|
if File.exist?(fn)
|
|
@rakefile = fn
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
# Display the program usage line.
|
|
def usage
|
|
puts "rake [-f rakefile] {options} targets..."
|
|
end
|
|
|
|
# Display the rake command line help.
|
|
def help
|
|
usage
|
|
puts
|
|
puts "Options are ..."
|
|
puts
|
|
OPTIONS.sort.each do |long, short, mode, desc|
|
|
if mode == GetoptLong::REQUIRED_ARGUMENT
|
|
if desc =~ /\b([A-Z]{2,})\b/
|
|
long = long + "=#{$1}"
|
|
end
|
|
end
|
|
printf " %-20s (%s)\n", long, short
|
|
printf " %s\n", desc
|
|
end
|
|
end
|
|
|
|
# Display the tasks and dependencies.
|
|
def display_tasks
|
|
MiniRake::Task.tasks.each do |t|
|
|
puts "#{t.class} #{t.name}"
|
|
t.prerequisites.each { |pre| puts " #{pre}" }
|
|
end
|
|
end
|
|
|
|
# Return a list of the command line options supported by the
|
|
# program.
|
|
def command_line_options
|
|
OPTIONS.collect { |lst| lst[0..-2] }
|
|
end
|
|
|
|
# Do the option defined by +opt+ and +value+.
|
|
def do_option(opt, value)
|
|
case opt
|
|
when '--dry-run'
|
|
$dryrun = true
|
|
$trace = true
|
|
when '--help'
|
|
help
|
|
exit
|
|
when '--libdir'
|
|
$:.push(value)
|
|
when '--nosearch'
|
|
@nosearch = true
|
|
when '--quiet'
|
|
$verbose = false
|
|
when '--rakefile'
|
|
RAKEFILES.clear
|
|
RAKEFILES << value
|
|
when '--require'
|
|
require value
|
|
when '--tasks'
|
|
$show_tasks = true
|
|
when '--pull-gems'
|
|
$pull_gems = true
|
|
when '--trace'
|
|
$trace = true
|
|
when '--usage'
|
|
usage
|
|
exit
|
|
when '--verbose'
|
|
$verbose = true
|
|
when '--version'
|
|
puts "rake, version #{RAKEVERSION}"
|
|
exit
|
|
else
|
|
fail "Unknown option: #{opt}"
|
|
end
|
|
end
|
|
|
|
# Read and handle the command line options.
|
|
def handle_options
|
|
$verbose = false
|
|
$pull_gems = false
|
|
opts = GetoptLong.new(*command_line_options)
|
|
opts.each { |opt, value| do_option(opt, value) }
|
|
end
|
|
|
|
# Run the +rake+ application.
|
|
def run
|
|
handle_options
|
|
begin
|
|
here = Dir.pwd
|
|
while ! have_rakefile
|
|
Dir.chdir("..")
|
|
if Dir.pwd == here || @nosearch
|
|
fail "No Rakefile found (looking for: #{RAKEFILES.join(', ')})"
|
|
end
|
|
here = Dir.pwd
|
|
end
|
|
tasks = []
|
|
ARGV.each do |task_name|
|
|
if /^(\w+)=(.*)/.match(task_name)
|
|
ENV[$1] = $2
|
|
else
|
|
tasks << task_name
|
|
end
|
|
end
|
|
puts "(in #{Dir.pwd})"
|
|
$rakefile = @rakefile
|
|
load @rakefile
|
|
if $show_tasks
|
|
display_tasks
|
|
else
|
|
tasks.push("default") if tasks.size == 0
|
|
tasks.each do |task_name|
|
|
MiniRake::Task[task_name].invoke
|
|
end
|
|
end
|
|
rescue Exception => ex
|
|
puts "rake aborted!"
|
|
puts ex.message
|
|
if $trace
|
|
puts ex.backtrace.join("\n")
|
|
else
|
|
puts ex.backtrace.find {|str| str =~ /#{@rakefile}/ } || ""
|
|
end
|
|
exit 1
|
|
end
|
|
end
|
|
end
|
|
|
|
if __FILE__ == $0 then
|
|
RakeApp.new.run
|
|
end
|