diff --git a/build_tests/custom_builder/Rsconscript b/build_tests/custom_builder/Rsconscript index 01f83a7..d38a962 100644 --- a/build_tests/custom_builder/Rsconscript +++ b/build_tests/custom_builder/Rsconscript @@ -11,7 +11,7 @@ end build do Environment.new do |env| - env.add_builder(MySource.new) + env.add_builder(MySource) env.MySource('inc.h', []) env.Program('program.exe', Dir['*.c']) end diff --git a/build_tests/custom_builder/cvar_expansion.rb b/build_tests/custom_builder/cvar_expansion.rb index 9bc26e6..d4a80f3 100644 --- a/build_tests/custom_builder/cvar_expansion.rb +++ b/build_tests/custom_builder/cvar_expansion.rb @@ -13,7 +13,7 @@ build do env = Environment.new do |env| env["hdr"] = "inc.h" env["src"] = "program.c" - env.add_builder(MySource.new) + env.add_builder(MySource) env.MySource('${hdr}') env.Program('program.exe', "${src}") end diff --git a/build_tests/custom_builder/cvar_lambda.rb b/build_tests/custom_builder/cvar_lambda.rb index adeba08..90b8b30 100644 --- a/build_tests/custom_builder/cvar_lambda.rb +++ b/build_tests/custom_builder/cvar_lambda.rb @@ -11,7 +11,7 @@ end build do e1 = Environment.new do |env| - env.add_builder(MySource.new) + env.add_builder(MySource) env["one"] = "5" env[:cfg] = {val: "9"} env["two"] = lambda do |args| diff --git a/build_tests/custom_builder/multiple_targets.rb b/build_tests/custom_builder/multiple_targets.rb index 4adf364..a1d9abc 100644 --- a/build_tests/custom_builder/multiple_targets.rb +++ b/build_tests/custom_builder/multiple_targets.rb @@ -14,7 +14,7 @@ end build do Environment.new do |env| - env.add_builder(CHGen.new) + env.add_builder(CHGen) env.CHGen("inc.c", ["program.c"]) env.Program("program.exe", %w[program.c inc.c]) end diff --git a/build_tests/json_to_yaml/Rsconscript b/build_tests/json_to_yaml/Rsconscript index 4ba0a4f..a5177ab 100644 --- a/build_tests/json_to_yaml/Rsconscript +++ b/build_tests/json_to_yaml/Rsconscript @@ -2,7 +2,8 @@ build do Environment.new do |env| require 'json' require 'yaml' - env.add_builder(:JsonToYaml) do |target, sources, cache, env, vars| + env.add_builder(:JsonToYaml) do |params| + target, sources, cache, env, vars = params.values_at(:target, :sources, :cache, :env, :vars) unless cache.up_to_date?(target, :JsonToYaml, sources, env) cache.mkdir_p(File.dirname(target)) File.open(target, 'w') do |f| diff --git a/build_tests/simple/builder_no_sources.rb b/build_tests/simple/builder_no_sources.rb index f067642..a50602d 100644 --- a/build_tests/simple/builder_no_sources.rb +++ b/build_tests/simple/builder_no_sources.rb @@ -5,7 +5,7 @@ class TestBuilder < Rscons::Builder end build do Environment.new do |env| - env.add_builder(TestBuilder.new) + env.add_builder(TestBuilder) env.TestBuilder("file") end end diff --git a/build_tests/simple/cache_debugging.rb b/build_tests/simple/cache_debugging.rb index 6fcdd3a..bc58029 100644 --- a/build_tests/simple/cache_debugging.rb +++ b/build_tests/simple/cache_debugging.rb @@ -30,7 +30,7 @@ end build do Environment.new do |env| - env.add_builder(DebugBuilder.new) + env.add_builder(DebugBuilder) if Rscons.vars["new_user_dep"] env.depends("foo.o", "new_dep") end diff --git a/build_tests/simple/cache_varset.rb b/build_tests/simple/cache_varset.rb index a0ebe12..0c8e34e 100644 --- a/build_tests/simple/cache_varset.rb +++ b/build_tests/simple/cache_varset.rb @@ -16,7 +16,7 @@ end build do Environment.new do |env| - env.add_builder(TestBuilder.new) + env.add_builder(TestBuilder) env.TestBuilder("foo") end end diff --git a/build_tests/simple/phony_target.rb b/build_tests/simple/phony_target.rb index 665661d..8ee5071 100644 --- a/build_tests/simple/phony_target.rb +++ b/build_tests/simple/phony_target.rb @@ -1,6 +1,7 @@ build do Environment.new do |env| - env.add_builder(:Checker) do |target, sources, cache, env, vars| + env.add_builder(:Checker) do |params| + target, sources, cache, env, vars = params.values_at(:target, :sources, :cache, :env, :vars) unless cache.up_to_date?(target, :Checker, sources, env) puts "Checker #{sources.first}" if env.echo != :off cache.register_build(target, :Checker, sources, env) diff --git a/build_tests/simple/run_builder.rb b/build_tests/simple/run_builder.rb index 1f56fbc..18e4b77 100644 --- a/build_tests/simple/run_builder.rb +++ b/build_tests/simple/run_builder.rb @@ -1,13 +1,13 @@ class MyObject < Rscons::Builder def run(options) target, sources, cache, env, vars = options.values_at(:target, :sources, :cache, :env, :vars) - env.run_builder(env.builders["Object"], target, sources, cache, vars) + env.run_builder(env.builders["Object"].new, target, sources, cache, vars) end end build do Environment.new do |env| - env.add_builder(MyObject.new) + env.add_builder(MyObject) env.MyObject("simple.o", "simple.c") env.Program("simple.exe", "simple.o") end diff --git a/build_tests/simple/threading.rb b/build_tests/simple/threading.rb index b6768e1..c216c85 100644 --- a/build_tests/simple/threading.rb +++ b/build_tests/simple/threading.rb @@ -20,8 +20,8 @@ end build do Environment.new do |env| - env.add_builder(ThreadedTestBuilder.new) - env.add_builder(NonThreadedTestBuilder.new) + env.add_builder(ThreadedTestBuilder) + env.add_builder(NonThreadedTestBuilder) env.ThreadedTestBuilder("a") env.ThreadedTestBuilder("b") env.ThreadedTestBuilder("c") diff --git a/build_tests/simple/user_dep_build_order.rb b/build_tests/simple/user_dep_build_order.rb index a24cced..2243f8f 100644 --- a/build_tests/simple/user_dep_build_order.rb +++ b/build_tests/simple/user_dep_build_order.rb @@ -16,7 +16,7 @@ end build do Environment.new do |env| - env.add_builder(TestBuilder.new) + env.add_builder(TestBuilder) env.TestBuilder("one", [], "wait_time" => "3") env.TestBuilder("two", [], "wait_time" => "0") env.depends("two", "one") diff --git a/build_tests/simple/wait_for_builds_on_failure.rb b/build_tests/simple/wait_for_builds_on_failure.rb index b7ee54d..04b7867 100644 --- a/build_tests/simple/wait_for_builds_on_failure.rb +++ b/build_tests/simple/wait_for_builds_on_failure.rb @@ -13,7 +13,7 @@ end build do Environment.new do |env| - env.add_builder(Fail.new) + env.add_builder(Fail) 4.times do |i| wait_time = i + 1 env.Fail("foo_#{wait_time}", [], "wait_time" => wait_time.to_s) diff --git a/build_tests/two_sources/cache_strict_deps.rb b/build_tests/two_sources/cache_strict_deps.rb index df8c4ff..47a631b 100644 --- a/build_tests/two_sources/cache_strict_deps.rb +++ b/build_tests/two_sources/cache_strict_deps.rb @@ -16,7 +16,7 @@ end build do Environment.new(echo: :command) do |env| - env.add_builder(StrictBuilder.new) + env.add_builder(StrictBuilder) env.Object("one.o", "one.c", "CCFLAGS" => %w[-DONE]) env.Object("two.o", "two.c") sources = File.read("sources", mode: "rb").split(" ") diff --git a/build_tests/typical/backward_compatible_build_hooks.rb b/build_tests/typical/backward_compatible_build_hooks.rb index 270363a..9e63d37 100644 --- a/build_tests/typical/backward_compatible_build_hooks.rb +++ b/build_tests/typical/backward_compatible_build_hooks.rb @@ -1,12 +1,12 @@ class MyObject < Rscons::Builder def run(target, sources, cache, env, vars) - env.run_builder(env.builders["Object"], target, sources, cache, vars) + env.run_builder(env.builders["Object"].new, target, sources, cache, vars) end end build do Environment.new(echo: :command) do |env| - env.add_builder(MyObject.new) + env.add_builder(MyObject) env.append('CPPPATH' => Rscons.glob('src/**')) env.add_build_hook do |build_op| if build_op[:builder].name == "MyObject" && build_op[:sources].first =~ %r{one\.c} diff --git a/build_tests/typical/echo_command_string.rb b/build_tests/typical/echo_command_string.rb index 4b0be44..f54bc18 100644 --- a/build_tests/typical/echo_command_string.rb +++ b/build_tests/typical/echo_command_string.rb @@ -9,7 +9,7 @@ end build do Environment.new do |env| env.echo = :command - env.add_builder(MyBuilder.new) + env.add_builder(MyBuilder) env.MyBuilder("foo") end end diff --git a/lib/rscons.rb b/lib/rscons.rb index 7d1ab44..48d38d2 100644 --- a/lib/rscons.rb +++ b/lib/rscons.rb @@ -3,6 +3,7 @@ require_relative "rscons/application" require_relative "rscons/basic_environment" require_relative "rscons/build_target" require_relative "rscons/builder" +require_relative "rscons/builder_builder" require_relative "rscons/cache" require_relative "rscons/configure_op" require_relative "rscons/environment" diff --git a/lib/rscons/builder.rb b/lib/rscons/builder.rb index d40b970..ebb0465 100644 --- a/lib/rscons/builder.rb +++ b/lib/rscons/builder.rb @@ -6,21 +6,49 @@ module Rscons # Class to hold an object that knows how to build a certain type of file. class Builder + class << self + # 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 + + # Return a set of build features that this builder provides. + # + # @return [Array] + # Set of build features that this builder provides. + def features + [] + end + + # Return whether this builder object is capable of producing a given target + # file name from a given source file name. + # + # @param target [String] + # The target file name. + # @param source [String] + # The source file name. + # @param env [Environment] + # The Environment. + # + # @return [Boolean] + # Whether this builder object is capable of producing a given target + # file name from a given source file name. + def produces?(target, source, env) + false + end + 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.split(":").last - end - - # Return a set of build features that this builder provides. - # - # @return [Array] - # Set of build features that this builder provides. - def features - [] + self.class.name end # Create a BuildTarget object for this build target. @@ -43,23 +71,6 @@ module Rscons BuildTarget.new(options) end - # Return whether this builder object is capable of producing a given target - # file name from a given source file name. - # - # @param target [String] - # The target file name. - # @param source [String] - # The source file name. - # @param env [Environment] - # The Environment. - # - # @return [Boolean] - # Whether this builder object is capable of producing a given target - # file name from a given source file name. - def produces?(target, source, env) - false - end - # Set up a build operation using this builder. # # This method is called when a build target is registered using this diff --git a/lib/rscons/builder_builder.rb b/lib/rscons/builder_builder.rb new file mode 100644 index 0000000..67e2ed8 --- /dev/null +++ b/lib/rscons/builder_builder.rb @@ -0,0 +1,33 @@ +module Rscons + # A class that knows how to build an instance of another Builder class when + # it is needed. + class BuilderBuilder + + # @return [String] Builder name. + attr_reader :name + + # Create a BuilderBuilder. + # + # @param name [String] + # Builder name. + # @param builder_class [Class] + # The {Builder} class to be instantiated. + # @param builder_args [Array] + # Any extra arguments to be passed to the builder class. + # @param builder_block [Proc, nil] + # Optional block to be passed to the {Builder} class's #new method. + def initialize(name, builder_class, *builder_args, &builder_block) + @name = name + @builder_class = builder_class + @builder_args = builder_args + @builder_block = builder_block + end + + # Act like a regular {Builder} class object but really instantiate the + # requested {Builder} class, potentially with extra arguments and a block. + def new(*args) + @builder_class.new(*args, *@builder_args, &@builder_block) + end + + end +end diff --git a/lib/rscons/builders/install.rb b/lib/rscons/builders/install.rb index 7756093..036570b 100644 --- a/lib/rscons/builders/install.rb +++ b/lib/rscons/builders/install.rb @@ -43,7 +43,7 @@ module Rscons # Check the cache and copy if necessary unless cache.up_to_date?(dest, :Copy, [src], env) unless printed_message - env.print_builder_run_message("#{name} #{target}", nil) + env.print_builder_run_message("#{self.class.name} #{target}", nil) printed_message = true end cache.mkdir_p(File.dirname(dest)) diff --git a/lib/rscons/builders/object.rb b/lib/rscons/builders/object.rb index f6ca90e..6bf807f 100644 --- a/lib/rscons/builders/object.rb +++ b/lib/rscons/builders/object.rb @@ -53,24 +53,26 @@ module Rscons 'DCCMD' => ['${DC}', '-c', '-o', '${_TARGET}', '${DDEPGEN}', '${INCPREFIX}${D_IMPORT_PATH}', '${DFLAGS}', '${_SOURCES}'], ) - # Return whether this builder object is capable of producing a given target - # file name from a given source file name. - # - # @param target [String] - # The target file name. - # @param source [String] - # The source file name. - # @param env [Environment] - # The Environment. - # - # @return [Boolean] - # Whether this builder object is capable of producing a given target - # file name from a given source file name. - def produces?(target, source, env) - target.end_with?(*env['OBJSUFFIX']) and - KNOWN_SUFFIXES.find do |compiler, suffix_var| - source.end_with?(*env[suffix_var]) - end + class << self + # Return whether this builder object is capable of producing a given target + # file name from a given source file name. + # + # @param target [String] + # The target file name. + # @param source [String] + # The source file name. + # @param env [Environment] + # The Environment. + # + # @return [Boolean] + # Whether this builder object is capable of producing a given target + # file name from a given source file name. + def produces?(target, source, env) + target.end_with?(*env['OBJSUFFIX']) and + KNOWN_SUFFIXES.find do |compiler, suffix_var| + source.end_with?(*env[suffix_var]) + end + end end # Run the builder to produce a build target. diff --git a/lib/rscons/builders/preprocess.rb b/lib/rscons/builders/preprocess.rb index f3fd943..2238cc5 100644 --- a/lib/rscons/builders/preprocess.rb +++ b/lib/rscons/builders/preprocess.rb @@ -36,7 +36,7 @@ module Rscons env.produces(target, vars['_DEPFILE']) # Store vars back into options so new keys are accessible in #finalize. options[:vars] = vars - standard_threaded_build("#{name} #{target}", target, command, sources, env, cache) + standard_threaded_build("#{self.class.name} #{target}", target, command, sources, env, cache) end # Finalize the build operation. diff --git a/lib/rscons/builders/shared_library.rb b/lib/rscons/builders/shared_library.rb index a7912a3..5020647 100644 --- a/lib/rscons/builders/shared_library.rb +++ b/lib/rscons/builders/shared_library.rb @@ -14,12 +14,14 @@ module Rscons 'SHLDCMD' => ['${SHLD}', '-o', '${_TARGET}', '${SHLDFLAGS}', '${_SOURCES}', '${SHLIBDIRPREFIX}${LIBPATH}', '${SHLIBLINKPREFIX}${LIBS}'] ) - # Return a set of build features that this builder provides. - # - # @return [Array] - # Set of build features that this builder provides. - def features - %w[shared] + class << self + # Return a set of build features that this builder provides. + # + # @return [Array] + # Set of build features that this builder provides. + def features + %w[shared] + end end # Create a BuildTarget object for this build target. diff --git a/lib/rscons/builders/shared_object.rb b/lib/rscons/builders/shared_object.rb index 8c864ee..34bb522 100644 --- a/lib/rscons/builders/shared_object.rb +++ b/lib/rscons/builders/shared_object.rb @@ -30,32 +30,34 @@ module Rscons 'SHDCCMD' => ['${SHDC}', '-c', '-o', '${_TARGET}', '${INCPREFIX}${D_IMPORT_PATH}', '${SHDFLAGS}', '${_SOURCES}'], ) - # Return a set of build features that this builder provides. - # - # @return [Array] - # Set of build features that this builder provides. - def features - %w[shared] - end + class << self + # Return a set of build features that this builder provides. + # + # @return [Array] + # Set of build features that this builder provides. + def features + %w[shared] + end - # Return whether this builder object is capable of producing a given target - # file name from a given source file name. - # - # @param target [String] - # The target file name. - # @param source [String] - # The source file name. - # @param env [Environment] - # The Environment. - # - # @return [Boolean] - # Whether this builder object is capable of producing a given target - # file name from a given source file name. - def produces?(target, source, env) - target.end_with?(*env['OBJSUFFIX']) and - KNOWN_SUFFIXES.find do |compiler, suffix_var| - source.end_with?(*env[suffix_var]) - end + # Return whether this builder object is capable of producing a given target + # file name from a given source file name. + # + # @param target [String] + # The target file name. + # @param source [String] + # The source file name. + # @param env [Environment] + # The Environment. + # + # @return [Boolean] + # Whether this builder object is capable of producing a given target + # file name from a given source file name. + def produces?(target, source, env) + target.end_with?(*env['OBJSUFFIX']) and + KNOWN_SUFFIXES.find do |compiler, suffix_var| + source.end_with?(*env[suffix_var]) + end + end end # Run the builder to produce a build target. diff --git a/lib/rscons/builders/simple_builder.rb b/lib/rscons/builders/simple_builder.rb index ae21f05..512d151 100644 --- a/lib/rscons/builders/simple_builder.rb +++ b/lib/rscons/builders/simple_builder.rb @@ -1,39 +1,26 @@ module Rscons module Builders - # A Generic builder class whose name and operation is defined at - # instantiation. + # A Generic builder which has its operation is defined at instantiation. # # @since 1.8.0 class SimpleBuilder < Builder - # @return [String] The name of this builder. - attr_reader :name - # Create a new builder with the given name and action. + # Create a new builder with the given action. # - # @param name [String,Symbol] The name of the builder when registered. - # @param block [Block] - # The action to perform when the builder is processed. The provided - # block must return the target file on success or false on failure. - # The provided block should have the same signature as {Builder#run}. - def initialize(name, &block) - @name = name.to_s - @block = block + # @param run_proc [Proc] + # A Proc to execute when the builder runs. The provided block must + # provide the have the same signature as {Builder#run}. + def initialize(&run_proc) + @run_proc = run_proc end # Run the builder to produce a build target. # - # @param target [String] Target file name. - # @param sources [Array] Source file name(s). - # @param cache [Cache] The Cache object. - # @param env [Environment] The Environment executing the builder. - # @param vars [Hash,VarSet] Extra construction variables. - # - # @return [String,false] - # Name of the target file on success or false on failure. - def run(target, sources, cache, env, vars) - @block.call(target, sources, cache, env, vars) + # Method signature described by {Builder#run}. + def run(*args) + @run_proc.call(*args) end + end end end - diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index 9e3cb20..12a822a 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -72,13 +72,14 @@ module Rscons @side_effects = {} @job_set = JobSet.new(@registered_build_dependencies, @side_effects) @user_deps = {} + # Hash of builder name (String) => builder class (Class). @builders = {} @build_hooks = {pre: [], post: []} unless options[:exclude_builders] DEFAULT_BUILDERS.each do |builder_class_name| builder_class = Builders.const_get(builder_class_name) builder_class or raise "Could not find builder class #{builder_class_name}" - add_builder(builder_class.new) + add_builder(builder_class) end end @echo = @@ -146,22 +147,22 @@ module Rscons env end - # Add a {Builder} object to the Environment. + # Add a {Builder} to the Environment. # - # @overload add_builder(builder) + # @overload add_builder(builder_class) # # Add the given builder to the Environment. # - # @param builder [Builder] An instance of the builder to register. + # @param builder_class [Class] A builder class to register. # - # @overload add_builder(builder,&action) + # @overload add_builder(name,&action) # # Create a new {Builders::SimpleBuilder} instance and add it to the # environment. # # @since 1.8.0 # - # @param builder [String,Symbol] + # @param name [String,Symbol] # The name of the builder to add. # # @param action [Block] @@ -170,11 +171,11 @@ module Rscons # {Rscons::Builder#run}. # # @return [void] - def add_builder(builder, &action) - if not builder.is_a? Rscons::Builder - builder = Rscons::Builders::SimpleBuilder.new(builder, &action) + def add_builder(builder_class, &action) + if builder_class.is_a?(String) or builder_class.is_a?(Symbol) + builder_class = BuilderBuilder.new(builder_class.to_s, Rscons::Builders::SimpleBuilder, &action) end - @builders[builder.name] = builder + @builders[builder_class.name] = builder_class end # Add a build hook to the Environment. @@ -351,7 +352,7 @@ module Rscons unless vars.is_a?(Hash) or vars.is_a?(VarSet) raise "Unexpected construction variable set: #{vars.inspect}" end - builder = @builders[method.to_s] + builder = @builders[method.to_s].new target = expand_path(expand_varref(target)) sources = Array(sources).map do |source| expand_path(expand_varref(source)) @@ -789,22 +790,22 @@ module Rscons # @return [Builder, nil] # The builder found, if any. def find_builder_for(target, source, features) - @builders.values.find do |builder| - features_met?(builder, features) and builder.produces?(target, source, self) + @builders.values.find do |builder_class| + features_met?(builder_class, features) and builder_class.produces?(target, source, self) end end # Determine if a builder meets the requested features. # - # @param builder [Builder] + # @param builder_class [Class] # The builder. # @param features [Array] # See {#register_builds}. # # @return [Boolean] # Whether the builder meets the requested features. - def features_met?(builder, features) - builder_features = builder.features + def features_met?(builder_class, features) + builder_features = builder_class.features features.all? do |feature| want_feature = true if feature =~ /^-(.*)$/ diff --git a/spec/rscons/builders/simple_builder_spec.rb b/spec/rscons/builders/simple_builder_spec.rb index 0031221..569a2fe 100644 --- a/spec/rscons/builders/simple_builder_spec.rb +++ b/spec/rscons/builders/simple_builder_spec.rb @@ -3,15 +3,8 @@ module Rscons describe SimpleBuilder do let(:env) {Environment.new} - it "should create a new builder with the given name (as a symbol) and action" do - builder = Rscons::Builders::SimpleBuilder.new(:Foo) { 0x1234 } - expect(builder.name).to eq("Foo") - expect(builder.run(1,2,3,4,5)).to eq(0x1234) - end - - it "should create a new builder with the given name (as a string) and action" do - builder = Rscons::Builders::SimpleBuilder.new("Foo") { 0x1234 } - expect(builder.name).to eq("Foo") + it "should create a new builder with the given action" do + builder = Rscons::Builders::SimpleBuilder.new { 0x1234 } expect(builder.run(1,2,3,4,5)).to eq(0x1234) end end diff --git a/spec/rscons/environment_spec.rb b/spec/rscons/environment_spec.rb index f3459e4..351dc9b 100644 --- a/spec/rscons/environment_spec.rb +++ b/spec/rscons/environment_spec.rb @@ -4,7 +4,7 @@ module Rscons it "adds the default builders when they are not excluded" do env = Environment.new expect(env.builders.size).to be > 0 - expect(env.builders.map {|name, builder| builder.is_a?(Builder)}.all?).to be_truthy + expect(env.builders.map {|name, builder| builder.is_a?(Class)}.all?).to be_truthy expect(env.builders.find {|name, builder| name == "Object"}).to_not be_nil expect(env.builders.find {|name, builder| name == "Program"}).to_not be_nil expect(env.builders.find {|name, builder| name == "Library"}).to_not be_nil @@ -52,7 +52,7 @@ module Rscons it "adds the builder to the list of builders" do env = Environment.new(exclude_builders: true) expect(env.builders.keys).to eq [] - env.add_builder(Rscons::Builders::Object.new) + env.add_builder(Rscons::Builders::Object) expect(env.builders.keys).to eq ["Object"] end