Compare commits

..
13 Commits
15 changed files with 323 additions and 108 deletions

No files matched your search

+36 -1
View File
@@ -66,7 +66,8 @@ end
```ruby
class GenerateFoo < Rscons::Builder
def run(target, sources, cache, env, vars)
def run(target, sources, user_deps, cache, env, vars)
cache.mkdir_p(File.dirname(target))
File.open(target, "w") do |fh|
fh.puts <<EOF
#define GENERATED 42
@@ -81,6 +82,40 @@ Rscons::Environment.new do |env|
end
```
### Example: Custom Builder That Only Regenerates When Necessary
```ruby
class CmdBuilder < Rscons::Builder
def run(target, sources, user_deps, cache, env, vars)
cmd = ["cmd", "-i", sources.first, "-o", target]
unless cache.up_to_date?(target, cmd, sources, user_deps)
cache.mkdir_p(File.dirname(target))
system(cmd)
cache.register_build(target, cmd, sources, user_deps)
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, user_deps, cache, env, vars)
cmd = ["cmd", "-i", sources.first, "-o", target]
standard_build("CmdBld #{target}", target, cmd, sources, user_deps, 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
```ruby
+4 -6
View File
@@ -4,7 +4,6 @@ require "rscons/environment"
require "rscons/varset"
require "rscons/version"
require "rscons/monkey/module"
require "rscons/monkey/string"
# default builders
@@ -15,13 +14,12 @@ require "rscons/builders/program"
# Namespace module for rscons classes
module Rscons
DEFAULT_BUILDERS = [
Library,
Object,
Program,
:Library,
:Object,
:Program,
]
class BuildError < Exception
end
class BuildError < RuntimeError; end
# Remove all generated files
def self.clean
+11 -2
View File
@@ -1,8 +1,17 @@
require "fileutils"
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 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
# unless the user overrides any.
# @param env [Environment] The Environment.
@@ -23,11 +32,11 @@ module Rscons
# build command.
# Return the name of the target or false on failure.
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))
FileUtils.rm_f(target)
return false unless env.execute(short_cmd_string, command)
cache.register_build(target, command, sources)
cache.register_build(target, command, sources, env)
end
target
end
+4 -4
View File
@@ -1,8 +1,7 @@
require 'fileutils'
module Rscons
module Builders
# A default RScons builder that produces a static library archive.
class Library < Builder
class Rscons::Builders::Library < Rscons::Builder
def default_variables(env)
{
'AR' => 'ar',
@@ -12,7 +11,7 @@ module Rscons
}
end
def run(target, sources, 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
@@ -26,3 +25,4 @@ module Rscons
end
end
end
end
+5 -3
View File
@@ -1,4 +1,5 @@
module Rscons
module Builders
# A default RScons builder which knows how to produce an object file from
# various types of source files.
class Object < Builder
@@ -50,7 +51,7 @@ module Rscons
end
end
def run(target, sources, cache, env, vars = {})
def run(target, sources, cache, env, vars)
vars = vars.merge({
'_TARGET' => target,
'_SOURCES' => sources,
@@ -62,7 +63,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)
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,9 +72,10 @@ module Rscons
deps += Environment.parse_makefile_deps(vars['_DEPFILE'], target)
FileUtils.rm_f(vars['_DEPFILE'])
end
cache.register_build(target, command, deps.uniq)
cache.register_build(target, command, deps.uniq, env)
end
target
end
end
end
end
+4 -2
View File
@@ -1,7 +1,8 @@
module Rscons
module Builders
# A default RScons builder that knows how to link object files into an
# executable program.
class Program < Builder
class Rscons::Builders::Program < Rscons::Builder
def default_variables(env)
{
'LD' => nil,
@@ -14,7 +15,7 @@ module Rscons
}
end
def run(target, sources, 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
@@ -37,3 +38,4 @@ module Rscons
end
end
end
end
+35 -13
View File
@@ -18,9 +18,15 @@ module Rscons
# {
# 'fname' => 'program.o',
# 'checksum' => '87654321',
# }
# },
# ],
# }
# 'user_deps' => [
# {
# 'fname' => 'lscript.ld',
# 'checksum' => '77551133',
# },
# ],
# },
# 'program.o' => {
# 'checksum' => '87654321',
# 'command' => ['gcc', '-c', '-o', 'program.o', 'program.c'],
@@ -32,15 +38,16 @@ module Rscons
# {
# 'fname' => 'program.h',
# 'checksum' => '7979764643',
# },
# ],
# 'user_deps' => [],
# }
# ]
# }
# }
# },
# directories: {
# 'build' => true,
# 'build/one' => true,
# 'build/two' => true,
# }
# },
# }
class Cache
#### Constants
@@ -82,6 +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 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
@@ -96,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, options = {})
def up_to_date?(target, command, deps, env, options = {})
# target file must exist on disk
return false unless File.exists?(target)
@@ -109,17 +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
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].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
@@ -130,7 +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.
def register_build(target, command, 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),
@@ -139,7 +155,13 @@ module Rscons
fname: dep.encode(__ENCODING__),
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
+59 -29
View File
@@ -9,33 +9,39 @@ module Rscons
# Hash of +{"builder_name" => builder_object}+ pairs.
attr_reader :builders
# :command, :short, or :off
attr_accessor :echo
# 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.
# @param variables [Hash]
# The variables hash can contain construction variables, which are
# uppercase strings (such as "CC" or "LDFLAGS"), user variables, which
# are lowercase strings (such as "sources"), and RScons options, which
# are lowercase symbols (such as :echo).
# @param options [Hash]
# Possible options keys:
# :echo => :command, :short, or :off (default :short)
# :build_root => String specifying build root directory (default nil)
# :exclude_builders => true to omit adding default builders (default false)
# If a block is given, the Environment object is yielded to the block and
# when the block returns, the {#process} method is automatically called.
def initialize(variables = {})
@varset = VarSet.new(variables)
def initialize(options = {})
@varset = VarSet.new
@targets = {}
@user_deps = {}
@builders = {}
@build_dirs = []
@build_hooks = []
@varset[:exclude_builders] ||= []
unless @varset[:exclude_builders] == :all
exclude_builders = Set.new(@varset[:exclude_builders] || [])
DEFAULT_BUILDERS.each do |builder_class|
unless exclude_builders.include?(builder_class.short_name)
unless options[:exclude_builders]
DEFAULT_BUILDERS.each do |builder_class_name|
builder_class = Builders.const_get(builder_class_name)
builder_class or raise "Could not find builder class #{builder_class_name}"
add_builder(builder_class.new)
end
end
end
@varset[:echo] ||= :short
@echo = options[:echo] || :short
@build_root = options[:build_root]
if block_given?
yield self
@@ -45,17 +51,23 @@ module Rscons
# Make a copy of the Environment object.
# The cloned environment will contain a copy of all environment options,
# construction variables, and builders. It will not contain a copy of the
# targets, build hooks, build directories, or the build root. If a block
# is given, the Environment object is yielded to the block and when the
# block returns, the {#process} method is automatically called.
def clone(variables = {})
env = self.class.new
# construction variables, and builders (unless :exclude_builders => true is
# passed as an option). It will not contain a copy of the targets, build
# hooks, build directories, or the build root. If a block is given, the
# Environment object is yielded to the block and when the block returns,
# the {#process} method is automatically called. The possible options keys
# match those documented in the #initialize method.
def clone(options = {})
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
env.append(@varset.clone)
env.append(variables)
if block_given?
yield env
@@ -66,7 +78,7 @@ module Rscons
# Add a {Builder} object to the Environment.
def add_builder(builder)
@builders[builder.class.short_name] = builder
@builders[builder.name] = builder
var_defs = builder.default_variables(self)
if var_defs
var_defs.each_pair do |var, val|
@@ -99,7 +111,7 @@ module Rscons
end
end
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}"
end
end
@@ -156,6 +168,11 @@ module Rscons
cache.write
end
# Clear all targets registered for the Environment.
def clear_targets
@targets = {}
end
# Build a command line from the given template, resolving references to
# variables using the Environment's construction variables and any extra
# variables specified.
@@ -169,7 +186,7 @@ module Rscons
end
# 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
# @param command [Array] The command to execute.
# @param options [Hash] Optional options to pass to Kernel#system.
@@ -177,13 +194,13 @@ module Rscons
print_command = proc do
puts command.map { |c| c =~ /\s/ ? "'#{c}'" : c }.join(' ')
end
if @varset[:echo] == :command
if @echo == :command
print_command.call
elsif @varset[:echo] == :short
elsif @echo == :short
puts short_desc
end
system(*command, options).tap do |result|
unless result or @varset[:echo] == :command
unless result or @echo == :command
$stdout.write "Failed command was: "
print_command.call
end
@@ -209,6 +226,19 @@ module Rscons
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
# given by suffixes.
# This method is used internally by RScons builders.
@@ -217,7 +247,7 @@ module Rscons
# @param cache [Cache] The Cache.
# @param vars [Hash] Extra variables to pass to the builder.
# 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|
if source.has_suffix?(suffixes)
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 = '')
sub(/\.[^.]*$/, suffix)
end
# Return whether the string represents an absolute filesystem path
def absolute_path?
self =~ %r{^(/|\w:[\\/])}
end
end
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons
# gem version
VERSION = "0.2.2"
VERSION = "0.3.0"
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
test_dir('custom_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|
fh.puts <<EOF
#define THE_VALUE 5678
@@ -251,7 +251,8 @@ EOF
env.Program('program-debug', Dir['src/*.c'])
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.Program('program-release', Dir['src/*.c'])
end
@@ -327,6 +328,37 @@ EOF
]
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"]
it "supports building D sources" do
test_dir("d")
+62 -12
View File
@@ -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
@@ -82,13 +87,14 @@ module Rscons
{fname: "dep.2",
checksum: "dep.2.chk"},
{fname: "extra.dep",
checksum: "extra.dep.chk"}]}}}
checksum: "extra.dep.chk"}],
user_deps: []}}}
cache = build_from(_cache)
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.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
@@ -99,11 +105,48 @@ module Rscons
{fname: "dep.2",
checksum: "dep.2.chk"},
{fname: "extra.dep",
checksum: "extra.dep.chk"}]}}}
checksum: "extra.dep.chk"}],
user_deps: []}}}
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
_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
it "returns true when no condition for false is met" do
@@ -114,14 +157,15 @@ module Rscons
{fname: "dep.2",
checksum: "dep.2.chk"},
{fname: "extra.dep",
checksum: "extra.dep.chk"}]}}}
checksum: "extra.dep.chk"}],
user_deps: []}}}
cache = build_from(_cache)
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.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
@@ -129,10 +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.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.should_not be_nil
cached_target[:command].should == "the command"
@@ -141,6 +188,9 @@ module Rscons
{fname: "dep 1", checksum: "dep 1 checksum"},
{fname: "dep 2", checksum: "dep 2 checksum"},
]
cached_target[:user_deps].should == [
{fname: "user.dep", checksum: "user.dep checksum"},
]
end
end
+63 -20
View File
@@ -1,12 +1,6 @@
module Rscons
describe Environment 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
env = Environment.new
env.builders.size.should be > 0
@@ -17,18 +11,10 @@ module Rscons
end
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
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
it "yields self and invokes #process()" do
env = Environment.new do |env|
@@ -60,9 +46,9 @@ module Rscons
describe "#add_builder" 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.add_builder(Rscons::Object.new)
env.add_builder(Rscons::Builders::Object.new)
env.builders.keys.should == ["Object"]
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("source.cc", ".o").should == "source.o"
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
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("other_directory/o.d", ".a").should == "other_directory/o.a"
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
describe "#[]" 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"]
end
end
describe "#[]=" 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"] += ["-O3"]
env["CFLAGS"].should == ["-Wall", "-O3"]
@@ -110,7 +125,9 @@ module Rscons
describe "#append" 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["CFLAGS"].should == ["-Wall"]
env["CPPPATH"].should == ["include"]
@@ -158,6 +175,18 @@ module Rscons
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
it "returns a command based on the variables in the Environment" do
env = Environment.new
@@ -228,6 +257,20 @@ module Rscons
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
class ABuilder < Builder
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