4.3.2.4 Custom Builder OperationIn order for a builder to perform a build operation, the builder class must
implement a the class Rscons::Builders::Custom < Rscons::Builder def run(options) File.open(@target, "w") do |fh| fh.write("Target file created.") end true end end 4.3.2.4.1 Return ValueIf the build operation has completed and failed, the 4.3.2.4.2 Printing Build StatusA builder should print a status line when it produces a build target.
The class Rscons::Builders::Custom < Rscons::Builder def run(options) print_run_message("Creating <target>#{@target}<reset> from Custom builder", nil) File.open(@target, "w") do |fh| fh.write("Target file created.") end true end end 4.3.2.4.3 Custom Builder Cache Usage - Only Rebuild When NecessaryWhenever possible, a builder should keep track of information necessary to
know whether the target file(s) need to be rebuilt.
The class Rscons::Builders::Custom < Rscons::Builder def run(options) unless @cache.up_to_date?(@target, nil, @sources, @env) print_run_message("Combining <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", nil) File.open(@target, "wb") do |fh| @sources.each do |source| fh.write(File.read(source, mode: "rb")) end end @cache.register_build(@target, nil, @sources, @env) end true end end This builder would rebuild the target file and print its run message if the target file or any of the source file(s) were changed, but otherwise would be silent and not re-combine the source files. Note that generally the same arguments should be passed to
4.3.2.4.4 Custom Builder ParallelizationThe Rscons scheduler can parallelize builders to take advantage of multiple
processor cores.
Taking advantage of this ability to parallelize requires the builder author to
author the builder in a particular way.
The 4.3.2.4.4.1 Using a Ruby Thread to Parallelize a Build OperationHere is an example of using a Ruby thread to parallelize a build operation: class MyBuilder < Rscons::Builder def run(options) if @thread true else print_run_message("#{name} #{target}", nil) @thread = Thread.new do sleep 2 FileUtils.touch(@target) end wait_for(@thread) end end end build do Environment.new do |env| env.add_builder(MyBuilder) env.MyBuilder("foo") end end It is up to the author of the thread logic to only perform actions that are
thread-safe.
It is not safe to call other Rscons methods, for example, registering other
builders or using the Cache, from a thread other than the one that calls the
4.3.2.4.4.2 Executing a Subcommand from a Custom BuilderIt is a very common case that a builder will execute a subcommand which
produces the build target.
This is how most of the built-in Rscons builders execute.
A low-level way to handle this is for the builder to construct an instance of
the The The built-in Rscons builders Example (built-in Command builder): module Rscons module Builders # A builder to execute an arbitrary command that will produce the given # target based on the given sources. # # Example: # env.Command("docs.html", "docs.md", # CMD => %w[pandoc -fmarkdown -thtml -o${_TARGET} ${_SOURCES}]) class Command < Builder # Run the builder to produce a build target. def run(options) if @command finalize_command else @vars["_TARGET"] = @target @vars["_SOURCES"] = @sources command = @env.build_command("${CMD}", @vars) cmd_desc = @vars["CMD_DESC"] || "Command" options = {} if @vars["CMD_STDOUT"] options[:stdout] = @env.expand_varref("${CMD_STDOUT}", @vars) end standard_command("#{cmd_desc} <target>#{@target}<reset>", command, options) end end end end end Example (built-in Disassemble builder): module Rscons module Builders # The Disassemble builder produces a disassembly listing of a source file. class Disassemble < Builder # Run the builder to produce a build target. def run(options) if @command finalize_command else @vars["_SOURCES"] = @sources command = @env.build_command("${DISASM_CMD}", @vars) standard_command("Disassembling <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{target}<reset>", command, stdout: @target) end end end end end
|