diff --git a/lib/rscons/builder.rb b/lib/rscons/builder.rb index a2c27fb..c22d2b5 100644 --- a/lib/rscons/builder.rb +++ b/lib/rscons/builder.rb @@ -22,12 +22,12 @@ module Rscons # Check if the cache is up to date for the target and if not execute the # build command. # Return the name of the target or false on failure. - def standard_build(short_cmd_string, target, command, sources, user_deps, env, cache) - unless cache.up_to_date?(target, command, sources, user_deps) + def standard_build(short_cmd_string, target, command, sources, env, cache) + unless cache.up_to_date?(target, command, sources, env) cache.mkdir_p(File.dirname(target)) FileUtils.rm_f(target) return false unless env.execute(short_cmd_string, command) - cache.register_build(target, command, sources, user_deps) + cache.register_build(target, command, sources, env) end target end diff --git a/lib/rscons/builders/library.rb b/lib/rscons/builders/library.rb index cefbd3f..8d91370 100644 --- a/lib/rscons/builders/library.rb +++ b/lib/rscons/builders/library.rb @@ -12,7 +12,7 @@ module Rscons } end - def run(target, sources, user_deps, cache, env, vars) + def run(target, sources, cache, env, vars) # build sources to linkable objects objects = env.build_sources(sources, [env['OBJSUFFIX'], env['LIBSUFFIX']].flatten, cache, vars) if objects @@ -21,7 +21,7 @@ module Rscons '_SOURCES' => objects, }) command = env.build_command(env['ARCMD'], vars) - standard_build("AR #{target}", target, command, objects, user_deps, env, cache) + standard_build("AR #{target}", target, command, objects, env, cache) end end end diff --git a/lib/rscons/builders/object.rb b/lib/rscons/builders/object.rb index 7489dd3..20322ef 100644 --- a/lib/rscons/builders/object.rb +++ b/lib/rscons/builders/object.rb @@ -50,7 +50,7 @@ module Rscons end end - def run(target, sources, user_deps, cache, env, vars) + def run(target, sources, cache, env, vars) vars = vars.merge({ '_TARGET' => target, '_SOURCES' => sources, @@ -62,7 +62,7 @@ module Rscons v.nil? and raise "Error: unknown input file type: #{sources.first.inspect}" end.first command = env.build_command(env["#{com_prefix}CMD"], vars) - unless cache.up_to_date?(target, command, sources, user_deps) + unless cache.up_to_date?(target, command, sources, env) cache.mkdir_p(File.dirname(target)) FileUtils.rm_f(target) return false unless env.execute("#{com_prefix} #{target}", command) @@ -71,7 +71,7 @@ module Rscons deps += Environment.parse_makefile_deps(vars['_DEPFILE'], target) FileUtils.rm_f(vars['_DEPFILE']) end - cache.register_build(target, command, deps.uniq, user_deps) + cache.register_build(target, command, deps.uniq, env) end target end diff --git a/lib/rscons/builders/program.rb b/lib/rscons/builders/program.rb index 1b08de8..850c80e 100644 --- a/lib/rscons/builders/program.rb +++ b/lib/rscons/builders/program.rb @@ -14,7 +14,7 @@ module Rscons } end - def run(target, sources, user_deps, cache, env, vars) + def run(target, sources, cache, env, vars) # build sources to linkable objects objects = env.build_sources(sources, [env['OBJSUFFIX'], env['LIBSUFFIX']].flatten, cache, vars) return false unless objects @@ -33,7 +33,7 @@ module Rscons 'LD' => ld, }) command = env.build_command(env['LDCMD'], vars) - standard_build("LD #{target}", target, command, objects, user_deps, env, cache) + standard_build("LD #{target}", target, command, objects, env, cache) end end end diff --git a/lib/rscons/cache.rb b/lib/rscons/cache.rb index c361ac0..c35b340 100644 --- a/lib/rscons/cache.rb +++ b/lib/rscons/cache.rb @@ -89,7 +89,7 @@ module Rscons # @param target [String] The name of the target file. # @param command [Array] The command used to build the target. # @param deps [Array] List of the target's dependency files. - # @param user_deps [Array] List of user-specified extra dependencies. + # @param env [Environment] The Rscons::Environment. # @param options [Hash] Optional options. Can contain the following keys: # :strict_deps:: # Only consider a target up to date if its list of dependencies is @@ -104,7 +104,7 @@ module Rscons # exactly equal to those cached # - each cached dependency file's current checksum matches the checksum # stored in the cache file - def up_to_date?(target, command, deps, user_deps, options = {}) + def up_to_date?(target, command, deps, env, options = {}) # target file must exist on disk return false unless File.exists?(target) @@ -117,20 +117,24 @@ module Rscons # command used to build target must be identical return false unless @cache[:targets][target][:command] == command - cached_deps = @cache[:targets][target][:deps].map { |dc| dc[:fname] } + cached_deps = @cache[:targets][target][:deps] or [] + cached_deps_fnames = cached_deps.map { |dc| dc[:fname] } if options[:strict_deps] # depedencies passed in must exactly equal those in the cache - return false unless deps == cached_deps + return false unless deps == cached_deps_fnames else # all dependencies passed in must exist in cache (but cache may have more) - return false unless (Set.new(deps) - Set.new(cached_deps)).empty? + return false unless (Set.new(deps) - Set.new(cached_deps_fnames)).empty? end # set of user dependencies must match - return false unless user_deps == @cache[:targets][target][:user_deps].map { |dc| dc[:fname] } + user_deps = env.get_user_deps(target) || [] + cached_user_deps = @cache[:targets][target][:user_deps] or [] + cached_user_deps_fnames = cached_user_deps.map { |dc| dc[:fname] } + return false unless user_deps == cached_user_deps_fnames # all cached dependencies must have their checksums match - (@cache[:targets][target][:deps] + @cache[:targets][target][:user_deps]).each do |dep_cache| + (cached_deps + cached_user_deps).each do |dep_cache| return false unless dep_cache[:checksum] == lookup_checksum(dep_cache[:fname]) end @@ -141,8 +145,8 @@ module Rscons # @param target [String] The name of the target. # @param command [Array] The command used to build the target. # @param deps [Array] List of dependencies for the target. - # @param user_deps [Array] List of user-specified extra dependencies. - def register_build(target, command, deps, user_deps) + # @param env [Environment] The Rscons::Environment. + def register_build(target, command, deps, env) @cache[:targets][target.encode(__ENCODING__)] = { command: command, checksum: calculate_checksum(target), @@ -152,7 +156,7 @@ module Rscons checksum: lookup_checksum(dep), } end, - user_deps: user_deps.map do |dep| + user_deps: (env.get_user_deps(target) || []).map do |dep| { fname: dep.encode(__ENCODING__), checksum: lookup_checksum(dep), diff --git a/lib/rscons/environment.rb b/lib/rscons/environment.rb index 2985541..edb6dc6 100644 --- a/lib/rscons/environment.rb +++ b/lib/rscons/environment.rb @@ -151,7 +151,6 @@ module Rscons result = run_builder(@targets[target][:builder], target, @targets[target][:source], - @user_deps[target] || [], cache, @targets[target][:vars] || {}) unless result @@ -232,6 +231,12 @@ module Rscons @user_deps[target] = (@user_deps[target] + user_deps).uniq end + # Return the list of user dependencies for a given target, or +nil+ for + # none. + def get_user_deps(target) + @user_deps[target] + end + # Build a list of source files into files containing one of the suffixes # given by suffixes. # This method is used internally by RScons builders. @@ -250,7 +255,7 @@ module Rscons converted_fname = get_build_fname(source, suffix) builder = @builders.values.find { |b| b.produces?(converted_fname, source, self) } if builder - converted = run_builder(builder, converted_fname, [source], [], cache, vars) + converted = run_builder(builder, converted_fname, [source], cache, vars) return nil unless converted break end @@ -267,19 +272,18 @@ module Rscons # @param cache [Cache] The Cache. # @param vars [Hash] Extra variables to pass to the builder. # Return the result of the builder's run() method. - def run_builder(builder, target, sources, user_deps, cache, vars) + def run_builder(builder, target, sources, cache, vars) vars = @varset.merge(vars) @build_hooks.each do |build_hook_block| build_operation = { builder: builder, target: target, sources: sources, - user_deps: user_deps, vars: vars, } build_hook_block.call(build_operation) end - builder.run(target, sources, user_deps, cache, self, vars) + builder.run(target, sources, cache, self, vars) end # Parse dependencies for a given target from a Makefile. diff --git a/spec/rscons/cache_spec.rb b/spec/rscons/cache_spec.rb index b995a9a..2e50b61 100644 --- a/spec/rscons/cache_spec.rb +++ b/spec/rscons/cache_spec.rb @@ -38,14 +38,19 @@ module Rscons end describe "#up_to_date?" do + empty_env = "env" + before do + empty_env.stub(:get_user_deps) { nil } + end + it "returns false when target file does not exist" do File.should_receive(:exists?).with("target").and_return(false) - build_from({}).up_to_date?("target", "command", [], []).should be_false + build_from({}).up_to_date?("target", "command", [], empty_env).should be_false end it "returns false when target is not registered in the cache" do File.should_receive(:exists?).with("target").and_return(true) - build_from({}).up_to_date?("target", "command", [], []).should be_false + build_from({}).up_to_date?("target", "command", [], empty_env).should be_false end it "returns false when the target's checksum does not match" do @@ -53,7 +58,7 @@ module Rscons cache = build_from(_cache) File.should_receive(:exists?).with("target").and_return(true) cache.should_receive(:calculate_checksum).with("target").and_return("def") - cache.up_to_date?("target", "command", [], []).should be_false + cache.up_to_date?("target", "command", [], empty_env).should be_false end it "returns false when the build command has changed" do @@ -61,7 +66,7 @@ module Rscons cache = build_from(_cache) File.should_receive(:exists?).with("target").and_return(true) cache.should_receive(:calculate_checksum).with("target").and_return("abc") - cache.up_to_date?("target", "command", [], []).should be_false + cache.up_to_date?("target", "command", [], empty_env).should be_false end it "returns false when there is a new dependency" do @@ -71,7 +76,7 @@ module Rscons cache = build_from(_cache) File.should_receive(:exists?).with("target").and_return(true) cache.should_receive(:calculate_checksum).with("target").and_return("abc") - cache.up_to_date?("target", "command", ["dep.1", "dep.2"], []).should be_false + cache.up_to_date?("target", "command", ["dep.1", "dep.2"], empty_env).should be_false end it "returns false when a dependency's checksum has changed" do @@ -89,7 +94,7 @@ module Rscons cache.should_receive(:calculate_checksum).with("target").and_return("abc") cache.should_receive(:calculate_checksum).with("dep.1").and_return("dep.1.chk") cache.should_receive(:calculate_checksum).with("dep.2").and_return("dep.2.changed") - cache.up_to_date?("target", "command", ["dep.1", "dep.2"], []).should be_false + cache.up_to_date?("target", "command", ["dep.1", "dep.2"], empty_env).should be_false end it "returns false with strict_deps=true when cache has an extra dependency" do @@ -105,7 +110,7 @@ module Rscons cache = build_from(_cache) File.should_receive(:exists?).with("target").and_return(true) cache.should_receive(:calculate_checksum).with("target").and_return("abc") - cache.up_to_date?("target", "command", ["dep.1", "dep.2"], [], strict_deps: true).should be_false + cache.up_to_date?("target", "command", ["dep.1", "dep.2"], empty_env, strict_deps: true).should be_false end it "returns false when there is a new user dependency" do @@ -114,9 +119,11 @@ module Rscons deps: [{fname: "dep.1"}], user_deps: []}}} cache = build_from(_cache) + env = "env" + env.should_receive(:get_user_deps).with("target").and_return(["file.ld"]) File.should_receive(:exists?).with("target").and_return(true) cache.should_receive(:calculate_checksum).with("target").and_return("abc") - cache.up_to_date?("target", "command", ["dep.1"], ["file.ld"]).should be_false + cache.up_to_date?("target", "command", ["dep.1"], env).should be_false end it "returns false when a user dependency checksum has changed" do @@ -131,13 +138,15 @@ module Rscons user_deps: [{fname: "user.dep", checksum: "user.dep.chk"}]}}} cache = build_from(_cache) + env = "env" + env.should_receive(:get_user_deps).with("target").and_return(["user.dep"]) File.should_receive(:exists?).with("target").and_return(true) cache.should_receive(:calculate_checksum).with("target").and_return("abc") cache.should_receive(:calculate_checksum).with("dep.1").and_return("dep.1.chk") cache.should_receive(:calculate_checksum).with("dep.2").and_return("dep.2.chk") cache.should_receive(:calculate_checksum).with("extra.dep").and_return("extra.dep.chk") cache.should_receive(:calculate_checksum).with("user.dep").and_return("INCORRECT") - cache.up_to_date?("target", "command", ["dep.1", "dep.2"], ["user.dep"]).should be_false + cache.up_to_date?("target", "command", ["dep.1", "dep.2"], env).should be_false end it "returns true when no condition for false is met" do @@ -156,7 +165,7 @@ module Rscons cache.should_receive(:calculate_checksum).with("dep.1").and_return("dep.1.chk") cache.should_receive(:calculate_checksum).with("dep.2").and_return("dep.2.chk") cache.should_receive(:calculate_checksum).with("extra.dep").and_return("extra.dep.chk") - cache.up_to_date?("target", "command", ["dep.1", "dep.2"], []).should be_true + cache.up_to_date?("target", "command", ["dep.1", "dep.2"], empty_env).should be_true end end @@ -164,11 +173,13 @@ module Rscons it "stores the given information in the cache" do _cache = {} cache = build_from(_cache) + env = "env" + env.should_receive(:get_user_deps).with("the target").and_return(["user.dep"]) cache.should_receive(:calculate_checksum).with("the target").and_return("the checksum") cache.should_receive(:calculate_checksum).with("dep 1").and_return("dep 1 checksum") cache.should_receive(:calculate_checksum).with("dep 2").and_return("dep 2 checksum") cache.should_receive(:calculate_checksum).with("user.dep").and_return("user.dep checksum") - cache.register_build("the target", "the command", ["dep 1", "dep 2"], ["user.dep"]) + cache.register_build("the target", "the command", ["dep 1", "dep 2"], env) cached_target = cache.instance_variable_get(:@cache)[:targets]["the target"] cached_target.should_not be_nil cached_target[:command].should == "the command" diff --git a/spec/rscons/environment_spec.rb b/spec/rscons/environment_spec.rb index 0c31b8f..3ca170a 100644 --- a/spec/rscons/environment_spec.rb +++ b/spec/rscons/environment_spec.rb @@ -141,7 +141,7 @@ module Rscons cache = "cache" Cache.should_receive(:new).and_return(cache) - env.should_receive(:run_builder).with(anything, "a.out", ["main.c"], [], cache, {}).and_return(true) + env.should_receive(:run_builder).with(anything, "a.out", ["main.c"], cache, {}).and_return(true) cache.should_receive(:write) env.process @@ -154,8 +154,8 @@ module Rscons cache = "cache" Cache.should_receive(:new).and_return(cache) - env.should_receive(:run_builder).with(anything, "main.o", ["other.cc"], [], cache, {}).and_return("main.o") - env.should_receive(:run_builder).with(anything, "a.out", ["main.o"], [], cache, {}).and_return("a.out") + env.should_receive(:run_builder).with(anything, "main.o", ["other.cc"], cache, {}).and_return("main.o") + env.should_receive(:run_builder).with(anything, "a.out", ["main.o"], cache, {}).and_return("a.out") cache.should_receive(:write) env.process @@ -168,7 +168,7 @@ module Rscons cache = "cache" Cache.should_receive(:new).and_return(cache) - env.should_receive(:run_builder).with(anything, "main.o", ["other.cc"], [], cache, {}).and_return(false) + env.should_receive(:run_builder).with(anything, "main.o", ["other.cc"], cache, {}).and_return(false) cache.should_receive(:write) expect { env.process }.to raise_error BuildError, /Failed.to.build.main.o/ @@ -282,8 +282,8 @@ module Rscons cache = "cache" env = Environment.new env.add_builder(ABuilder.new) - env.builders["Object"].should_receive(:run).with("mod.o", ["mod.c"], [], cache, env, anything).and_return("mod.o") - env.builders["ABuilder"].should_receive(:run).with("mod2.ab_out", ["mod2.ab_in"], [], cache, env, anything).and_return("mod2.ab_out") + env.builders["Object"].should_receive(:run).with("mod.o", ["mod.c"], cache, env, anything).and_return("mod.o") + env.builders["ABuilder"].should_receive(:run).with("mod2.ab_out", ["mod2.ab_in"], cache, env, anything).and_return("mod2.ab_out") env.build_sources(["precompiled.o", "mod.c", "mod2.ab_in"], [".o", ".ab_out"], cache, {}).should == ["precompiled.o", "mod.o", "mod2.ab_out"] end end @@ -296,14 +296,14 @@ module Rscons build_op[:vars]["CFLAGS"] += ["-O3", "-DSPECIAL"] end end - env.builders["Object"].stub(:run) do |target, sources, user_deps, cache, env, vars| + env.builders["Object"].stub(:run) do |target, sources, cache, env, vars| vars["CFLAGS"].should == [] end - env.run_builder(env.builders["Object"], "build/normal/module.o", ["src/normal/module.c"], [], "cache", {}) - env.builders["Object"].stub(:run) do |target, sources, user_deps, cache, env, vars| + env.run_builder(env.builders["Object"], "build/normal/module.o", ["src/normal/module.c"], "cache", {}) + env.builders["Object"].stub(:run) do |target, sources, cache, env, vars| vars["CFLAGS"].should == ["-O3", "-DSPECIAL"] end - env.run_builder(env.builders["Object"], "build/special/module.o", ["src/special/module.c"], [], "cache", {}) + env.run_builder(env.builders["Object"], "build/special/module.o", ["src/special/module.c"], "cache", {}) end end