Compare commits

..
21 Commits
Author SHA1 Message Date
josh 7c89dddbd6 v0.3.1 2014-01-23 14:40:45 -05:00
josh c07d9f86a6 bugfix: loading a Cache without deps or user_deps stored causes an exception 2014-01-23 14:40:30 -05:00
josh ff26da8865 fix custom builder examples in README.md 2014-01-15 16:57:46 -05:00
josh 5ff555cf6a add Environment#expand_varref() 2014-01-02 11:49:08 -05:00
josh e3aadc562f Rscons::Varset: remove unused type parameter to #[] 2014-01-02 11:05:15 -05:00
josh 7ff2ec1f1a v0.3.0 2013-12-31 14:10:47 -05:00
josh 5362f761e6 move default builder classes into Rscons::Builders namespace module 2013-12-31 13:31:17 -05:00
josh cdb3352b4e remove module monkey-patching; add Builder#name() 2013-12-31 13:10:00 -05:00
josh 08bc390d40 Rscons::BuildError inherits from RuntimeError instead of Exception 2013-12-31 13:08:38 -05:00
josh f8af0630b0 pass env to Cache#up_to_date?() and #register_build() instead of user_deps 2013-12-31 12:46:30 -05:00
josh 1868193b54 do not prepend build_root to source files that are already inside build_root 2013-12-31 10:15:19 -05:00
josh 8976d7dea2 update README custom builder examples 2013-12-27 15:01:02 -05:00
josh 295324eafd add Environment#depends() to specify user dependencies for a build target 2013-12-27 14:31:46 -05:00
josh 7330e74ab8 modify Cache to store user-specified extra dependencies 2013-12-27 14:20:32 -05:00
josh a28cef9eb7 change "vars" parameters to be required 2013-12-27 13:53:40 -05:00
josh e667046da8 add Environment#clear_targets() 2013-12-27 13:35:16 -05:00
josh 539a49fe90 do not allow construction variables as arguments to Environment#initialize() or Environment#clone() 2013-12-27 12:10:54 -05:00
josh 7681c7584f add String#absolute_path? 2013-12-27 11:40:24 -05:00
josh 8fcbd39057 v0.2.2 2013-11-13 23:52:33 -05:00
josh d0bbc78751 Program: do not continue executing builder if building a source fails 2013-11-13 23:52:15 -05:00
josh 7249e57951 add info to error message when expanding unknown variable types 2013-11-13 23:50:46 -05:00
17 changed files with 463 additions and 246 deletions

No files matched your search

