Add tasks - close #139

This commit is contained in:
josh committed 2022-01-30 13:45:06 -05:00
1 parent f5ab51c477
commit bd9cf55b85
149 files changed
+1578 -1063

No files matched your search

+1 -7
View File
@@ -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
View File
@@ -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
-8
View File
@@ -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>]
+1 -1
View File
@@ -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
View File
@@ -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
+7 -20
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+232
View File
@@ -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