diff --git a/build_tests/build_tests.rb b/build_tests/build_tests.rb index eaa34c5..1961105 100644 --- a/build_tests/build_tests.rb +++ b/build_tests/build_tests.rb @@ -726,7 +726,7 @@ test 'supports build hooks to override the entire vars hash' do result = run_rscons(args: %w[-f build_hooks_override_vars.rb]) expect_eq(result.stderr, "") verify_lines(lines(result.stdout), [ - %r{gcc -c -o one.o -MMD -MF build/o/one.o.mf -Isrc -Isrc/one -Isrc/two -O1 src/two/two.c}, + %r{gcc -c -o one.o -MMD -MF build/o/one.o.mf -Isrc -Isrc/one -Isrc/two -O1 src/one/one.c}, %r{gcc -c -o two.o -MMD -MF build/o/two.o.mf -Isrc -Isrc/one -Isrc/two -O2 src/two/two.c}, ]) expect_truthy(File.exist?('one.o')) @@ -3557,4 +3557,12 @@ test "supports a Barrier builder to order builds" do expect_match(slines[2], /B:one/) end +test "maps relative paths to absolute paths for dependency resolution" do + test_dir "simple" + result = run_rscons(args: %w[-f abs_rel_paths.rb]) + expect_eq(result.stderr, "") + expect_eq(result.status, 0) + expect_match(result.stdout, /three.*two.*one/m) +end + run_tests diff --git a/build_tests/simple/abs_rel_paths.rb b/build_tests/simple/abs_rel_paths.rb new file mode 100644 index 0000000..fd1c721 --- /dev/null +++ b/build_tests/simple/abs_rel_paths.rb @@ -0,0 +1,14 @@ +class B < Builder + def run(*args) + puts @target + true + end +end + +env do |env| + env.add_builder(B) + env.B("one", File.expand_path("two")) + env.B("two") + env.B("three") + env.depends("two", File.expand_path("three")) +end diff --git a/build_tests/typical/build_hooks_override_vars.rb b/build_tests/typical/build_hooks_override_vars.rb index d6810c3..6b46ce2 100644 --- a/build_tests/typical/build_hooks_override_vars.rb +++ b/build_tests/typical/build_hooks_override_vars.rb @@ -3,7 +3,6 @@ env(echo: :command) do |env| env.add_build_hook do |builder| if builder.name == "Object" && builder.sources.first =~ %r{one\.c} builder.vars["CFLAGS"] << "-O1" - builder.sources = ['src/two/two.c'] elsif builder.name == "Object" && builder.target =~ %r{two\.o} new_vars = builder.vars.clone new_vars["CFLAGS"] << "-O2" diff --git a/lib/rscons/builder.rb b/lib/rscons/builder.rb index fa844dd..6b66851 100644 --- a/lib/rscons/builder.rb +++ b/lib/rscons/builder.rb @@ -29,19 +29,27 @@ module Rscons # @return [String, Symbol] # Target file name. - attr_accessor :target + attr_reader :target - # @return [Array] - # Source file name(s). - attr_accessor :sources + # @return [String, Symbol] + # Absolute target file name. + attr_reader :abstarget + + # @return [Array] + # Source file names. + attr_reader :sources + + # @return [Array] + # Absolute source file names. + attr_reader :abssources # @return [Cache] # Cache instance. - attr_accessor :cache + attr_reader :cache # @return [Environment] # The {Environment} performing the build operation. - attr_accessor :env + attr_reader :env # @return [Hash, VarSet] # Construction variables used to perform the build operation. @@ -49,7 +57,7 @@ module Rscons # @return [Set] # Side effect file(s) produced when this builder runs. - attr_accessor :side_effects + attr_reader :side_effects # @return [Integer] # Build step. @@ -71,7 +79,11 @@ module Rscons # Extra construction variables. def initialize(options) @target = options[:target] - @sources = options[:sources] + @abstarget = Util.absolute_path(@target) + @sources = Array(options[:sources]) + @abssources = @sources.map do |source| + Util.absolute_path(source) + end @cache = options[:cache] @env = options[:env] @vars = options[:vars] @@ -103,7 +115,7 @@ module Rscons # @return [void] def produces(*side_effects) side_effects.each do |side_effect| - side_effect_expanded = @env.expand(side_effect) + side_effect_expanded = Util.absolute_path(@env.expand(side_effect)) @env.register_side_effect(side_effect_expanded) @side_effects << side_effect_expanded end diff --git a/lib/rscons/builder_set.rb b/lib/rscons/builder_set.rb index f0bae36..bec7449 100644 --- a/lib/rscons/builder_set.rb +++ b/lib/rscons/builder_set.rb @@ -27,8 +27,8 @@ module Rscons # env.Directory("dest") # env.Install("dest", "bin") # env.Install("dest", "share") - self[builder.target] ||= [] - self[builder.target] << builder + self[builder.abstarget] ||= [] + self[builder.abstarget] << builder end # Return the number of remaining build steps. @@ -54,7 +54,7 @@ module Rscons # The next builder to run. def get_next_builder_to_run(targets_still_building) to_build = self.find do |target, builders| - deps = builders.first.sources + (@build_dependencies[target] || []).to_a + deps = builders.first.abssources + (@build_dependencies[target] || []).to_a # All dependencies must have been built for this target to be ready to # build. deps.all? do |dep| @@ -79,7 +79,7 @@ module Rscons # not find a builder to run above, then there might be a circular # dependency introduced by the user. if (self.size > 0) and targets_still_building.empty? - raise "Could not find a runnable builder. Possible circular dependency for #{self.keys.first}" + raise "Could not find a runnable builder. Possible circular dependency for #{self.first[1].first.target}" end end diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index ebaaba6..538cb03 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -371,8 +371,8 @@ module Rscons @builder_sets << build_builder_set end @builder_sets.last << builder - @build_steps += 1 - @build_targets[target] = builder + @build_steps += 1 unless builder.is_a?(Rscons::Builders::Barrier) + @build_targets[builder.abstarget] = builder builder else super @@ -396,8 +396,10 @@ module Rscons end expand(ud) end + target = Util.absolute_path(target) @user_deps[target] ||= [] - (@user_deps[target] + user_deps).each do |ud| + user_deps.map! {|ud| Util.absolute_path(ud)} + user_deps.each do |ud| unless Rscons.phony_target?(ud) || @user_deps[target].include?(ud) @user_deps[target] << ud end @@ -432,13 +434,13 @@ module Rscons targets = Array(targets) prerequisites = Array(prerequisites) targets.each do |target| - target = expand(target) + target = Util.absolute_path(expand(target)) @registered_build_dependencies[target] ||= Set.new prerequisites.each do |prerequisite| if prerequisite.is_a?(Builder) prerequisite = prerequisite.target end - prerequisite = expand(prerequisite) + prerequisite = Util.absolute_path(expand(prerequisite)) @registered_build_dependencies[target] << prerequisite end end @@ -454,9 +456,9 @@ module Rscons # # @return [void] def produces(target, *side_effects) - target = expand(target) + abstarget = Util.absolute_path(expand(target)) @builder_sets.reverse.each do |builder_set| - if builders = builder_set[target] + if builders = builder_set[abstarget] builders.last.produces(*side_effects) return end @@ -473,7 +475,7 @@ module Rscons # @param side_effect [String] # Side effect fiel name. def register_side_effect(side_effect) - @side_effects << side_effect + @side_effects << Util.absolute_path(side_effect) end # Return the list of user dependencies for a given target. @@ -484,6 +486,7 @@ module Rscons # List of user-specified dependencies for the target, or nil if none were # specified. def get_user_deps(target) + target = Util.absolute_path(target) @user_deps[target] end @@ -507,10 +510,11 @@ module Rscons # @return [String] # Output file name. def register_dependency_build(target, source, suffix, vars, builder_class) + target = Util.absolute_path(target) output_fname = get_build_fname(source, suffix, builder_class) self.__send__(builder_class.name, output_fname, source, vars) @registered_build_dependencies[target] ||= Set.new - @registered_build_dependencies[target] << output_fname + @registered_build_dependencies[target] << Util.absolute_path(output_fname) output_fname end @@ -587,6 +591,7 @@ module Rscons # @return [Builder, nil] # The {Builder} for target, or +nil+ if none found. def builder_for(target) + target = Util.absolute_path(target) @build_targets[target] end @@ -730,7 +735,7 @@ module Rscons # If no builder was found to run yet and there are threads available, try # to get a runnable builder from the builder set. targets_still_building = @threads.reduce([]) do |result, (thread, obj)| - result << builder_for_thread(thread).target + result << builder_for_thread(thread).abstarget end if @builder_sets.size > 0 if builder = @builder_sets[0].get_next_builder_to_run(targets_still_building) diff --git a/lib/rscons/util.rb b/lib/rscons/util.rb index 20c4434..8bacb73 100644 --- a/lib/rscons/util.rb +++ b/lib/rscons/util.rb @@ -18,15 +18,16 @@ module Rscons # Return the absolute path for a given target if not a phony target. # - # @param path [String, Symbol] + # @param path [String, Symbol, nil] # Given target name. # - # @return Absolute path of given target. + # @return [String, Symbol, nil] + # Absolute path of given target. def absolute_path(path) - if Rscons.phony_target?(path) - path - else + if path.is_a?(String) File.expand_path(path) + else + path end end diff --git a/spec/rscons/environment_spec.rb b/spec/rscons/environment_spec.rb index e6feb14..0aa7545 100644 --- a/spec/rscons/environment_spec.rb +++ b/spec/rscons/environment_spec.rb @@ -138,27 +138,6 @@ module Rscons end end - describe "#depends" do - it "records the given dependencies in @user_deps" do - env = Environment.new - env.depends("foo", "bar", "baz") - expect(env.instance_variable_get(:@user_deps)).to eq({"foo" => ["bar", "baz"]}) - end - it "records user dependencies only once" do - env = Environment.new - env.instance_variable_set(:@user_deps, {"foo" => ["bar"]}) - env.depends("foo", "bar", "baz") - expect(env.instance_variable_get(:@user_deps)).to eq({"foo" => ["bar", "baz"]}) - end - it "expands arguments for construction variable references" do - env = Environment.new - env["foo"] = "foo.exe" - env["bar"] = "bar.c" - env.depends("${foo}", "${bar}", "a.h") - expect(env.instance_variable_get(:@user_deps)).to eq({"foo.exe" => ["bar.c", "a.h"]}) - end - end - describe "#shell" do it "executes the given shell command and returns the results" do env = Environment.new