Add tasks - close #139
This commit is contained in:
1 parent
f5ab51c477
commit
bd9cf55b85
149 files changed
+1578
-1063
No files matched your search
+1
-7
@@ -10,6 +10,7 @@ require_relative "rscons/configure_op"
|
||||
require_relative "rscons/default_construction_variables"
|
||||
require_relative "rscons/environment"
|
||||
require_relative "rscons/script"
|
||||
require_relative "rscons/task"
|
||||
require_relative "rscons/util"
|
||||
require_relative "rscons/varset"
|
||||
require_relative "rscons/version"
|
||||
@@ -49,13 +50,6 @@ module Rscons
|
||||
@application ||= Application.new
|
||||
end
|
||||
|
||||
# Access any variables set on the rscons command-line.
|
||||
#
|
||||
# @return [VarSet]
|
||||
def vars(*args)
|
||||
application.vars(*args)
|
||||
end
|
||||
|
||||
# Return whether the given target is a phony target.
|
||||
#
|
||||
# @param target [Symbol, String] Target name.
|
||||
|
||||
+53
-127
@@ -17,141 +17,57 @@ module Rscons
|
||||
# The number of threads to use when scheduling subprocesses.
|
||||
attr_accessor :n_threads
|
||||
|
||||
# @return [Script]
|
||||
# Build script.
|
||||
attr_reader :script
|
||||
|
||||
# @return [Boolean]
|
||||
# Whether to run verbosely.
|
||||
attr_accessor :verbose
|
||||
|
||||
# @return [VarSet]
|
||||
# Access any variables set on the rscons command-line.
|
||||
attr_reader :vars
|
||||
|
||||
# Create Application instance.
|
||||
def initialize
|
||||
@script = Script.new
|
||||
@build_dir = ENV["RSCONS_BUILD_DIR"] || "build"
|
||||
ENV.delete("RSCONS_BUILD_DIR")
|
||||
@n_threads = Util.determine_n_threads
|
||||
@vars = VarSet.new
|
||||
@operations = Set.new
|
||||
@build_step = 0
|
||||
end
|
||||
|
||||
# Check whether a requested operation is active.
|
||||
# Run the application.
|
||||
#
|
||||
# @param op [String]
|
||||
# Operation name.
|
||||
# Execute user-specified tasks.
|
||||
#
|
||||
# @return [Boolean]
|
||||
# Whether the requested operation is active.
|
||||
def operation(op)
|
||||
@operations.include?(op)
|
||||
end
|
||||
|
||||
# Run the specified operation.
|
||||
# @api private
|
||||
#
|
||||
# @param operation [String]
|
||||
# The operation to perform (e.g. "clean", "configure", "build", etc...)
|
||||
# @param script [Script]
|
||||
# The script.
|
||||
# @param operation_options [Hash]
|
||||
# Option values from the CLI for the operation.
|
||||
# @param options [Hash]
|
||||
# Optional parameters.
|
||||
# @option sub_op [Boolean]
|
||||
# Whether this operation is not the top-level operation.
|
||||
# @param tasks [Array<String>]
|
||||
# List of task(s) to execute.
|
||||
#
|
||||
# @return [Integer]
|
||||
# Process exit code (0 on success).
|
||||
def run(operation, script, operation_options, options = {})
|
||||
@start_time = Time.new
|
||||
@script = script
|
||||
@operations << operation
|
||||
puts "Starting '#{operation}' at #{Time.new}" if verbose
|
||||
rv =
|
||||
case operation
|
||||
when "build"
|
||||
rv = 0
|
||||
unless Cache.instance["configuration_data"]["configured"]
|
||||
rv =
|
||||
if @script.autoconf
|
||||
run("configure", script, operation_options, sub_op: false)
|
||||
else
|
||||
$stderr.puts "Project must be configured first, and autoconf is disabled"
|
||||
1
|
||||
end
|
||||
end
|
||||
if rv == 0
|
||||
build(operation_options)
|
||||
else
|
||||
rv
|
||||
end
|
||||
when "clean"
|
||||
clean
|
||||
when "configure"
|
||||
configure(operation_options)
|
||||
when "distclean"
|
||||
distclean
|
||||
when "install"
|
||||
run("build", script, operation_options, sub_op: false)
|
||||
when "uninstall"
|
||||
uninstall
|
||||
else
|
||||
$stderr.puts "Unknown operation: #{operation}"
|
||||
1
|
||||
end
|
||||
if verbose and options[:sub_op].nil?
|
||||
time = Time.new
|
||||
elapsed = time - @start_time
|
||||
puts "'#{operation}' complete at #{time} (#{Util.format_elapsed_time(elapsed)})"
|
||||
def run(tasks)
|
||||
Cache.instance["failed_commands"] = []
|
||||
tasks.each do |task|
|
||||
Task[task].check_execute
|
||||
end
|
||||
rv
|
||||
0
|
||||
end
|
||||
|
||||
# Get the next build step number.
|
||||
# Show the last failures.
|
||||
#
|
||||
# This is used internally by the {Environment} class.
|
||||
#
|
||||
# @api private
|
||||
def get_next_build_step
|
||||
@build_step += 1
|
||||
end
|
||||
|
||||
# Get the total number of build steps.
|
||||
#
|
||||
# @return [Integer]
|
||||
# The total number of build steps.
|
||||
def get_total_build_steps
|
||||
Environment.environments.reduce(@build_step) do |result, env|
|
||||
result + env.build_steps_remaining
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Build the project.
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
#
|
||||
# @return [Integer]
|
||||
# Exit code.
|
||||
def build(options)
|
||||
begin
|
||||
Cache.instance["failed_commands"] = []
|
||||
@script.build
|
||||
Environment.environments.each do |env|
|
||||
env.process
|
||||
end
|
||||
0
|
||||
rescue RsconsError => e
|
||||
Ansi.write($stderr, :red, e.message, :reset, "\n")
|
||||
1
|
||||
# @return [void]
|
||||
def show_failure
|
||||
failed_commands = Cache.instance["failed_commands"]
|
||||
failed_commands.each_with_index do |command, i|
|
||||
Ansi.write($stdout, :red, "Failed command (#{i + 1}/#{failed_commands.size}):", :reset, "\n")
|
||||
$stdout.puts Util.command_to_s(command)
|
||||
end
|
||||
end
|
||||
|
||||
# Remove all generated files.
|
||||
#
|
||||
# @return [Integer]
|
||||
# Exit code.
|
||||
# @api private
|
||||
#
|
||||
# @return [void]
|
||||
def clean
|
||||
cache = Cache.instance
|
||||
# remove all built files
|
||||
@@ -168,49 +84,60 @@ module Rscons
|
||||
end
|
||||
end
|
||||
cache.write
|
||||
0
|
||||
end
|
||||
|
||||
# Remove the build directory and clear the cache.
|
||||
#
|
||||
# @return [Integer]
|
||||
# Exit code.
|
||||
# @api private
|
||||
#
|
||||
# @return [void]
|
||||
def distclean
|
||||
cache = Cache.instance
|
||||
clean
|
||||
FileUtils.rm_rf(@build_dir)
|
||||
cache.clear
|
||||
0
|
||||
end
|
||||
|
||||
# Check if the project needs to be configured.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @return [void]
|
||||
def check_configure
|
||||
unless Cache.instance["configuration_data"]["configured"]
|
||||
if @script.autoconf
|
||||
configure
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Configure the project.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
#
|
||||
# @return [Integer]
|
||||
# Exit code.
|
||||
def configure(options)
|
||||
rv = 0
|
||||
options = options.merge(project_name: @script.project_name)
|
||||
co = ConfigureOp.new(options)
|
||||
# @return [void]
|
||||
def configure
|
||||
co = ConfigureOp.new(@script)
|
||||
begin
|
||||
@script.configure(co)
|
||||
rescue RsconsError => e
|
||||
if e.message and e.message != ""
|
||||
$stderr.puts e.message
|
||||
end
|
||||
Ansi.write($stderr, :red, "Configuration failed", :reset, "\n")
|
||||
rv = 1
|
||||
co.close(false)
|
||||
raise e
|
||||
end
|
||||
co.close(rv == 0)
|
||||
rv
|
||||
co.close(true)
|
||||
end
|
||||
|
||||
# Remove installed files.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @return [Integer]
|
||||
# Exit code.
|
||||
#
|
||||
# @return [void]
|
||||
def uninstall
|
||||
cache = Cache.instance
|
||||
cache.targets(true).each do |target|
|
||||
@@ -229,7 +156,6 @@ module Rscons
|
||||
end
|
||||
end
|
||||
cache.write
|
||||
0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -87,14 +87,6 @@ module Rscons
|
||||
self.class.name
|
||||
end
|
||||
|
||||
# Return whether the builder is a no-op.
|
||||
#
|
||||
# @return [Boolean]
|
||||
# Whether the builder is a no-op.
|
||||
def nop?
|
||||
false
|
||||
end
|
||||
|
||||
# Manually record a given build target as depending on the specified files.
|
||||
#
|
||||
# @param user_deps [Array<String>]
|
||||
|
||||
@@ -37,7 +37,7 @@ module Rscons
|
||||
# The number of remaining build steps.
|
||||
def build_steps_remaining
|
||||
self.reduce(0) do |result, (target, builders)|
|
||||
result + builders.select {|b| not b.nop?}.size
|
||||
result + builders.size
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+36
-49
@@ -9,61 +9,48 @@ module Rscons
|
||||
def initialize(*args)
|
||||
super
|
||||
@install_builder = self.class.name == "Install"
|
||||
@nop = @install_builder && !Rscons.application.operation("install")
|
||||
end
|
||||
|
||||
# Return whether the builder is a no-op.
|
||||
#
|
||||
# @return [Boolean]
|
||||
# Whether the builder is a no-op.
|
||||
def nop?
|
||||
@nop
|
||||
end
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
def run(options)
|
||||
if @nop
|
||||
true
|
||||
target_is_dir = (@sources.length > 1) ||
|
||||
Dir.exists?(@sources.first) ||
|
||||
Dir.exists?(@target)
|
||||
outdir = target_is_dir ? @target : File.dirname(@target)
|
||||
# Collect the list of files to copy over.
|
||||
file_map = {}
|
||||
if target_is_dir
|
||||
@sources.each do |src|
|
||||
if Dir.exists? src
|
||||
Dir.glob("#{src}/**/*", File::FNM_DOTMATCH).select do |f|
|
||||
File.file?(f)
|
||||
end.each do |subfile|
|
||||
subpath = Pathname.new(subfile).relative_path_from(Pathname.new(src)).to_s
|
||||
file_map[subfile] = "#{outdir}/#{subpath}"
|
||||
end
|
||||
else
|
||||
file_map[src] = "#{outdir}/#{File.basename(src)}"
|
||||
end
|
||||
end
|
||||
else
|
||||
target_is_dir = (@sources.length > 1) ||
|
||||
Dir.exists?(@sources.first) ||
|
||||
Dir.exists?(@target)
|
||||
outdir = target_is_dir ? @target : File.dirname(@target)
|
||||
# Collect the list of files to copy over.
|
||||
file_map = {}
|
||||
if target_is_dir
|
||||
@sources.each do |src|
|
||||
if Dir.exists? src
|
||||
Dir.glob("#{src}/**/*", File::FNM_DOTMATCH).select do |f|
|
||||
File.file?(f)
|
||||
end.each do |subfile|
|
||||
subpath = Pathname.new(subfile).relative_path_from(Pathname.new(src)).to_s
|
||||
file_map[subfile] = "#{outdir}/#{subpath}"
|
||||
end
|
||||
else
|
||||
file_map[src] = "#{outdir}/#{File.basename(src)}"
|
||||
end
|
||||
end
|
||||
else
|
||||
file_map[sources.first] = target
|
||||
end
|
||||
printed_message = false
|
||||
file_map.each do |src, dest|
|
||||
# Check the cache and copy if necessary
|
||||
unless @cache.up_to_date?(dest, :Copy, [src], @env)
|
||||
unless printed_message
|
||||
message = "#{name} <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>"
|
||||
print_run_message(message, nil)
|
||||
printed_message = true
|
||||
end
|
||||
@cache.mkdir_p(File.dirname(dest), install: @install_builder)
|
||||
FileUtils.rm_f(dest)
|
||||
FileUtils.cp(src, dest, :preserve => true)
|
||||
end
|
||||
@cache.register_build(dest, :Copy, [src], @env, install: @install_builder)
|
||||
end
|
||||
(target_is_dir ? Dir.exists?(@target) : File.exists?(@target)) ? true : false
|
||||
file_map[sources.first] = target
|
||||
end
|
||||
printed_message = false
|
||||
file_map.each do |src, dest|
|
||||
# Check the cache and copy if necessary
|
||||
unless @cache.up_to_date?(dest, :Copy, [src], @env)
|
||||
unless printed_message
|
||||
message = "#{name} <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>"
|
||||
print_run_message(message, nil)
|
||||
printed_message = true
|
||||
end
|
||||
@cache.mkdir_p(File.dirname(dest), install: @install_builder)
|
||||
FileUtils.rm_f(dest)
|
||||
FileUtils.cp(src, dest, :preserve => true)
|
||||
end
|
||||
@cache.register_build(dest, :Copy, [src], @env, install: @install_builder)
|
||||
end
|
||||
(target_is_dir ? Dir.exists?(@target) : File.exists?(@target)) ? true : false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,32 +8,19 @@ module Rscons
|
||||
def initialize(*args)
|
||||
super
|
||||
@install_builder = self.class.name == "InstallDirectory"
|
||||
@nop = @install_builder && !Rscons.application.operation("install")
|
||||
end
|
||||
|
||||
# Return whether the builder is a no-op.
|
||||
#
|
||||
# @return [Boolean]
|
||||
# Whether the builder is a no-op.
|
||||
def nop?
|
||||
@nop
|
||||
end
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
def run(options)
|
||||
if @nop
|
||||
if File.directory?(@target)
|
||||
true
|
||||
elsif File.exists?(@target)
|
||||
Ansi.write($stderr, :red, "Error: `#{@target}' already exists and is not a directory", :reset, "\n")
|
||||
false
|
||||
else
|
||||
if File.directory?(@target)
|
||||
true
|
||||
elsif File.exists?(@target)
|
||||
Ansi.write($stderr, :red, "Error: `#{@target}' already exists and is not a directory", :reset, "\n")
|
||||
false
|
||||
else
|
||||
print_run_message("Creating directory <target>#{@target}<reset>", nil)
|
||||
@cache.mkdir_p(@target, install: @install_builder)
|
||||
true
|
||||
end
|
||||
print_run_message("Creating directory <target>#{@target}<reset>", nil)
|
||||
@cache.mkdir_p(@target, install: @install_builder)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+140
-124
@@ -1,65 +1,88 @@
|
||||
require "rscons"
|
||||
require "optparse"
|
||||
|
||||
# CLI usage string.
|
||||
USAGE = <<EOF
|
||||
Usage: #{$0} [global options] [operation] [operation options]
|
||||
|
||||
Global options:
|
||||
-b BUILD, --build=BUILD Set build directory (default: build)
|
||||
-f FILE Use FILE as Rsconscript
|
||||
-F, --show-failure Show failed command log from previous build and exit
|
||||
-h, --help Show rscons help and exit
|
||||
-j N, --nthreads=N Set number of threads (local default: #{Rscons.application.n_threads})
|
||||
-r COLOR, --color=COLOR Set color mode (off, auto, force)
|
||||
-v, --verbose Run verbosely
|
||||
--version Show rscons version and exit
|
||||
|
||||
Operations:
|
||||
configure Configure the project
|
||||
build Build the project
|
||||
clean Remove build artifacts (but not configuration)
|
||||
distclean Remove build directory and configuration
|
||||
install Install project to installation destination
|
||||
uninstall Uninstall project from installation destination
|
||||
|
||||
Configure options:
|
||||
--prefix=PREFIX Set installation prefix (default: /usr/local)
|
||||
EOF
|
||||
|
||||
module Rscons
|
||||
# Command-Line Interface functionality.
|
||||
module Cli
|
||||
class Cli
|
||||
|
||||
# Default files to look for to execute if none specified.
|
||||
DEFAULT_RSCONSCRIPTS = %w[Rsconscript Rsconscript.rb]
|
||||
|
||||
class << self
|
||||
# Run the Rscons CLI.
|
||||
#
|
||||
# @param argv [Array]
|
||||
# Command-line parameters.
|
||||
#
|
||||
# @return [void]
|
||||
def run(argv)
|
||||
argv = argv.dup
|
||||
begin
|
||||
exit run_toplevel(argv)
|
||||
rescue OptionParser::InvalidOption => io
|
||||
$stderr.puts io.message
|
||||
$stderr.puts usage
|
||||
exit 2
|
||||
end
|
||||
end
|
||||
|
||||
# Run the Rscons CLI.
|
||||
#
|
||||
# @param argv [Array]
|
||||
# Command-line parameters.
|
||||
#
|
||||
# @return [void]
|
||||
def run(argv)
|
||||
argv = argv.dup
|
||||
begin
|
||||
exit run_toplevel(argv)
|
||||
rescue OptionParser::InvalidOption => io
|
||||
$stderr.puts io.message
|
||||
$stderr.puts USAGE
|
||||
exit 2
|
||||
private
|
||||
|
||||
def parse_task_params(task, argv)
|
||||
while argv.size > 0
|
||||
if argv[0].start_with?("-")
|
||||
valid_arg = false
|
||||
if argv[0] =~ /^--(\S+?)(?:=(.*))?$/
|
||||
param_name, value = $1, $2
|
||||
if param = Task[task].params[param_name]
|
||||
param.value = value || argv[0]
|
||||
argv.slice!(0)
|
||||
valid_arg = true
|
||||
end
|
||||
end
|
||||
unless valid_arg
|
||||
$stderr.puts "Invalid task '#{task}' argument '#{argv[0].split("=").first}'"
|
||||
$stderr.puts usage
|
||||
exit 2
|
||||
end
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def parse_tasks_and_params(argv)
|
||||
tasks = []
|
||||
while argv.size > 0
|
||||
task = argv.shift
|
||||
parse_task_params(task, argv)
|
||||
tasks << task
|
||||
end
|
||||
tasks
|
||||
end
|
||||
|
||||
def run_toplevel(argv)
|
||||
rsconscript = nil
|
||||
do_help = false
|
||||
|
||||
OptionParser.new do |opts|
|
||||
|
||||
def add_global_options(opts)
|
||||
opts.on("-b", "--build DIR") do |build_dir|
|
||||
Rscons.application.build_dir = build_dir
|
||||
end
|
||||
|
||||
opts.on("-f FILE") do |f|
|
||||
rsconscript = f
|
||||
end
|
||||
|
||||
opts.on("-F", "--show-failure") do
|
||||
Rscons.application.show_failure
|
||||
return 0
|
||||
end
|
||||
|
||||
opts.on("-h", "--help") do
|
||||
do_help = true
|
||||
end
|
||||
|
||||
opts.on("-j NTHREADS") do |n_threads|
|
||||
Rscons.application.n_threads = n_threads.to_i
|
||||
end
|
||||
@@ -76,103 +99,96 @@ module Rscons
|
||||
opts.on("-v", "--verbose") do
|
||||
Rscons.application.verbose = true
|
||||
end
|
||||
|
||||
opts.on("--version") do
|
||||
puts "Rscons version #{Rscons::VERSION}"
|
||||
return 0
|
||||
end
|
||||
|
||||
end.order!(argv)
|
||||
|
||||
# Find the build script.
|
||||
if rsconscript
|
||||
unless File.exists?(rsconscript)
|
||||
$stderr.puts "Cannot read #{rsconscript}"
|
||||
return 1
|
||||
end
|
||||
else
|
||||
rsconscript = DEFAULT_RSCONSCRIPTS.find do |f|
|
||||
File.exists?(f)
|
||||
end
|
||||
end
|
||||
|
||||
def run_toplevel(argv)
|
||||
rsconscript = nil
|
||||
do_help = false
|
||||
|
||||
OptionParser.new do |opts|
|
||||
|
||||
add_global_options(opts)
|
||||
|
||||
opts.on("-f FILE") do |f|
|
||||
rsconscript = f
|
||||
end
|
||||
|
||||
opts.on("-F", "--show-failure") do
|
||||
show_failure
|
||||
exit 0
|
||||
end
|
||||
|
||||
opts.on("--version") do
|
||||
puts "Rscons version #{Rscons::VERSION}"
|
||||
exit 0
|
||||
end
|
||||
|
||||
opts.on("-h", "--help") do
|
||||
do_help = true
|
||||
end
|
||||
|
||||
end.order!(argv)
|
||||
|
||||
# Retrieve the operation, or default to build.
|
||||
operation = argv.shift || "build"
|
||||
|
||||
argv.each do |arg|
|
||||
if arg =~ /^([^=]+)=(.*)$/
|
||||
Rscons.application.vars[$1] = $2
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
# Load the build script.
|
||||
if rsconscript
|
||||
unless File.exists?(rsconscript)
|
||||
$stderr.puts "Cannot read #{rsconscript}"
|
||||
exit 1
|
||||
end
|
||||
else
|
||||
rsconscript = DEFAULT_RSCONSCRIPTS.find do |f|
|
||||
File.exists?(f)
|
||||
end
|
||||
end
|
||||
|
||||
if rsconscript
|
||||
script = Script.new
|
||||
script.load(rsconscript)
|
||||
Rscons.application.script.load(rsconscript)
|
||||
end
|
||||
|
||||
# Do help after loading the build script (if found) so that any
|
||||
# script-defined tasks and task options can be displayed.
|
||||
if do_help
|
||||
puts USAGE
|
||||
exit 0
|
||||
puts usage
|
||||
return 0
|
||||
end
|
||||
|
||||
# Anything else requires a build script, so complain if we didn't find
|
||||
# one.
|
||||
unless rsconscript
|
||||
$stderr.puts "Could not find the Rsconscript to execute."
|
||||
$stderr.puts "Looked for: #{DEFAULT_RSCONSCRIPTS.join(", ")}"
|
||||
exit 1
|
||||
return 1
|
||||
end
|
||||
|
||||
operation_options = parse_operation_args(operation, argv) || {}
|
||||
# Parse the rest of the command line. This is done after loading the
|
||||
# build script so that script-defined tasks and task options can be
|
||||
# taken into account.
|
||||
tasks = parse_tasks_and_params(argv)
|
||||
|
||||
exit Rscons.application.run(operation, script, operation_options)
|
||||
end
|
||||
|
||||
def parse_operation_args(operation, argv)
|
||||
options = {}
|
||||
OptionParser.new do |opts|
|
||||
add_global_options(opts)
|
||||
case operation
|
||||
when "configure"
|
||||
parse_configure_args(opts, argv, options)
|
||||
end
|
||||
end.order!(argv)
|
||||
options
|
||||
end
|
||||
|
||||
def parse_configure_args(opts, argv, options)
|
||||
opts.on("--prefix PREFIX") do |prefix|
|
||||
options[:prefix] = prefix
|
||||
# If no user specified tasks, run "default" task.
|
||||
if tasks.empty?
|
||||
tasks << "default"
|
||||
end
|
||||
end
|
||||
|
||||
def show_failure
|
||||
failed_commands = Cache.instance["failed_commands"]
|
||||
failed_commands.each_with_index do |command, i|
|
||||
Ansi.write($stdout, :red, "Failed command (#{i + 1}/#{failed_commands.size}):", :reset, "\n")
|
||||
$stdout.puts Util.command_to_s(command)
|
||||
end
|
||||
# Finally, with the script fully loaded and command-line parsed, run
|
||||
# the application to execute all required tasks.
|
||||
Rscons.application.run(tasks)
|
||||
rescue RsconsError => e
|
||||
Ansi.write($stderr, :red, e.message, :reset, "\n")
|
||||
1
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def usage
|
||||
usage = <<EOF
|
||||
Usage: #{$0} [global options] [[task] [task options] ...]
|
||||
|
||||
Global options:
|
||||
-b BUILD, --build=BUILD Set build directory (default: build)
|
||||
-f FILE Use FILE as Rsconscript
|
||||
-F, --show-failure Show failed command log from previous build and exit
|
||||
-h, --help Show rscons help and exit
|
||||
-j N, --nthreads=N Set number of threads (local default: #{Rscons.application.n_threads})
|
||||
-r COLOR, --color=COLOR Set color mode (off, auto, force)
|
||||
-v, --verbose Run verbosely
|
||||
--version Show rscons version and exit
|
||||
|
||||
Tasks:
|
||||
EOF
|
||||
Task[].each do |name, task|
|
||||
if task.desc
|
||||
usage += %[ #{sprintf("%-27s", name)} #{task.desc}\n]
|
||||
task.params.each do |name, param|
|
||||
arg_text = "--#{name}"
|
||||
if param.takes_arg
|
||||
arg_text += "=#{name.upcase}"
|
||||
end
|
||||
usage += %[ #{sprintf("%-25s", "#{arg_text}")} #{param.description}\n]
|
||||
end
|
||||
end
|
||||
end
|
||||
usage
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
+11
-14
@@ -7,29 +7,26 @@ module Rscons
|
||||
|
||||
# Create a ConfigureOp.
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Optional parameters.
|
||||
# @option options [String] :prefix
|
||||
# Install prefix.
|
||||
# @option options [String] :project_name
|
||||
# Project name.
|
||||
def initialize(options)
|
||||
# Default options.
|
||||
options[:prefix] ||= "/usr/local"
|
||||
# @param script [Script]
|
||||
# Build script.
|
||||
def initialize(script)
|
||||
@work_dir = "#{Rscons.application.build_dir}/_configure"
|
||||
FileUtils.mkdir_p(@work_dir)
|
||||
@log_fh = File.open("#{@work_dir}/config.log", "wb")
|
||||
cache = Cache.instance
|
||||
cache["failed_commands"] = []
|
||||
cache["configuration_data"] = {}
|
||||
cache["configuration_data"]["prefix"] = options[:prefix]
|
||||
if project_name = options[:project_name]
|
||||
if project_name = script.project_name
|
||||
Ansi.write($stdout, "Configuring ", :cyan, project_name, :reset, "...\n")
|
||||
else
|
||||
$stdout.puts "Configuring project..."
|
||||
end
|
||||
Ansi.write($stdout, "Setting prefix... ", :green, options[:prefix], :reset, "\n")
|
||||
store_merge("prefix" => options[:prefix])
|
||||
vars = {}
|
||||
Task["configure"].params.each do |name, param|
|
||||
Ansi.write($stdout, "Setting #{name}... ", :green, param.value, :reset, "\n")
|
||||
vars[name] = param.value
|
||||
end
|
||||
store_merge(vars)
|
||||
end
|
||||
|
||||
# Close the log file handle.
|
||||
@@ -399,7 +396,7 @@ module Rscons
|
||||
options[:on_fail].call
|
||||
end
|
||||
if should_fail
|
||||
raise RsconsError.new
|
||||
raise RsconsError.new("Configuration failed")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+27
-14
@@ -14,6 +14,10 @@ module Rscons
|
||||
# All Environments.
|
||||
attr_reader :environments
|
||||
|
||||
# @return [Environment]
|
||||
# The Environment that is currently executing a construction block.
|
||||
attr_accessor :open_environment
|
||||
|
||||
# Initialize class instance variables.
|
||||
def class_init
|
||||
@environments = []
|
||||
@@ -72,6 +76,7 @@ module Rscons
|
||||
# If a block is given, the Environment object is yielded to the block and
|
||||
# when the block returns, the {#process} method is automatically called.
|
||||
def initialize(options = {})
|
||||
Rscons.application.check_configure
|
||||
unless Cache.instance["configuration_data"]["configured"]
|
||||
raise "Project must be configured before creating an Environment"
|
||||
end
|
||||
@@ -109,7 +114,10 @@ module Rscons
|
||||
@n_threads = Rscons.application.n_threads
|
||||
|
||||
if block_given?
|
||||
Environment.open_environment = self
|
||||
yield self
|
||||
Environment.open_environment = nil
|
||||
process
|
||||
end
|
||||
end
|
||||
|
||||
@@ -158,7 +166,10 @@ module Rscons
|
||||
env.instance_variable_set(:@n_threads, @n_threads)
|
||||
|
||||
if block_given?
|
||||
Environment.open_environment = env
|
||||
yield env
|
||||
Environment.open_environment = nil
|
||||
env.process
|
||||
end
|
||||
env
|
||||
end
|
||||
@@ -274,6 +285,10 @@ module Rscons
|
||||
@process_commands_waiting_to_run = []
|
||||
@process_builder_waits = {}
|
||||
@process_builders_to_run = []
|
||||
@build_step = 0
|
||||
@build_steps = @builder_sets.reduce(0) do |result, builder_set|
|
||||
result + builder_set.build_steps_remaining
|
||||
end
|
||||
begin
|
||||
while @builder_sets.size > 0 or @threads.size > 0 or @process_commands_waiting_to_run.size > 0
|
||||
process_step
|
||||
@@ -524,7 +539,7 @@ module Rscons
|
||||
message = short_description if short_description
|
||||
end
|
||||
if message
|
||||
total_build_steps = Rscons.application.get_total_build_steps.to_s
|
||||
total_build_steps = @build_steps.to_s
|
||||
this_build_step = sprintf("%#{total_build_steps.size}d", builder.build_step)
|
||||
progress = "[#{this_build_step}/#{total_build_steps}]"
|
||||
Ansi.write($stdout, *Util.colorize_markup("#{progress} #{message}"), "\n")
|
||||
@@ -549,16 +564,6 @@ module Rscons
|
||||
@builder_sets << build_builder_set
|
||||
end
|
||||
|
||||
# Get the number of build steps remaining.
|
||||
#
|
||||
# @return [Integer]
|
||||
# The number of build steps remaining.
|
||||
def build_steps_remaining
|
||||
@builder_sets.reduce(0) do |result, builder_set|
|
||||
result + builder_set.build_steps_remaining
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Build a BuilderSet.
|
||||
@@ -569,6 +574,16 @@ module Rscons
|
||||
BuilderSet.new(@registered_build_dependencies, @side_effects)
|
||||
end
|
||||
|
||||
# Get the next build step number.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @return [Integer]
|
||||
# Next build step number.
|
||||
def get_next_build_step
|
||||
@build_step += 1
|
||||
end
|
||||
|
||||
# Run a builder and process its return value.
|
||||
#
|
||||
# @param builder [Builder]
|
||||
@@ -576,9 +591,7 @@ module Rscons
|
||||
#
|
||||
# @return [void]
|
||||
def run_builder(builder)
|
||||
unless builder.nop?
|
||||
builder.build_step ||= Rscons.application.get_next_build_step
|
||||
end
|
||||
builder.build_step ||= get_next_build_step
|
||||
case result = builder.run({})
|
||||
when Array
|
||||
result.each do |waititem|
|
||||
|
||||
+76
-34
@@ -5,6 +5,10 @@ module Rscons
|
||||
|
||||
# Global DSL methods.
|
||||
class GlobalDsl
|
||||
# Create a GlobalDsl.
|
||||
def initialize(script)
|
||||
@script = script
|
||||
end
|
||||
|
||||
# Return a list of paths matching the specified pattern(s).
|
||||
#
|
||||
@@ -33,6 +37,20 @@ module Rscons
|
||||
end.sort
|
||||
end
|
||||
|
||||
# Construct a task parameter.
|
||||
#
|
||||
# @param name [String]
|
||||
# Param name.
|
||||
# @param value [String, nil]
|
||||
# Param value.
|
||||
# @param takes_arg [String]
|
||||
# Whether the parameter takes an argument.
|
||||
# @param description [String]
|
||||
# Param description.
|
||||
def param(name, value, takes_arg, description)
|
||||
Task::Param.new(name, value, takes_arg, description)
|
||||
end
|
||||
|
||||
# Return path components from the PATH variable.
|
||||
#
|
||||
# @return [Array<String>]
|
||||
@@ -159,6 +177,16 @@ module Rscons
|
||||
end
|
||||
end
|
||||
|
||||
# Create or modify a task.
|
||||
def task(name, options = {}, &block)
|
||||
if task = Task.tasks[name]
|
||||
task.modify(options, &block)
|
||||
else
|
||||
task = Task.new(name, options, &block)
|
||||
end
|
||||
task
|
||||
end
|
||||
|
||||
[
|
||||
:cd,
|
||||
:chmod,
|
||||
@@ -191,12 +219,7 @@ module Rscons
|
||||
end
|
||||
|
||||
# Top-level DSL available to the Rsconscript.
|
||||
class Dsl < GlobalDsl
|
||||
# Create a Dsl.
|
||||
def initialize(script)
|
||||
@script = script
|
||||
end
|
||||
|
||||
class TopLevelDsl < GlobalDsl
|
||||
# Set the project name.
|
||||
def project_name(project_name)
|
||||
@script.project_name = project_name
|
||||
@@ -207,14 +230,18 @@ module Rscons
|
||||
@script.autoconf = autoconf
|
||||
end
|
||||
|
||||
# Enter build block.
|
||||
def build(&block)
|
||||
@script.operations["build"] = block
|
||||
end
|
||||
|
||||
# Enter configuration block.
|
||||
def configure(&block)
|
||||
@script.operations["configure"] = block
|
||||
# Shortcut methods to create task blocks for special tasks.
|
||||
[
|
||||
:clean,
|
||||
:distclean,
|
||||
:configure,
|
||||
:default,
|
||||
:install,
|
||||
:uninstall,
|
||||
].each do |method_name|
|
||||
define_method(method_name) do |*args, &block|
|
||||
task(method_name.to_s, *args, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -222,9 +249,12 @@ module Rscons
|
||||
class ConfigureDsl < GlobalDsl
|
||||
# Create a ConfigureDsl.
|
||||
#
|
||||
# @param script [Script]
|
||||
# The Script being evaluated.
|
||||
# @param configure_op [ConfigureOp]
|
||||
# The configure operation object.
|
||||
def initialize(configure_op)
|
||||
def initialize(script, configure_op)
|
||||
super(script)
|
||||
@configure_op = configure_op
|
||||
end
|
||||
|
||||
@@ -263,18 +293,36 @@ module Rscons
|
||||
attr_accessor :project_name
|
||||
|
||||
# @return [Boolean]
|
||||
# Whether to autoconfigure if the user does not explicitly perform a
|
||||
# configure operation before building (default: true).
|
||||
# Whether to autoconfigure if the user does not explicitly configure
|
||||
# before calling a normal task (default: true).
|
||||
attr_accessor :autoconf
|
||||
|
||||
# @return [Hash]
|
||||
# Operation lambdas.
|
||||
attr_reader :operations
|
||||
|
||||
# Construct a Script.
|
||||
def initialize
|
||||
@autoconf = true
|
||||
@operations = {}
|
||||
TopLevelDsl.new(self).instance_eval do
|
||||
task("clean",
|
||||
desc: "Remove build artifacts (but not configuration)",
|
||||
autoconf: false) do
|
||||
Rscons.application.clean
|
||||
end
|
||||
task("configure",
|
||||
desc: "Configure the project",
|
||||
autoconf: false,
|
||||
params: [param("prefix", "/usr/local", true, "Set installation prefix (default: /usr/local)")])
|
||||
task("distclean",
|
||||
desc: "Remove build directory and configuration",
|
||||
autoconf: false) do
|
||||
Rscons.application.distclean
|
||||
end
|
||||
task("install",
|
||||
desc: "Install project to configured installation prefix")
|
||||
task("uninstall",
|
||||
desc: "Uninstall project",
|
||||
autoconf: false) do
|
||||
Rscons.application.uninstall
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Load a script from the specified file.
|
||||
@@ -285,21 +333,15 @@ module Rscons
|
||||
# @return [void]
|
||||
def load(path)
|
||||
script_contents = File.read(path, mode: "rb")
|
||||
Dsl.new(self).instance_eval(script_contents, path, 1)
|
||||
TopLevelDsl.new(self).instance_eval(script_contents, path, 1)
|
||||
end
|
||||
|
||||
# Perform build operation.
|
||||
def build
|
||||
if build_proc = @operations["build"]
|
||||
build_proc.call
|
||||
end
|
||||
end
|
||||
|
||||
# Perform configure operation.
|
||||
# Perform configure action.
|
||||
def configure(configure_op)
|
||||
if operation_lambda = @operations["configure"]
|
||||
cdsl = ConfigureDsl.new(configure_op)
|
||||
cdsl.instance_eval(&operation_lambda)
|
||||
cdsl = ConfigureDsl.new(self, configure_op)
|
||||
configure_task = Task["configure"]
|
||||
configure_task.actions.each do |action|
|
||||
cdsl.instance_exec(configure_task, configure_task.param_values, &action)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,232 @@
|
||||
module Rscons
|
||||
|
||||
# A task is a named collection of actions and/or dependencies.
|
||||
class Task
|
||||
|
||||
class << self
|
||||
|
||||
# Access a task by name.
|
||||
#
|
||||
# @param name [String, nil]
|
||||
# Task name, or nil to return a Hash of all tasks.
|
||||
#
|
||||
# @return [Hash, Task, nil]
|
||||
# Hash of all tasks if name is nil, otherwise Task with the given name,
|
||||
# if found.
|
||||
def [](name = nil)
|
||||
if name
|
||||
if task = tasks[name]
|
||||
task
|
||||
else
|
||||
raise RsconsError.new("Task '#{name}' not found")
|
||||
end
|
||||
else
|
||||
tasks
|
||||
end
|
||||
end
|
||||
|
||||
# Register a newly created task.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @param task [Task]
|
||||
# Task to register.
|
||||
#
|
||||
# @return [void]
|
||||
def register(task)
|
||||
tasks[task.name] = task
|
||||
end
|
||||
|
||||
# Get all registered tasks.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @return [Hash]
|
||||
# All registered tasks.
|
||||
def tasks
|
||||
@tasks ||= {}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# Class to represent a task parameter.
|
||||
class Param
|
||||
|
||||
# @return [String]
|
||||
# Param name.
|
||||
attr_reader :name
|
||||
|
||||
# @return [String]
|
||||
# Param description.
|
||||
attr_reader :description
|
||||
|
||||
# @return [Boolean]
|
||||
# Whether the parameter takes an argument.
|
||||
attr_reader :takes_arg
|
||||
|
||||
# @return [String, nil]
|
||||
# Param value.
|
||||
attr_accessor :value
|
||||
|
||||
# Construct a Param.
|
||||
#
|
||||
# @param name [String]
|
||||
# Param name.
|
||||
# @param value [String, nil]
|
||||
# Param value.
|
||||
# @param takes_arg [String]
|
||||
# Whether the parameter takes an argument.
|
||||
# @param description [String]
|
||||
# Param description.
|
||||
def initialize(name, value, takes_arg, description)
|
||||
@name = name
|
||||
@value = value
|
||||
@takes_arg = takes_arg
|
||||
@description = description
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# @return [Array<Proc>]
|
||||
# Task action blocks.
|
||||
attr_reader :actions
|
||||
|
||||
# @return [Boolean]
|
||||
# Whether to automatically configure before running this task.
|
||||
attr_reader :autoconf
|
||||
|
||||
# @return [Array<String>]
|
||||
# Task dependencies.
|
||||
attr_reader :deps
|
||||
|
||||
# @return [String, nil]
|
||||
# Task description, if given.
|
||||
attr_reader :desc
|
||||
|
||||
# @return [String]
|
||||
# Task name.
|
||||
attr_reader :name
|
||||
|
||||
# @return [Hash<String => Param>]
|
||||
# Task params.
|
||||
attr_reader :params
|
||||
|
||||
# Construct a task.
|
||||
#
|
||||
# @param name [String]
|
||||
# Task name.
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
# @option options [Boolean] :autoconf
|
||||
# Whether to automatically configure before running this task (default
|
||||
# true).
|
||||
def initialize(name, options, &block)
|
||||
@autoconf = true
|
||||
@deps = []
|
||||
@desc = nil
|
||||
@name = name
|
||||
@params = {}
|
||||
@actions = []
|
||||
Task.register(self)
|
||||
modify(options, &block)
|
||||
end
|
||||
|
||||
# Get a parameter's value.
|
||||
#
|
||||
# @param param_name [String]
|
||||
# Parameter name.
|
||||
#
|
||||
# @return [String]
|
||||
# Parameter value.
|
||||
def [](param_name)
|
||||
param = @params[param_name]
|
||||
unless param
|
||||
raise RsconsError.new("Could not find parameter '#{param_name}'")
|
||||
end
|
||||
param.value
|
||||
end
|
||||
|
||||
# Execute a task's actions.
|
||||
#
|
||||
# @return [void]
|
||||
def execute
|
||||
@executed = true
|
||||
if @autoconf
|
||||
Rscons.application.check_configure
|
||||
end
|
||||
@deps.each do |dep|
|
||||
Task[dep].check_execute
|
||||
end
|
||||
if @name == "configure"
|
||||
Rscons.application.configure
|
||||
else
|
||||
@actions.each do |action|
|
||||
action[self, param_values]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Check if the task has been executed, and if not execute it.
|
||||
#
|
||||
# @return [void]
|
||||
def check_execute
|
||||
unless executed?
|
||||
execute
|
||||
end
|
||||
end
|
||||
|
||||
# Check if the task has been executed.
|
||||
#
|
||||
# @return [Boolean]
|
||||
# Whether the task has been executed.
|
||||
def executed?
|
||||
@executed
|
||||
end
|
||||
|
||||
# Modify a task.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @return [void]
|
||||
def modify(options, &block)
|
||||
if options.include?(:autoconf)
|
||||
@autoconf = options[:autoconf]
|
||||
end
|
||||
if options.include?(:desc)
|
||||
@desc = options[:desc]
|
||||
end
|
||||
if options.include?(:deps)
|
||||
@deps += Array(options[:deps])
|
||||
end
|
||||
if options.include?(:params)
|
||||
Array(options[:params]).each do |param|
|
||||
@params[param.name] = param
|
||||
end
|
||||
end
|
||||
if block
|
||||
if env = Environment.open_environment
|
||||
@actions << proc do
|
||||
block[]
|
||||
env.process
|
||||
end
|
||||
else
|
||||
@actions << block
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Get parameter values as a Hash.
|
||||
#
|
||||
# @return [Hash]
|
||||
# Parameter values.
|
||||
def param_values
|
||||
param_values = {}
|
||||
@params.each do |name, param|
|
||||
param_values[name] = param.value
|
||||
end
|
||||
param_values
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
Reference in new issue
Block a user