diff --git a/build_tests/custom_builder/produces.rb b/build_tests/custom_builder/produces.rb new file mode 100644 index 0000000..079267b --- /dev/null +++ b/build_tests/custom_builder/produces.rb @@ -0,0 +1,14 @@ +Rscons::Environment.new do |env| + env["build_root"] = env.build_root + env["inc_h"] = "inc.h" + + env.Copy("copy_inc.h", "${inc_h}") + env.depends("${build_root}/program.o", "${inc_h}") + env.Program("program.exe", ["program.c", "inc.c"]) + + env.Command("inc.c", + [], + "CMD" => %w[ruby gen.rb ${_TARGET}], + "CMD_DESC" => "Generating") + env.produces("inc.c", "inc.h") +end diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index fe46ef3..b79c0da 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -45,8 +45,9 @@ module Rscons def initialize(options = {}) @threaded_commands = Set.new @registered_build_dependencies = {} + @side_effects = {} @varset = VarSet.new - @job_set = JobSet.new(@registered_build_dependencies) + @job_set = JobSet.new(@registered_build_dependencies, @side_effects) @user_deps = {} @builders = {} @build_dirs = [] @@ -502,6 +503,26 @@ module Rscons end end + # Manually record the given side effect file(s) as being produced when the + # named target is produced. + # + # @since 1.13.0 + # + # @param target [String] + # Target of a build operation. + # @param side_effects [Array] + # File(s) produced when the target file is produced. + # + # @return [void] + def produces(target, *side_effects) + target = expand_path(expand_varref(target)) + side_effects = Array(side_effects).map do |side_effect| + expand_path(expand_varref(side_effect)) + end.flatten + @side_effects[target] ||= [] + @side_effects[target] += side_effects + end + # Return the list of user dependencies for a given target. # # @param target [String] Target file name. diff --git a/lib/rscons/job_set.rb b/lib/rscons/job_set.rb index c4b24ab..942c81b 100644 --- a/lib/rscons/job_set.rb +++ b/lib/rscons/job_set.rb @@ -9,9 +9,14 @@ module Rscons # @param build_dependencies [Hash] # Hash mapping targets to a set of build dependencies. A job will not be # returned as ready to run if any of its dependencies are still building. - def initialize(build_dependencies) + # @param side_effects [Hash] + # Hash mapping targets to a set of side-effect files. A job will not be + # returned as ready to run if any of its dependencies is a side-effect + # of another target that has not yet been built. + def initialize(build_dependencies, side_effects) @jobs = {} @build_dependencies = build_dependencies + @side_effects = side_effects end # Add a job to the JobSet. @@ -47,18 +52,16 @@ module Rscons # @return [nil, Hash] # The next job to run. def get_next_job_to_run(targets_still_building) + targets_not_built_yet = targets_still_building + @jobs.keys + side_effects = targets_not_built_yet.map do |target| + @side_effects[target] || [] + end.flatten + targets_not_built_yet += side_effects + @jobs.keys.each do |target| skip = false (@jobs[target][0][:sources] + (@build_dependencies[target] || []).to_a).each do |src| - if @jobs.include?(src) - # Skip this target because it depends on another target not yet - # built. - skip = true - break - end - if targets_still_building.include?(src) - # Skip this target because it depends on another target that is - # currently being built. + if targets_not_built_yet.include?(src) skip = true break end diff --git a/spec/build_tests_spec.rb b/spec/build_tests_spec.rb index aa52793..963fb5e 100644 --- a/spec/build_tests_spec.rb +++ b/spec/build_tests_spec.rb @@ -1306,6 +1306,13 @@ EOF result = run_test(rsconsfile: "build_after.rb", rscons_args: %w[-j 4]) expect(result.stderr).to eq "" end + + it "allows the user to specify side-effect files produced by another builder" do + test_dir("custom_builder") + result = run_test(rsconsfile: "produces.rb", rscons_args: %w[-j 4]) + expect(result.stderr).to eq "" + expect(File.exists?("copy_inc.h")).to be_truthy + end end context "CLI" do