+35
View File
@@ -67,6 +67,7 @@ end
```ruby ```ruby
class GenerateFoo < Rscons::Builder class GenerateFoo < Rscons::Builder
def run(target, sources, cache, env, vars) def run(target, sources, cache, env, vars)
cache.mkdir_p(File.dirname(target))
File.open(target, "w") do |fh| File.open(target, "w") do |fh|
fh.puts <<EOF fh.puts <<EOF
#define GENERATED 42 #define GENERATED 42
@@ -81,6 +82,40 @@ Rscons::Environment.new do |env|
end end
``` ```
### Example: Custom Builder That Only Regenerates When Necessary
```ruby
class CmdBuilder < Rscons::Builder
def run(target, sources, cache, env, vars)
cmd = ["cmd", "-i", sources.first, "-o", target]
unless cache.up_to_date?(target, cmd, sources, env)
cache.mkdir_p(File.dirname(target))
system(cmd)
cache.register_build(target, cmd, sources, env)
end
end
end
Rscons::Environment.new do |env|
env.CmdBuilder("foo.gen", "foo_gen.cfg")
end
```
### Example: Custom Builder Using Builder#standard_build()
```ruby
class CmdBuilder < Rscons::Builder
def run(target, sources, cache, env, vars)
cmd = ["cmd", "-i", sources.first, "-o", target]
standard_build("CmdBld #{target}", target, cmd, sources, env, cache)
end
end
Rscons::Environment.new do |env|
env.CmdBuilder("foo.gen", "foo_gen.cfg")
end
```
### Example: Using different compilation flags for some sources ### Example: Using different compilation flags for some sources
```ruby ```ruby
+4 -6
View File
@@ -4,7 +4,6 @@ require "rscons/environment"
require "rscons/varset" require "rscons/varset"
require "rscons/version" require "rscons/version"
require "rscons/monkey/module"
require "rscons/monkey/string" require "rscons/monkey/string"
# default builders # default builders
@@ -15,13 +14,12 @@ require "rscons/builders/program"
# Namespace module for rscons classes # Namespace module for rscons classes
module Rscons module Rscons
DEFAULT_BUILDERS = [ DEFAULT_BUILDERS = [
Library, :Library,
Object, :Object,
Program, :Program,
] ]
class BuildError < Exception class BuildError < RuntimeError; end
end
# Remove all generated files # Remove all generated files
def self.clean def self.clean
+11 -2
View File
@@ -1,8 +1,17 @@
require "fileutils" require "fileutils"
module Rscons module Rscons
# Namespace module in which to store builders for convenient grouping
module Builders; end
# Class to hold an object that knows how to build a certain type of file. # Class to hold an object that knows how to build a certain type of file.
class Builder class Builder
# Return the name of the builder.
# If not overridden this defaults to the last component of the class name.
def name
self.class.name.split(":").last
end
# Return a set of default variable values for the Environment to use # Return a set of default variable values for the Environment to use
# unless the user overrides any. # unless the user overrides any.
# @param env [Environment] The Environment. # @param env [Environment] The Environment.
@@ -23,11 +32,11 @@ module Rscons
# build command. # build command.
# Return the name of the target or false on failure. # Return the name of the target or false on failure.
def standard_build(short_cmd_string, target, command, sources, env, cache) def standard_build(short_cmd_string, target, command, sources, env, cache)
unless cache.up_to_date?(target, command, sources) unless cache.up_to_date?(target, command, sources, env)
cache.mkdir_p(File.dirname(target)) cache.mkdir_p(File.dirname(target))
FileUtils.rm_f(target) FileUtils.rm_f(target)
return false unless env.execute(short_cmd_string, command) return false unless env.execute(short_cmd_string, command)
cache.register_build(target, command, sources) cache.register_build(target, command, sources, env)
end end
target target
end end
+22 -22
View File
@@ -1,27 +1,27 @@
require 'fileutils'
module Rscons module Rscons
# A default RScons builder that produces a static library archive. module Builders
class Library < Builder # A default RScons builder that produces a static library archive.
def default_variables(env) class Rscons::Builders::Library < Rscons::Builder
{ def default_variables(env)
'AR' => 'ar', {
'LIBSUFFIX' => '.a', 'AR' => 'ar',
'ARFLAGS' => [], 'LIBSUFFIX' => '.a',
'ARCMD' => ['${AR}', 'rcs', '${ARFLAGS}', '${_TARGET}', '${_SOURCES}'] 'ARFLAGS' => [],
} 'ARCMD' => ['${AR}', 'rcs', '${ARFLAGS}', '${_TARGET}', '${_SOURCES}']
end }
end
def run(target, sources, cache, env, vars = {}) def run(target, sources, cache, env, vars)
# build sources to linkable objects # build sources to linkable objects
objects = env.build_sources(sources, [env['OBJSUFFIX'], env['LIBSUFFIX']].flatten, cache, vars) objects = env.build_sources(sources, [env['OBJSUFFIX'], env['LIBSUFFIX']].flatten, cache, vars)
if objects if objects
vars = vars.merge({ vars = vars.merge({
'_TARGET' => target, '_TARGET' => target,
'_SOURCES' => objects, '_SOURCES' => objects,
}) })
command = env.build_command(env['ARCMD'], vars) command = env.build_command(env['ARCMD'], vars)
standard_build("AR #{target}", target, command, objects, env, cache) standard_build("AR #{target}", target, command, objects, env, cache)
end
end end
end end
end end
+71 -69
View File
@@ -1,79 +1,81 @@
module Rscons module Rscons
# A default RScons builder which knows how to produce an object file from module Builders
# various types of source files. # A default RScons builder which knows how to produce an object file from
class Object < Builder # various types of source files.
KNOWN_SUFFIXES = { class Object < Builder
"AS" => "ASSUFFIX", KNOWN_SUFFIXES = {
"CC" => "CSUFFIX", "AS" => "ASSUFFIX",
"CXX" => "CXXSUFFIX", "CC" => "CSUFFIX",
"DC" => "DSUFFIX", "CXX" => "CXXSUFFIX",
} "DC" => "DSUFFIX",
def default_variables(env)
{
'OBJSUFFIX' => '.o',
'AS' => '${CC}',
'ASFLAGS' => [],
'ASSUFFIX' => '.S',
'ASPPPATH' => '${CPPPATH}',
'ASPPFLAGS' => '${CPPFLAGS}',
'ASDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'],
'ASCMD' => ['${AS}', '-c', '-o', '${_TARGET}', '${ASDEPGEN}', '-I${ASPPPATH}', '${ASPPFLAGS}', '${ASFLAGS}', '${_SOURCES}'],
'CPPFLAGS' => [],
'CPPPATH' => [],
'CC' => 'gcc',
'CFLAGS' => [],
'CSUFFIX' => '.c',
'CCDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'],
'CCCMD' => ['${CC}', '-c', '-o', '${_TARGET}', '${CCDEPGEN}', '-I${CPPPATH}', '${CPPFLAGS}', '${CFLAGS}', '${_SOURCES}'],
'CXX' => 'g++',
'CXXFLAGS' => [],
'CXXSUFFIX' => '.cc',
'CXXDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'],
'CXXCMD' =>['${CXX}', '-c', '-o', '${_TARGET}', '${CXXDEPGEN}', '-I${CPPPATH}', '${CPPFLAGS}', '${CXXFLAGS}', '${_SOURCES}'],
'DC' => 'gdc',
'DFLAGS' => [],
'DSUFFIX' => '.d',
'D_IMPORT_PATH' => [],
'DCCMD' => ['${DC}', '-c', '-o', '${_TARGET}', '-I${D_IMPORT_PATH}', '${DFLAGS}', '${_SOURCES}'],
} }
end
def produces?(target, source, env) def default_variables(env)
target.has_suffix?(env['OBJSUFFIX']) and KNOWN_SUFFIXES.find do |compiler, suffix_var| {
source.has_suffix?(env[suffix_var]) 'OBJSUFFIX' => '.o',
'AS' => '${CC}',
'ASFLAGS' => [],
'ASSUFFIX' => '.S',
'ASPPPATH' => '${CPPPATH}',
'ASPPFLAGS' => '${CPPFLAGS}',
'ASDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'],
'ASCMD' => ['${AS}', '-c', '-o', '${_TARGET}', '${ASDEPGEN}', '-I${ASPPPATH}', '${ASPPFLAGS}', '${ASFLAGS}', '${_SOURCES}'],
'CPPFLAGS' => [],
'CPPPATH' => [],
'CC' => 'gcc',
'CFLAGS' => [],
'CSUFFIX' => '.c',
'CCDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'],
'CCCMD' => ['${CC}', '-c', '-o', '${_TARGET}', '${CCDEPGEN}', '-I${CPPPATH}', '${CPPFLAGS}', '${CFLAGS}', '${_SOURCES}'],
'CXX' => 'g++',
'CXXFLAGS' => [],
'CXXSUFFIX' => '.cc',
'CXXDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'],
'CXXCMD' =>['${CXX}', '-c', '-o', '${_TARGET}', '${CXXDEPGEN}', '-I${CPPPATH}', '${CPPFLAGS}', '${CXXFLAGS}', '${_SOURCES}'],
'DC' => 'gdc',
'DFLAGS' => [],
'DSUFFIX' => '.d',
'D_IMPORT_PATH' => [],
'DCCMD' => ['${DC}', '-c', '-o', '${_TARGET}', '-I${D_IMPORT_PATH}', '${DFLAGS}', '${_SOURCES}'],
}
end end
end
def run(target, sources, cache, env, vars = {}) def produces?(target, source, env)
vars = vars.merge({ target.has_suffix?(env['OBJSUFFIX']) and KNOWN_SUFFIXES.find do |compiler, suffix_var|
'_TARGET' => target, source.has_suffix?(env[suffix_var])
'_SOURCES' => sources,
'_DEPFILE' => target.set_suffix('.mf'),
})
com_prefix = KNOWN_SUFFIXES.find do |compiler, suffix_var|
sources.first.has_suffix?(env[suffix_var])
end.tap do |v|
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)
cache.mkdir_p(File.dirname(target))
FileUtils.rm_f(target)
return false unless env.execute("#{com_prefix} #{target}", command)
deps = sources
if File.exists?(vars['_DEPFILE'])
deps += Environment.parse_makefile_deps(vars['_DEPFILE'], target)
FileUtils.rm_f(vars['_DEPFILE'])
end end
cache.register_build(target, command, deps.uniq)
end end
target
def run(target, sources, cache, env, vars)
vars = vars.merge({
'_TARGET' => target,
'_SOURCES' => sources,
'_DEPFILE' => target.set_suffix('.mf'),
})
com_prefix = KNOWN_SUFFIXES.find do |compiler, suffix_var|
sources.first.has_suffix?(env[suffix_var])
end.tap do |v|
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, env)
cache.mkdir_p(File.dirname(target))
FileUtils.rm_f(target)
return false unless env.execute("#{com_prefix} #{target}", command)
deps = sources
if File.exists?(vars['_DEPFILE'])
deps += Environment.parse_makefile_deps(vars['_DEPFILE'], target)
FileUtils.rm_f(vars['_DEPFILE'])
end
cache.register_build(target, command, deps.uniq, env)
end
target
end
end end
end end
end end
+36 -33
View File
@@ -1,38 +1,41 @@
module Rscons module Rscons
# A default RScons builder that knows how to link object files into an module Builders
# executable program. # A default RScons builder that knows how to link object files into an
class Program < Builder # executable program.
def default_variables(env) class Rscons::Builders::Program < Rscons::Builder
{ def default_variables(env)
'LD' => nil, {
'OBJSUFFIX' => '.o', 'LD' => nil,
'LIBSUFFIX' => '.a', 'OBJSUFFIX' => '.o',
'LDFLAGS' => [], 'LIBSUFFIX' => '.a',
'LIBPATH' => [], 'LDFLAGS' => [],
'LIBS' => [], 'LIBPATH' => [],
'LDCMD' => ['${LD}', '-o', '${_TARGET}', '${LDFLAGS}', '${_SOURCES}', '-L${LIBPATH}', '-l${LIBS}'] 'LIBS' => [],
} 'LDCMD' => ['${LD}', '-o', '${_TARGET}', '${LDFLAGS}', '${_SOURCES}', '-L${LIBPATH}', '-l${LIBS}']
end }
end
def run(target, sources, cache, env, vars = {}) def run(target, sources, cache, env, vars)
# build sources to linkable objects # build sources to linkable objects
objects = env.build_sources(sources, [env['OBJSUFFIX'], env['LIBSUFFIX']].flatten, cache, vars) objects = env.build_sources(sources, [env['OBJSUFFIX'], env['LIBSUFFIX']].flatten, cache, vars)
ld = if env["LD"] return false unless objects
env["LD"] ld = if env["LD"]
elsif sources.find {|s| s.has_suffix?(env["DSUFFIX"])} env["LD"]
env["DC"] elsif sources.find {|s| s.has_suffix?(env["DSUFFIX"])}
elsif sources.find {|s| s.has_suffix?(env["CXXSUFFIX"])} env["DC"]
env["CXX"] elsif sources.find {|s| s.has_suffix?(env["CXXSUFFIX"])}
else env["CXX"]
env["CC"] else
end env["CC"]
vars = vars.merge({ end
'_TARGET' => target, vars = vars.merge({
'_SOURCES' => objects, '_TARGET' => target,
'LD' => ld, '_SOURCES' => objects,
}) 'LD' => ld,
command = env.build_command(env['LDCMD'], vars) })
standard_build("LD #{target}", target, command, objects, env, cache) command = env.build_command(env['LDCMD'], vars)
standard_build("LD #{target}", target, command, objects, env, cache)
end
end end
end end
end end
+35 -13
View File
@@ -18,9 +18,15 @@ module Rscons
# { # {
# 'fname' => 'program.o', # 'fname' => 'program.o',
# 'checksum' => '87654321', # 'checksum' => '87654321',
# } # },
# ], # ],
# } # 'user_deps' => [
# {
# 'fname' => 'lscript.ld',
# 'checksum' => '77551133',
# },
# ],
# },
# 'program.o' => { # 'program.o' => {
# 'checksum' => '87654321', # 'checksum' => '87654321',
# 'command' => ['gcc', '-c', '-o', 'program.o', 'program.c'], # 'command' => ['gcc', '-c', '-o', 'program.o', 'program.c'],
@@ -32,15 +38,16 @@ module Rscons
# { # {
# 'fname' => 'program.h', # 'fname' => 'program.h',
# 'checksum' => '7979764643', # 'checksum' => '7979764643',
# } # },
# ] # ],
# 'user_deps' => [],
# } # }
# } # },
# directories: { # directories: {
# 'build' => true, # 'build' => true,
# 'build/one' => true, # 'build/one' => true,
# 'build/two' => true, # 'build/two' => true,
# } # },
# } # }
class Cache class Cache
#### Constants #### Constants
@@ -82,6 +89,7 @@ module Rscons
# @param target [String] The name of the target file. # @param target [String] The name of the target file.
# @param command [Array] The command used to build the target. # @param command [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 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
@@ -96,7 +104,7 @@ 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, options = {}) def up_to_date?(target, command, deps, env, options = {})
# target file must exist on disk # target file must exist on disk
return false unless File.exists?(target) return false unless File.exists?(target)
@@ -109,17 +117,24 @@ module Rscons
# 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].map { |dc| dc[:fname] } cached_deps = @cache[:targets][target][:deps] || []
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 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)).empty? return false unless (Set.new(deps) - Set.new(cached_deps_fnames)).empty?
end end
# set of user dependencies must match
user_deps = env.get_user_deps(target) || []
cached_user_deps = @cache[:targets][target][:user_deps] || []
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 # all cached dependencies must have their checksums match
@cache[:targets][target][: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
@@ -130,7 +145,8 @@ module Rscons
# @param target [String] The name of the target. # @param target [String] The name of the target.
# @param command [Array] The command used to build the target. # @param command [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.
def register_build(target, command, deps) # @param env [Environment] The Rscons::Environment.
def register_build(target, command, deps, env)
@cache[:targets][target.encode(__ENCODING__)] = { @cache[:targets][target.encode(__ENCODING__)] = {
command: command, command: command,
checksum: calculate_checksum(target), checksum: calculate_checksum(target),
@@ -139,7 +155,13 @@ module Rscons
fname: dep.encode(__ENCODING__), fname: dep.encode(__ENCODING__),
checksum: lookup_checksum(dep), checksum: lookup_checksum(dep),
} }
end end,
user_deps: (env.get_user_deps(target) || []).map do |dep|
{
fname: dep.encode(__ENCODING__),
checksum: lookup_checksum(dep),
}
end,
} }
end end
+67 -32
View File
@@ -9,33 +9,39 @@ module Rscons
# Hash of +{"builder_name" => builder_object}+ pairs. # Hash of +{"builder_name" => builder_object}+ pairs.
attr_reader :builders attr_reader :builders
# :command, :short, or :off
attr_accessor :echo
# String or +nil+ # String or +nil+
attr_accessor :build_root attr_reader :build_root
def build_root=(build_root)
@build_root = build_root.gsub('\\', '/')
end
# Create an Environment object. # Create an Environment object.
# @param variables [Hash] # @param options [Hash]
# The variables hash can contain construction variables, which are # Possible options keys:
# uppercase strings (such as "CC" or "LDFLAGS"), user variables, which # :echo => :command, :short, or :off (default :short)
# are lowercase strings (such as "sources"), and RScons options, which # :build_root => String specifying build root directory (default nil)
# are lowercase symbols (such as :echo). # :exclude_builders => true to omit adding default builders (default false)
# If a block is given, the Environment object is yielded to the block and # If a block is given, the Environment object is yielded to the block and
# when the block returns, the {#process} method is automatically called. # when the block returns, the {#process} method is automatically called.
def initialize(variables = {}) def initialize(options = {})
@varset = VarSet.new(variables) @varset = VarSet.new
@targets = {} @targets = {}
@user_deps = {}
@builders = {} @builders = {}
@build_dirs = [] @build_dirs = []
@build_hooks = [] @build_hooks = []
@varset[:exclude_builders] ||= [] unless options[:exclude_builders]
unless @varset[:exclude_builders] == :all DEFAULT_BUILDERS.each do |builder_class_name|
exclude_builders = Set.new(@varset[:exclude_builders] || []) builder_class = Builders.const_get(builder_class_name)
DEFAULT_BUILDERS.each do |builder_class| builder_class or raise "Could not find builder class #{builder_class_name}"
unless exclude_builders.include?(builder_class.short_name) add_builder(builder_class.new)
add_builder(builder_class.new)
end
end end
end end
@varset[:echo] ||= :short @echo = options[:echo] || :short
@build_root = options[:build_root]
if block_given? if block_given?
yield self yield self
@@ -45,17 +51,23 @@ module Rscons
# Make a copy of the Environment object. # Make a copy of the Environment object.
# The cloned environment will contain a copy of all environment options, # The cloned environment will contain a copy of all environment options,
# construction variables, and builders. It will not contain a copy of the # construction variables, and builders (unless :exclude_builders => true is
# targets, build hooks, build directories, or the build root. If a block # passed as an option). It will not contain a copy of the targets, build
# is given, the Environment object is yielded to the block and when the # hooks, build directories, or the build root. If a block is given, the
# block returns, the {#process} method is automatically called. # Environment object is yielded to the block and when the block returns,
def clone(variables = {}) # the {#process} method is automatically called. The possible options keys
env = self.class.new # match those documented in the #initialize method.
@builders.each do |builder_name, builder| def clone(options = {})
env.add_builder(builder) env = self.class.new(
echo: options[:echo] || @echo,
build_root: options[:build_root],
exclude_builders: true)
unless options[:exclude_builders]
@builders.each do |builder_name, builder|
env.add_builder(builder)
end
end end
env.append(@varset.clone) env.append(@varset.clone)
env.append(variables)
if block_given? if block_given?
yield env yield env
@@ -66,7 +78,7 @@ module Rscons
# Add a {Builder} object to the Environment. # Add a {Builder} object to the Environment.
def add_builder(builder) def add_builder(builder)
@builders[builder.class.short_name] = builder @builders[builder.name] = builder
var_defs = builder.default_variables(self) var_defs = builder.default_variables(self)
if var_defs if var_defs
var_defs.each_pair do |var, val| var_defs.each_pair do |var, val|
@@ -99,7 +111,7 @@ module Rscons
end end
end end
if @build_root and not found_match if @build_root and not found_match
unless source_fname.start_with?('/') or source_fname =~ %r{^\w:[\\/]} unless source_fname.absolute_path? or build_fname.start_with?("#{@build_root}/")
build_fname = "#{@build_root}/#{build_fname}" build_fname = "#{@build_root}/#{build_fname}"
end end
end end
@@ -156,6 +168,11 @@ module Rscons
cache.write cache.write
end end
# Clear all targets registered for the Environment.
def clear_targets
@targets = {}
end
# Build a command line from the given template, resolving references to # Build a command line from the given template, resolving references to
# variables using the Environment's construction variables and any extra # variables using the Environment's construction variables and any extra
# variables specified. # variables specified.
@@ -168,8 +185,13 @@ module Rscons
@varset.merge(extra_vars).expand_varref(command_template) @varset.merge(extra_vars).expand_varref(command_template)
end end
# Expand a construction variable reference (String or Array)
def expand_varref(varref)
@varset.expand_varref(varref)
end
# Execute a builder command # Execute a builder command
# @param short_desc [String] Message to print if the Environment's :echo # @param short_desc [String] Message to print if the Environment's echo
# mode is set to :short # mode is set to :short
# @param command [Array] The command to execute. # @param command [Array] The command to execute.
# @param options [Hash] Optional options to pass to Kernel#system. # @param options [Hash] Optional options to pass to Kernel#system.
@@ -177,13 +199,13 @@ module Rscons
print_command = proc do print_command = proc do
puts command.map { |c| c =~ /\s/ ? "'#{c}'" : c }.join(' ') puts command.map { |c| c =~ /\s/ ? "'#{c}'" : c }.join(' ')
end end
if @varset[:echo] == :command if @echo == :command
print_command.call print_command.call
elsif @varset[:echo] == :short elsif @echo == :short
puts short_desc puts short_desc
end end
system(*command, options).tap do |result| system(*command, options).tap do |result|
unless result or @varset[:echo] == :command unless result or @echo == :command
$stdout.write "Failed command was: " $stdout.write "Failed command was: "
print_command.call print_command.call
end end
@@ -209,6 +231,19 @@ module Rscons
end end
end end
# Manually record a given target as depending on the specified
# dependency files.
def depends(target, *user_deps)
@user_deps[target] ||= []
@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 # Build a list of source files into files containing one of the suffixes
# given by suffixes. # given by suffixes.
# This method is used internally by RScons builders. # This method is used internally by RScons builders.
@@ -217,7 +252,7 @@ module Rscons
# @param cache [Cache] The Cache. # @param cache [Cache] The Cache.
# @param vars [Hash] Extra variables to pass to the builder. # @param vars [Hash] Extra variables to pass to the builder.
# Return a list of the converted file names. # Return a list of the converted file names.
def build_sources(sources, suffixes, cache, vars = {}) def build_sources(sources, suffixes, cache, vars)
sources.map do |source| sources.map do |source|
if source.has_suffix?(suffixes) if source.has_suffix?(suffixes)
source source
-7
View File
@@ -1,7 +0,0 @@
# Standard Ruby Module class.
class Module
# @return the base module name (not the fully qualified name)
def short_name
name.split(':').last
end
end
+5
View File
@@ -17,4 +17,9 @@ class String
def set_suffix(suffix = '') def set_suffix(suffix = '')
sub(/\.[^.]*$/, suffix) sub(/\.[^.]*$/, suffix)
end end
# Return whether the string represents an absolute filesystem path
def absolute_path?
self =~ %r{^(/|\w:[\\/])}
end
end end
+3 -15
View File
@@ -17,20 +17,8 @@ module Rscons
# Access the value of variable as a particular type # Access the value of variable as a particular type
# @param key [String, Symbol] The variable name. # @param key [String, Symbol] The variable name.
# @param type [Symbol, nil] Optional specification of the type desired. def [](key)
# If the variable is a String and type is :array, a 1-element array with @vars[key]
# the variable value will be returned. If the variable is an Array and
# type is :string, the first element from the variable value will be
# returned.
def [](key, type = nil)
val = @vars[key]
if type == :array and not val.is_a?(Array)
[val]
elsif type == :scalar and val.is_a?(Array)
val.first
else
val
end
end end
# Assign a value to a variable. # Assign a value to a variable.
@@ -72,7 +60,7 @@ module Rscons
elsif varval.is_a?(Array) elsif varval.is_a?(Array)
varval.map {|vv| expand_varref("#{prefix}#{vv}#{suffix}")}.flatten varval.map {|vv| expand_varref("#{prefix}#{vv}#{suffix}")}.flatten
else else
raise "I do not know how to expand a variable reference to a #{varval.class.name}" raise "I do not know how to expand a variable reference to a #{varval.class.name} (from #{varname.inspect} => #{@vars[varname].inspect})"
end end
else else
varref varref
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons module Rscons
# gem version # gem version
VERSION = "0.2.1" VERSION = "0.3.1"
end end
+34 -2
View File
@@ -220,7 +220,7 @@ describe Rscons do
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, cache, env, vars = {}) def run(target, sources, user_deps, 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
@@ -251,7 +251,8 @@ EOF
env.Program('program-debug', Dir['src/*.c']) env.Program('program-debug', Dir['src/*.c'])
end end
release = debug.clone('CPPFLAGS' => '-DSTRING="Release Version"') do |env| release = debug.clone do |env|
env["CPPFLAGS"] = '-DSTRING="Release Version"'
env.build_dir('src', 'release') env.build_dir('src', 'release')
env.Program('program-release', Dir['src/*.c']) env.Program('program-release', Dir['src/*.c'])
end end
@@ -327,6 +328,37 @@ EOF
] ]
end end
it 'rebuilds when user-specified dependencies change' do
test_dir('simple')
Rscons::Environment.new do |env|
env.Program('simple', Dir['*.c'])
File.open("file.ld", "w") do |fh|
fh.puts("foo")
end
env.depends('simple', 'file.ld')
end
lines.should == ["CC simple.o", "LD simple"]
File.exists?('simple.o').should be_true
`./simple`.should == "This is a simple C program\n"
Rscons::Environment.new do |env|
env.Program('simple', Dir['*.c'])
File.open("file.ld", "w") do |fh|
fh.puts("bar")
end
env.depends('simple', 'file.ld')
end
lines.should == ["LD simple"]
Rscons::Environment.new do |env|
env.Program('simple', Dir['*.c'])
File.unlink("file.ld")
end
lines.should == ["LD simple"]
Rscons::Environment.new do |env|
env.Program('simple', Dir['*.c'])
end
lines.should == []
end
unless ENV["omit_gdc_tests"] unless ENV["omit_gdc_tests"]
it "supports building D sources" do it "supports building D sources" do
test_dir("d") test_dir("d")
+62 -12
View File
@@ -38,14 +38,19 @@ module Rscons
end end
describe "#up_to_date?" do 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 it "returns false when target file does not exist" do
File.should_receive(:exists?).with("target").and_return(false) 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 end
it "returns false when target is not registered in the cache" do it "returns false when target is not registered in the cache" do
File.should_receive(:exists?).with("target").and_return(true) 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 end
it "returns false when the target's checksum does not match" do it "returns false when the target's checksum does not match" do
@@ -53,7 +58,7 @@ module Rscons
cache = build_from(_cache) cache = build_from(_cache)
File.should_receive(:exists?).with("target").and_return(true) File.should_receive(:exists?).with("target").and_return(true)
cache.should_receive(:calculate_checksum).with("target").and_return("def") 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 end
it "returns false when the build command has changed" do it "returns false when the build command has changed" do
@@ -61,7 +66,7 @@ module Rscons
cache = build_from(_cache) cache = build_from(_cache)
File.should_receive(:exists?).with("target").and_return(true) 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("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 end
it "returns false when there is a new dependency" do it "returns false when there is a new dependency" do
@@ -71,7 +76,7 @@ module Rscons
cache = build_from(_cache) cache = build_from(_cache)
File.should_receive(:exists?).with("target").and_return(true) 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("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 end
it "returns false when a dependency's checksum has changed" do it "returns false when a dependency's checksum has changed" do
@@ -82,13 +87,14 @@ module Rscons
{fname: "dep.2", {fname: "dep.2",
checksum: "dep.2.chk"}, checksum: "dep.2.chk"},
{fname: "extra.dep", {fname: "extra.dep",
checksum: "extra.dep.chk"}]}}} checksum: "extra.dep.chk"}],
user_deps: []}}}
cache = build_from(_cache) cache = build_from(_cache)
File.should_receive(:exists?).with("target").and_return(true) 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("target").and_return("abc")
cache.should_receive(:calculate_checksum).with("dep.1").and_return("dep.1.chk") 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.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 end
it "returns false with strict_deps=true when cache has an extra dependency" do it "returns false with strict_deps=true when cache has an extra dependency" do
@@ -99,11 +105,48 @@ module Rscons
{fname: "dep.2", {fname: "dep.2",
checksum: "dep.2.chk"}, checksum: "dep.2.chk"},
{fname: "extra.dep", {fname: "extra.dep",
checksum: "extra.dep.chk"}]}}} checksum: "extra.dep.chk"}],
user_deps: []}}}
cache = build_from(_cache) cache = build_from(_cache)
File.should_receive(:exists?).with("target").and_return(true) 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("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
_cache = {targets: {"target" => {checksum: "abc",
command: "command",
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"], env).should be_false
end
it "returns false when a user dependency checksum has changed" do
_cache = {targets: {"target" => {checksum: "abc",
command: "command",
deps: [{fname: "dep.1",
checksum: "dep.1.chk"},
{fname: "dep.2",
checksum: "dep.2.chk"},
{fname: "extra.dep",
checksum: "extra.dep.chk"}],
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"], env).should be_false
end end
it "returns true when no condition for false is met" do it "returns true when no condition for false is met" do
@@ -114,14 +157,15 @@ module Rscons
{fname: "dep.2", {fname: "dep.2",
checksum: "dep.2.chk"}, checksum: "dep.2.chk"},
{fname: "extra.dep", {fname: "extra.dep",
checksum: "extra.dep.chk"}]}}} checksum: "extra.dep.chk"}],
user_deps: []}}}
cache = build_from(_cache) cache = build_from(_cache)
File.should_receive(:exists?).with("target").and_return(true) 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("target").and_return("abc")
cache.should_receive(:calculate_checksum).with("dep.1").and_return("dep.1.chk") 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("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("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
end end
@@ -129,10 +173,13 @@ module Rscons
it "stores the given information in the cache" do it "stores the given information in the cache" do
_cache = {} _cache = {}
cache = build_from(_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("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 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("dep 2").and_return("dep 2 checksum")
cache.register_build("the target", "the command", ["dep 1", "dep 2"]) cache.should_receive(:calculate_checksum).with("user.dep").and_return("user.dep checksum")
cache.register_build("the target", "the command", ["dep 1", "dep 2"], env)
cached_target = cache.instance_variable_get(:@cache)[:targets]["the target"] cached_target = cache.instance_variable_get(:@cache)[:targets]["the target"]
cached_target.should_not be_nil cached_target.should_not be_nil
cached_target[:command].should == "the command" cached_target[:command].should == "the command"
@@ -141,6 +188,9 @@ module Rscons
{fname: "dep 1", checksum: "dep 1 checksum"}, {fname: "dep 1", checksum: "dep 1 checksum"},
{fname: "dep 2", checksum: "dep 2 checksum"}, {fname: "dep 2", checksum: "dep 2 checksum"},
] ]
cached_target[:user_deps].should == [
{fname: "user.dep", checksum: "user.dep checksum"},
]
end end
end end
+77 -20
View File
@@ -1,12 +1,6 @@
module Rscons module Rscons
describe Environment do describe Environment do
describe "#initialize" do describe "#initialize" do
it "stores the construction variables passed in" do
env = Environment.new("CFLAGS" => ["-g"], "CPPPATH" => ["dir"])
env["CFLAGS"].should == ["-g"]
env["CPPPATH"].should == ["dir"]
end
it "adds the default builders when they are not excluded" do it "adds the default builders when they are not excluded" do
env = Environment.new env = Environment.new
env.builders.size.should be > 0 env.builders.size.should be > 0
@@ -17,18 +11,10 @@ module Rscons
end end
it "excludes the default builders with exclude_builders: :all" do it "excludes the default builders with exclude_builders: :all" do
env = Environment.new(exclude_builders: :all) env = Environment.new(exclude_builders: true)
env.builders.size.should == 0 env.builders.size.should == 0
end end
it "excludes the named builders" do
env = Environment.new(exclude_builders: ["Library"])
env.builders.size.should be > 0
env.builders.find {|name, builder| name == "Object"}.should_not be_nil
env.builders.find {|name, builder| name == "Program"}.should_not be_nil
env.builders.find {|name, builder| name == "Library"}.should be_nil
end
context "when a block is given" do context "when a block is given" do
it "yields self and invokes #process()" do it "yields self and invokes #process()" do
env = Environment.new do |env| env = Environment.new do |env|
@@ -60,9 +46,9 @@ module Rscons
describe "#add_builder" do describe "#add_builder" do
it "adds the builder to the list of builders" do it "adds the builder to the list of builders" do
env = Environment.new(exclude_builders: :all) env = Environment.new(exclude_builders: true)
env.builders.keys.should == [] env.builders.keys.should == []
env.add_builder(Rscons::Object.new) env.add_builder(Rscons::Builders::Object.new)
env.builders.keys.should == ["Object"] env.builders.keys.should == ["Object"]
end end
end end
@@ -75,6 +61,18 @@ module Rscons
env.get_build_fname("src\\dir\\other.d", ".a").should == "src/dir/other.a" env.get_build_fname("src\\dir\\other.d", ".a").should == "src/dir/other.a"
env.get_build_fname("source.cc", ".o").should == "source.o" env.get_build_fname("source.cc", ".o").should == "source.o"
end end
context "with a build_root" do
it "uses the build_root unless the path is absolute" do
env = Environment.new
env.build_root = "build/proj"
env.get_build_fname("src/dir/file.c", ".o").should == "build/proj/src/dir/file.o"
env.get_build_fname("/some/lib.c", ".a").should == "/some/lib.a"
env.get_build_fname("C:\\abspath\\mod.cc", ".o").should == "C:/abspath/mod.o"
env.get_build_fname("build\\proj\\generated.c", ".o").should == "build/proj/generated.o"
env.get_build_fname("build/proj.XX", ".yy").should == "build/proj/build/proj.yy"
end
end
end end
context "with build directories" do context "with build directories" do
@@ -87,19 +85,36 @@ module Rscons
env.get_build_fname("libs/otherlib/otherlib.cc", ".o").should == "build/libs/otherlib/otherlib.o" env.get_build_fname("libs/otherlib/otherlib.cc", ".o").should == "build/libs/otherlib/otherlib.o"
env.get_build_fname("other_directory/o.d", ".a").should == "other_directory/o.a" env.get_build_fname("other_directory/o.d", ".a").should == "other_directory/o.a"
end end
context "with a build_root" do
it "uses the build_root unless a build directory matches or the path is absolute" do
env = Environment.new
env.build_dir("src", "bld")
env.build_dir(%r{^libs/([^/]+)}, 'build/libs/\1')
env.build_root = "bldit"
env.get_build_fname("src/input.cc", ".o").should == "bld/input.o"
env.get_build_fname("libs/lib1/some/file.c", ".o").should == "build/libs/lib1/some/file.o"
env.get_build_fname("libs/otherlib/otherlib.cc", ".o").should == "build/libs/otherlib/otherlib.o"
env.get_build_fname("other_directory/o.d", ".a").should == "bldit/other_directory/o.a"
env.get_build_fname("bldit/some/mod.d", ".a").should == "bldit/some/mod.a"
end
end
end end
end end
describe "#[]" do describe "#[]" do
it "allows reading construction variables" do it "allows reading construction variables" do
env = Environment.new("CFLAGS" => ["-g", "-Wall"]) env = Environment.new
env["CFLAGS"] = ["-g", "-Wall"]
env["CFLAGS"].should == ["-g", "-Wall"] env["CFLAGS"].should == ["-g", "-Wall"]
end end
end end
describe "#[]=" do describe "#[]=" do
it "allows writing construction variables" do it "allows writing construction variables" do
env = Environment.new("CFLAGS" => ["-g", "-Wall"]) env = Environment.new
env["CFLAGS"] = ["-g", "-Wall"]
env["CFLAGS"] -= ["-g"] env["CFLAGS"] -= ["-g"]
env["CFLAGS"] += ["-O3"] env["CFLAGS"] += ["-O3"]
env["CFLAGS"].should == ["-Wall", "-O3"] env["CFLAGS"].should == ["-Wall", "-O3"]
@@ -110,7 +125,9 @@ module Rscons
describe "#append" do describe "#append" do
it "allows adding many construction variables at once" do it "allows adding many construction variables at once" do
env = Environment.new("CFLAGS" => ["-g"], "CPPPATH" => ["inc"]) env = Environment.new
env["CFLAGS"] = ["-g"]
env["CPPPATH"] = ["inc"]
env.append("CFLAGS" => ["-Wall"], "CPPPATH" => ["include"]) env.append("CFLAGS" => ["-Wall"], "CPPPATH" => ["include"])
env["CFLAGS"].should == ["-Wall"] env["CFLAGS"].should == ["-Wall"]
env["CPPPATH"].should == ["include"] env["CPPPATH"].should == ["include"]
@@ -158,6 +175,18 @@ module Rscons
end end
end end
describe "#clear_targets" do
it "resets @targets to an empty hash" do
env = Environment.new
env.Program("a.out", "main.o")
expect(env.instance_variable_get(:@targets).keys).to eq(["a.out"])
env.clear_targets
expect(env.instance_variable_get(:@targets).keys).to eq([])
end
end
describe "#build_command" do describe "#build_command" do
it "returns a command based on the variables in the Environment" do it "returns a command based on the variables in the Environment" do
env = Environment.new env = Environment.new
@@ -170,6 +199,20 @@ module Rscons
end end
end end
describe "#expand_varref" do
it "returns the fully expanded variable reference" do
env = Environment.new
env["path"] = ["dir1", "dir2"]
env["flags"] = ["-x", "-y", "${specialflag}"]
env["specialflag"] = "-z"
env.expand_varref(["-p${path}", "${flags}"]).should == ["-pdir1", "-pdir2", "-x", "-y", "-z"]
env.expand_varref("foo").should == "foo"
expect {env.expand_varref("${foo}")}.to raise_error /expand.a.variable.reference/
env.expand_varref("${specialflag}").should == "-z"
env.expand_varref("${path}").should == ["dir1", "dir2"]
end
end
describe "#execute" do describe "#execute" do
context "with echo: :short" do context "with echo: :short" do
context "with no errors" do context "with no errors" do
@@ -228,6 +271,20 @@ module Rscons
end end
end end
describe "#depends" do
it "records the given dependencies in @user_deps" do
env = Environment.new
env.depends("foo", "bar", "baz")
env.instance_variable_get(:@user_deps).should == {"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")
env.instance_variable_get(:@user_deps).should == {"foo" => ["bar", "baz"]}
end
end
describe "#build_sources" do describe "#build_sources" do
class ABuilder < Builder class ABuilder < Builder
def produces?(target, source, env) def produces?(target, source, env)
-6
View File
@@ -1,6 +0,0 @@
describe "Module#short_name" do
it "returns the inner name of the module" do
Rscons::Environment.short_name.should == "Environment"
Rscons::Object.short_name.should == "Object"
end
end
-6
View File
@@ -30,12 +30,6 @@ module Rscons
v["qax"].should == [3, 6] v["qax"].should == [3, 6]
v["qux"].should == {a: :b} v["qux"].should == {a: :b}
end end
it "allows accessing a non-array converted to an array" do
v["fuz", :array].should == ["a string"]
end
it "allows accessing an array as a single value" do
v["qax", :scalar].should == 3
end
end end
describe :[]= do describe :[]= do