rscons/lib/rscons/builder.rb

222 lines
6.7 KiB
Ruby

require "fileutils"
module Rscons
# Namespace module in which to store builders for convenient grouping
module Builders; end
# Class to hold an object that knows how to build a certain type of file.
class Builder
class << self
# Return a String specifying an extra path component used to
# differentiate build targets built by this builder from others.
#
# @return [String, nil]
# Extra path component used to differentiate build targets built by
# this builder from others.
def extra_path
end
# Return the name of the builder.
#
# If not overridden this defaults to the last component of the class name.
#
# @return [String] The name of the builder.
def name
super.split(":").last
end
end
# @return [String, Symbol]
# Target file name.
attr_accessor :target
# @return [Array<String>]
# Source file name(s).
attr_accessor :sources
# @return [Cache]
# Cache instance.
attr_accessor :cache
# @return [Environment]
# The {Environment} performing the build operation.
attr_accessor :env
# @return [Hash, VarSet]
# Construction variables used to perform the build operation.
attr_accessor :vars
# @return [Set<String>]
# Side effect file(s) produced when this builder runs.
attr_accessor :side_effects
# @return [Integer]
# Build step.
attr_accessor :build_step
# Create an instance of the Builder to build a target.
#
# @param options [Hash]
# Options.
# @option options [String, Symbol] :target
# Target file name.
# @option options [Array<String>] :sources
# Source file name(s).
# @option options [Cache] :cache
# The Cache object.
# @option options [Environment] :env
# The Environment executing the builder.
# @option options [Hash,VarSet] :vars
# Extra construction variables.
def initialize(options)
@target = options[:target]
@sources = options[:sources]
@cache = options[:cache]
@env = options[:env]
@vars = options[:vars]
@side_effects = Set.new
end
# Return the name of the builder.
#
# If not overridden this defaults to the last component of the class name.
#
# @return [String] The name of the builder.
def name
self.class.name
end
# Manually record a given build target as depending on the specified files.
#
# @param user_deps [Array<String>]
# Dependency files.
#
# @return [void]
def depends(*user_deps)
@env.depends(@target, *user_deps)
end
# Manually record the given side effect file(s) as being produced when the
# named target is produced.
#
# @return [void]
def produces(*side_effects)
side_effects.each do |side_effect|
side_effect_expanded = @env.expand(side_effect)
@env.register_side_effect(side_effect_expanded)
@side_effects << side_effect_expanded
end
end
# Run the builder to produce a build target.
#
# @param options [Hash]
# Run options.
#
# @return [Object]
# If the build operation fails, this method should return +false+.
# If the build operation succeeds, this method should return +true+.
# If the build operation is not yet complete and is waiting on other
# operations, this method should return the return value from the
# {#wait_for} method.
def run(options)
raise "This method must be overridden in a subclass"
end
# Print the builder run message, depending on the Environment's echo mode.
#
# @param short_description [String]
# Builder short description, printed if the echo mode is :short, or if
# there is no command.
# @param command [Array<String>]
# Builder command, printed if the echo mode is :command.
#
# @return [void]
def print_run_message(short_description, command)
@env.print_builder_run_message(self, short_description, command)
end
# Create a {Command} object to execute the build command in a thread.
#
# @param short_description [String]
# Short description of build action to be printed when env.echo ==
# :short.
# @param command [Array<String>]
# The command to execute.
# @param options [Hash]
# Options.
# @option options [String] :stdout
# File name to redirect standard output to.
#
# @return [Object]
# Return value for {#run} method.
def register_command(short_description, command, options = {})
command_options = {}
if options[:stdout]
command_options[:system_options] = {out: options[:stdout]}
end
print_run_message(short_description, @command)
wait_for(Command.new(command, self, command_options))
end
# Check if the cache is up to date for the target and if not create a
# {Command} object to execute the build command in a thread.
#
# @param short_description [String]
# Short description of build action to be printed when env.echo ==
# :short.
# @param command [Array<String>]
# The command to execute.
# @param options [Hash]
# Options.
# @option options [Array<String>] :sources
# Sources to override @sources.
# @option options [String] :stdout
# File name to redirect standard output to.
#
# @return [Object]
# Return value for {#run} method.
def standard_command(short_description, command, options = {})
@command = command
sources = options[:sources] || @sources
if @cache.up_to_date?(@target, @command, sources, @env)
true
else
unless Rscons.phony_target?(@target)
@cache.mkdir_p(File.dirname(@target))
FileUtils.rm_f(@target)
end
register_command(short_description, @command, options)
end
end
# Register build results from a {Command} with the cache.
#
# @param options [Hash]
# Builder finalize options.
# @option options [String] :stdout
# File name to redirect standard output to.
#
# @return [true]
# Return value for {#run} method.
def finalize_command(options = {})
sources = options[:sources] || @sources
@cache.register_build(@target, @command, sources, @env)
true
end
# A builder can indicate to Rscons that it needs to wait for a separate
# operation to complete by using this method. The return value from this
# method should be returned from the builder's #run method.
#
# @param things [Builder, Command, Thread, Array<Builder, Command, Thread>]
# Builder(s) or Command(s) or Thread(s) that this builder needs to wait
# for.
def wait_for(things)
Array(things)
end
end
end