support passing multiple targets to Cache#up_to_date? and #register_build

This commit is contained in:
Josh Holtrop 2014-02-17 16:10:15 -05:00
parent f7b7a4fefa
commit 1d47db3144
2 changed files with 90 additions and 52 deletions

View File

@ -85,16 +85,17 @@ module Rscons
end end
end end
# Check if a target is up to date # Check if target(s) are up to date
# @param target [String] The name of the target file. # @param targets [String, Array] The name(s) of the target file(s).
# @param command [Array] The command used to build the target. # @param command [String, Array] The command used to build the target.
# @param deps [Array] List of the target's dependency files. # @param deps [Array] List of the target's dependency files.
# @param env [Environment] The Rscons::Environment. # @param env [Environment] The Rscons::Environment.
# @param options [Hash] Optional options. Can contain the following keys: # @param options [Hash] Optional options. Can contain the following keys:
# :strict_deps:: # :strict_deps::
# Only consider a target up to date if its list of dependencies is # Only consider a target up to date if its list of dependencies is
# exactly equal (including order) to the cached list of dependencies # exactly equal (including order) to the cached list of dependencies
# @return true value if the target is up to date, meaning that: # @return true if the targets are all up to date, meaning that,
# for each target:
# - the target exists on disk # - the target exists on disk
# - the cache has information for the target # - the cache has information for the target
# - the target's checksum matches its checksum when it was last built # - the target's checksum matches its checksum when it was last built
@ -104,65 +105,69 @@ module Rscons
# exactly equal to those cached # exactly equal to those cached
# - each cached dependency file's current checksum matches the checksum # - each cached dependency file's current checksum matches the checksum
# stored in the cache file # stored in the cache file
def up_to_date?(target, command, deps, env, options = {}) def up_to_date?(targets, command, deps, env, options = {})
# target file must exist on disk Array(targets).each do |target|
return false unless File.exists?(target) # target file must exist on disk
return false unless File.exists?(target)
# target must be registered in the cache # target must be registered in the cache
return false unless @cache[:targets].has_key?(target) return false unless @cache[:targets].has_key?(target)
# target must have the same checksum as when it was built last # target must have the same checksum as when it was built last
return false unless @cache[:targets][target][:checksum] == lookup_checksum(target) return false unless @cache[:targets][target][:checksum] == lookup_checksum(target)
# command used to build target must be identical # command used to build target must be identical
return false unless @cache[:targets][target][:command] == command return false unless @cache[:targets][target][:command] == command
cached_deps = @cache[:targets][target][:deps] || [] cached_deps = @cache[:targets][target][:deps] || []
cached_deps_fnames = cached_deps.map { |dc| dc[:fname] } cached_deps_fnames = cached_deps.map { |dc| dc[:fname] }
if options[:strict_deps] if options[:strict_deps]
# depedencies passed in must exactly equal those in the cache # depedencies passed in must exactly equal those in the cache
return false unless deps == cached_deps_fnames return false unless deps == cached_deps_fnames
else else
# all dependencies passed in must exist in cache (but cache may have more) # all dependencies passed in must exist in cache (but cache may have more)
return false unless (Set.new(deps) - Set.new(cached_deps_fnames)).empty? return false unless (Set.new(deps) - Set.new(cached_deps_fnames)).empty?
end end
# set of user dependencies must match # set of user dependencies must match
user_deps = env.get_user_deps(target) || [] user_deps = env.get_user_deps(target) || []
cached_user_deps = @cache[:targets][target][:user_deps] || [] cached_user_deps = @cache[:targets][target][:user_deps] || []
cached_user_deps_fnames = cached_user_deps.map { |dc| dc[:fname] } cached_user_deps_fnames = cached_user_deps.map { |dc| dc[:fname] }
return false unless user_deps == cached_user_deps_fnames return false unless user_deps == cached_user_deps_fnames
# all cached dependencies must have their checksums match # all cached dependencies must have their checksums match
(cached_deps + cached_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]) return false unless dep_cache[:checksum] == lookup_checksum(dep_cache[:fname])
end
end end
true true
end end
# Store cache information about a target built by a builder # Store cache information about target(s) built by a builder
# @param target [String] The name of the target. # @param targets [String, Array] The name of the target(s) built.
# @param command [Array] The command used to build the target. # @param command [String, Array] The command used to build the target.
# @param deps [Array] List of dependencies for the target. # @param deps [Array] List of dependencies for the target.
# @param env [Environment] The Rscons::Environment. # @param env [Environment] The Rscons::Environment.
def register_build(target, command, deps, env) def register_build(targets, command, deps, env)
@cache[:targets][target.encode(__ENCODING__)] = { Array(targets).each do |target|
command: command, @cache[:targets][target.encode(__ENCODING__)] = {
checksum: calculate_checksum(target), command: command,
deps: deps.map do |dep| checksum: calculate_checksum(target),
{ deps: deps.map do |dep|
fname: dep.encode(__ENCODING__), {
checksum: lookup_checksum(dep), fname: dep.encode(__ENCODING__),
} checksum: lookup_checksum(dep),
end, }
user_deps: (env.get_user_deps(target) || []).map do |dep| end,
{ user_deps: (env.get_user_deps(target) || []).map do |dep|
fname: dep.encode(__ENCODING__), {
checksum: lookup_checksum(dep), fname: dep.encode(__ENCODING__),
} checksum: lookup_checksum(dep),
end, }
} end,
}
end
end end
# Return a list of targets that have been built # Return a list of targets that have been built

View File

@ -218,9 +218,9 @@ describe Rscons do
end end
it 'allows Ruby classes as custom builders to be used to construct files' do it 'allows Ruby classes as custom builders to be used to construct files' do
test_dir('custom_builder') test_dir('custom_builder')
class MySource < Rscons::Builder class MySource < Rscons::Builder
def run(target, sources, user_deps, cache, env, vars = {}) def run(target, sources, cache, env, vars)
File.open(target, 'w') do |fh| File.open(target, 'w') do |fh|
fh.puts <<EOF fh.puts <<EOF
#define THE_VALUE 5678 #define THE_VALUE 5678
@ -241,6 +241,39 @@ EOF
`./program`.should == "The value is 5678\n" `./program`.should == "The value is 5678\n"
end end
it 'supports custom builders with multiple targets' do
test_dir('custom_builder')
class CHGen < Rscons::Builder
def run(target, sources, cache, env, vars)
c_fname = target
h_fname = target.sub(/\.c$/, ".h")
unless cache.up_to_date?([c_fname, h_fname], "", sources, env)
puts "CHGen #{c_fname}"
File.open(c_fname, "w") {|fh| fh.puts "int THE_VALUE = 42;"}
File.open(h_fname, "w") {|fh| fh.puts "extern int THE_VALUE;"}
cache.register_build([c_fname, h_fname], "", sources, env)
end
target
end
end
env = Rscons::Environment.new do |env|
env.add_builder(CHGen.new)
env.CHGen("inc.c", ["program.c"])
env.Program("program", Dir["*.c"] + ["inc.c"])
end
lines.should == ["CHGen inc.c", "CC program.o", "CC inc.o", "LD program"]
File.exists?("inc.c").should be_true
File.exists?("inc.h").should be_true
`./program`.should == "The value is 42\n"
File.open("inc.c", "w") {|fh| fh.puts "int THE_VALUE = 33;"}
env.process
lines.should == ["CHGen inc.c"]
`./program`.should == "The value is 42\n"
end
it 'allows cloning Environment objects' do it 'allows cloning Environment objects' do
test_dir('clone_env') test_dir('clone_env')