Compare commits

...
22 Commits
Author SHA1 Message Date
josh 469f575564 v1.7.0 2014-09-25 11:35:00 -04:00
josh 07725581e9 add v1.7.0 release notes 2014-09-25 11:34:37 -04:00
josh ebb69cedc2 add @since tag to Environment#add_post_build_hook 2014-09-25 11:31:18 -04:00
josh c98b377ce8 use "rake/clean" module now that it isn' so noisy; clean .yardoc folder 2014-09-25 11:14:39 -04:00
josh a0874e7cf1 add tests for *SUFFIX variables containing Arrays - close #9 2014-09-25 11:12:11 -04:00
josh 0b04229e3d fix Environment#build_dir documentation 2014-09-25 10:47:21 -04:00
josh 20f3da3b04 allow trailing slashes to be passed to env.build_dir() 2014-09-25 10:45:52 -04:00
josh a6a79b8ef1 test cloning post-build hooks 2014-09-23 12:44:02 -04:00
josh 6d4f27602e update README.md notes about build hooks 2014-09-19 12:41:54 -04:00
josh 453295a49b add post-build hooks - close #14 2014-09-19 12:36:57 -04:00
josh dee2b98f6e allow build hooks to register new build targets - close #15 2014-09-19 11:10:59 -04:00
josh b186b3102c clear build targets after processing an Environment - close #17 2014-09-19 10:45:49 -04:00
josh 23c94f7841 fix YARD curly brace escapes 2014-09-11 13:53:10 -04:00
josh 6acb209ef4 v1.6.1 2014-08-19 09:12:35 -04:00
josh 43e12819d7 update .gitignore, version Gemfile.lock 2014-08-19 09:12:21 -04:00
josh 1bfb7dd100 add v1.6.1 release notes 2014-08-19 09:09:37 -04:00
josh df1e3be829 fix #12 - allow overriding dependency file suffix with DEPFILESUFFIX construction variable 2014-08-18 15:53:02 -04:00
josh 851adae66f fix #13 - Environment#depends should expand its arguments for construction variables 2014-08-18 15:39:33 -04:00
josh eb28e09d8c add v1.6.0 release notes 2014-07-10 14:34:35 -04:00
josh 01a84e8819 v1.6.0 2014-06-18 10:45:05 -04:00
josh 28bedfce11 Support lambdas as construction variable values 2014-06-18 10:43:35 -04:00
josh e792fd473b add v1.5.0 release notes 2014-06-18 09:15:49 -04:00
12 changed files with 420 additions and 106 deletions

No files matched your search

+6 -18
View File
@@ -1,18 +1,6 @@
*.gem /.yardoc/
*.rbc /_yardoc/
.bundle /coverage/
.config /doc/
.yardoc /pkg/
Gemfile.lock /build_test_run/
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
build_test_run
+45
View File
@@ -0,0 +1,45 @@
PATH
remote: .
specs:
rscons (1.7.0)
json (~> 1.0)
GEM
remote: https://rubygems.org/
specs:
diff-lcs (1.2.5)
docile (1.1.5)
json (1.8.1)
multi_json (1.10.1)
rake (10.3.2)
redcarpet (3.1.2)
rspec (3.0.0)
rspec-core (~> 3.0.0)
rspec-expectations (~> 3.0.0)
rspec-mocks (~> 3.0.0)
rspec-core (3.0.1)
rspec-support (~> 3.0.0)
rspec-expectations (3.0.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.0.0)
rspec-mocks (3.0.1)
rspec-support (~> 3.0.0)
rspec-support (3.0.0)
simplecov (0.8.2)
docile (~> 1.1.0)
multi_json
simplecov-html (~> 0.8.0)
simplecov-html (0.8.0)
yard (0.8.7.4)
PLATFORMS
ruby
x86-mingw32
DEPENDENCIES
rake
redcarpet
rscons!
rspec
simplecov
yard
+45
View File
@@ -173,6 +173,9 @@ Each build hook block will be invoked for every build operation, so the block
should test the target or sources if its action should only apply to some should test the target or sources if its action should only apply to some
subset of build targets or source files. subset of build targets or source files.
Build hooks can alter construction variable values for a particular build
operation. Build hooks can also register new build targets.
The `build_op` parameter to the build hook block is a Hash describing the The `build_op` parameter to the build hook block is a Hash describing the
build operation with the following keys: build operation with the following keys:
* `:builder` - `Builder` instance in use * `:builder` - `Builder` instance in use
@@ -317,6 +320,19 @@ cloned_env["CPPPATH"] << "three"
`base_env["CPPPATH"]` will not include "three". `base_env["CPPPATH"]` will not include "three".
#### Build Hooks
Environments can have build hooks which are added with `env.add_build_hook()`.
Build hooks are invoked immediately before a builder executes.
Build hooks can modify the construction variables in use for the build
operation.
They can also register new build targets.
Environments can also have post-build hooks added with `env.add_post_build_hook()`.
Post-build hooks are only invoked if the build operation was a success.
Post-build hooks can invoke commands using the newly-built files, or register
new build targets.
### Construction Variable Naming ### Construction Variable Naming
* uppercase strings - the default construction variables that Rscons uses * uppercase strings - the default construction variables that Rscons uses
@@ -329,6 +345,35 @@ http://rubydoc.info/github/holtrop/rscons/frames.
## Release Notes ## Release Notes
### v1.7.0
- allow build hooks to register new build targets
- add post-build hooks (register with Environment#add_post_build_hook)
- clear all build targets after processing an Environment
- allow trailing slashes in arguments to Environment#build_dir
### v1.6.1
- add DEPFILESUFFIX construction variable to override dependency file suffix
- fix Environment#depends to expand its arguments for construction variables
### v1.6.0
- support lambdas as construction variable values
### v1.5.0
- add "json" as a runtime dependency
- update construction variables to match SCons more closely
- add CPPDEFPREFIX, INCPREFIX, CPPDEFINES, CCFLAGS, LIBDIRPREFIX, and LIBLINKPREFIX
- add Environment#shell
- add Environment#parse_flags, #parse_flags!, #merge_flags
- unbuffer $stdout by default
- add PROGSUFFIX construction variable (defaults to .exe on MinGW/Cygwin)
- add Rscons::BuildTarget and Builder#create_build_target
- update specs to RSpec 3.x and fix to run on MinGW/Cygwin/Linux
- add YARD documentation to get to 100% coverage
### v1.4.3 ### v1.4.3
- fix builders properly using construction variable overrides - fix builders properly using construction variable overrides
+4 -8
View File
@@ -8,6 +8,10 @@ end
require "bundler/gem_tasks" require "bundler/gem_tasks"
require "rspec/core/rake_task" require "rspec/core/rake_task"
require "yard" require "yard"
require "rake/clean"
CLEAN.include %w[build_test_run .yardoc doc coverage]
CLOBBER.include %w[pkg]
RSpec::Core::RakeTask.new(:spec) RSpec::Core::RakeTask.new(:spec)
@@ -16,11 +20,3 @@ YARD::Rake::YardocTask.new do |yard|
end end
task :default => :spec task :default => :spec
task :clean do
FileUtils.rm_rf(["build_test_run", "doc", "coverage"])
end
task :clobber => :clean do
FileUtils.rm_rf(["pkg"])
end
+2 -1
View File
@@ -19,6 +19,7 @@ module Rscons
def default_variables(env) def default_variables(env)
{ {
'OBJSUFFIX' => '.o', 'OBJSUFFIX' => '.o',
'DEPFILESUFFIX' => '.mf',
'CPPDEFPREFIX' => '-D', 'CPPDEFPREFIX' => '-D',
'INCPREFIX' => '-I', 'INCPREFIX' => '-I',
@@ -87,7 +88,7 @@ module Rscons
vars = vars.merge({ vars = vars.merge({
'_TARGET' => target, '_TARGET' => target,
'_SOURCES' => sources, '_SOURCES' => sources,
'_DEPFILE' => Rscons.set_suffix(target, '.mf'), '_DEPFILE' => Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars)),
}) })
com_prefix = KNOWN_SUFFIXES.find do |compiler, suffix_var| com_prefix = KNOWN_SUFFIXES.find do |compiler, suffix_var|
sources.first.end_with?(*env.expand_varref("${#{suffix_var}}")) sources.first.end_with?(*env.expand_varref("${#{suffix_var}}"))
+75 -30
View File
@@ -7,7 +7,7 @@ module Rscons
# contains a collection of construction variables, options, builders, and # contains a collection of construction variables, options, builders, and
# rules for building targets. # rules for building targets.
class Environment class Environment
# @return [Hash] Set of \{"builder_name" => builder_object} pairs. # @return [Hash] Set of !{"builder_name" => builder_object} pairs.
attr_reader :builders attr_reader :builders
# :command, :short, or :off # :command, :short, or :off
@@ -42,7 +42,7 @@ module Rscons
@user_deps = {} @user_deps = {}
@builders = {} @builders = {}
@build_dirs = [] @build_dirs = []
@build_hooks = [] @build_hooks = {pre: [], post: []}
unless options[:exclude_builders] unless options[:exclude_builders]
DEFAULT_BUILDERS.each do |builder_class_name| DEFAULT_BUILDERS.each do |builder_class_name|
builder_class = Builders.const_get(builder_class_name) builder_class = Builders.const_get(builder_class_name)
@@ -103,9 +103,12 @@ module Rscons
end end
end end
if clone.include?(:build_hooks) if clone.include?(:build_hooks)
@build_hooks.each do |build_hook_block| @build_hooks[:pre].each do |build_hook_block|
env.add_build_hook(&build_hook_block) env.add_build_hook(&build_hook_block)
end end
@build_hooks[:post].each do |build_hook_block|
env.add_post_build_hook(&build_hook_block)
end
end end
if block_given? if block_given?
@@ -132,6 +135,12 @@ module Rscons
# Add a build hook to the Environment. # Add a build hook to the Environment.
# #
# Build hooks are Ruby blocks which are invoked immediately before a
# build operation takes place. Build hooks have an opportunity to modify
# the construction variables in use for the build operation based on the
# builder in use, target file name, or sources. Build hooks can also
# register new build targets.
#
# @yield [build_op] # @yield [build_op]
# Invoke the given block with the current build operation. # Invoke the given block with the current build operation.
# @yieldparam build_op [Hash] # @yieldparam build_op [Hash]
@@ -144,19 +153,45 @@ module Rscons
# #
# @return [void] # @return [void]
def add_build_hook(&block) def add_build_hook(&block)
@build_hooks << block @build_hooks[:pre] << block
end
# Add a post build hook to the Environment.
#
# Post-build hooks are Ruby blocks which are invoked immediately after a
# build operation takes place. Post-build hooks are only invoked if the
# build operation succeeded. Post-build hooks can register new build
# targets.
#
# @since 1.7.0
#
# @yield [build_op]
# Invoke the given block with the current build operation.
# @yieldparam build_op [Hash]
# Hash with keys:
# - :builder - The builder object in use.
# - :target - Target file name.
# - :sources - List of source file(s).
# - :vars - Set of construction variable values in use.
# - :env - The Environment invoking the builder.
#
# @return [void]
def add_post_build_hook(&block)
@build_hooks[:post] << block
end end
# Specify a build directory for this Environment. # Specify a build directory for this Environment.
# #
# Source files from src_dir will produce object files under obj_dir. # Source files from src_dir will produce object files under obj_dir.
# #
# @param src_dir [String] Path to the source directory. # @param src_dir [String, Regexp] Path to the source directory.
# @param obj_dir [String] Path to the object directory. # @param obj_dir [String] Path to the object directory.
# #
# @return [void] # @return [void]
def build_dir(src_dir, obj_dir) def build_dir(src_dir, obj_dir)
src_dir = src_dir.gsub('\\', '/') if src_dir.is_a?(String) if src_dir.is_a?(String)
src_dir = src_dir.gsub("\\", "/").sub(%r{/*$}, "")
end
@build_dirs << [src_dir, obj_dir] @build_dirs << [src_dir, obj_dir]
end end
@@ -216,23 +251,25 @@ module Rscons
# #
# @return [void] # @return [void]
def process def process
unless @targets.empty? expand_paths!
expand_paths! while @targets.size > 0
targets = @targets
@targets = {}
cache = Cache.instance cache = Cache.instance
cache.clear_checksum_cache! cache.clear_checksum_cache!
targets_processed = {} targets_processed = {}
process_target = proc do |target| process_target = proc do |target|
targets_processed[target] ||= begin targets_processed[target] ||= begin
@targets[target][:sources].each do |src| targets[target][:sources].each do |src|
if @targets.include?(src) and not targets_processed.include?(src) if targets.include?(src) and not targets_processed.include?(src)
process_target.call(src) process_target.call(src)
end end
end end
result = run_builder(@targets[target][:builder], result = run_builder(targets[target][:builder],
target, target,
@targets[target][:sources], targets[target][:sources],
cache, cache,
@targets[target][:vars] || {}) targets[target][:vars] || {})
unless result unless result
raise BuildError.new("Failed to build #{target}") raise BuildError.new("Failed to build #{target}")
end end
@@ -240,7 +277,7 @@ module Rscons
end end
end end
begin begin
@targets.each do |target, target_params| targets.each do |target, target_params|
process_target.call(target) process_target.call(target)
end end
ensure ensure
@@ -263,11 +300,13 @@ module Rscons
# #
# @return [Array, String] Expansion of the variable reference. # @return [Array, String] Expansion of the variable reference.
def expand_varref(varref, extra_vars = nil) def expand_varref(varref, extra_vars = nil)
if extra_vars.nil? vars = if extra_vars.nil?
@varset @varset
else else
@varset.merge(extra_vars) @varset.merge(extra_vars)
end.expand_varref(varref) end
lambda_args = [env: self, vars: vars]
vars.expand_varref(varref, lambda_args)
end end
alias_method :build_command, :expand_varref alias_method :build_command, :expand_varref
@@ -349,7 +388,8 @@ module Rscons
# #
# @return [void] # @return [void]
def depends(target, *user_deps) def depends(target, *user_deps)
target = target.to_s target = expand_varref(target.to_s)
user_deps = user_deps.map {|ud| expand_varref(ud)}
@user_deps[target] ||= [] @user_deps[target] ||= []
@user_deps[target] = (@user_deps[target] + user_deps).uniq @user_deps[target] = (@user_deps[target] + user_deps).uniq
end end
@@ -407,17 +447,22 @@ module Rscons
# @return [String,false] Return value from the {Builder}'s +run+ method. # @return [String,false] Return value from the {Builder}'s +run+ method.
def run_builder(builder, target, sources, cache, vars) def run_builder(builder, target, sources, cache, vars)
vars = @varset.merge(vars) vars = @varset.merge(vars)
@build_hooks.each do |build_hook_block| call_build_hooks = lambda do |sec|
build_operation = { @build_hooks[sec].each do |build_hook_block|
builder: builder, build_operation = {
target: target, builder: builder,
sources: sources, target: target,
vars: vars, sources: sources,
env: self, vars: vars,
} env: self,
build_hook_block.call(build_operation) }
build_hook_block.call(build_operation)
end
end end
builder.run(target, sources, cache, self, vars) call_build_hooks[:pre]
rv = builder.run(target, sources, cache, self, vars)
call_build_hooks[:post] if rv
rv
end end
# Expand a path to be relative to the Environment's build root. # Expand a path to be relative to the Environment's build root.
+22 -13
View File
@@ -83,32 +83,41 @@ module Rscons
end end
alias_method :clone, :merge alias_method :clone, :merge
# Replace "$\{var}" variable references in varref with the expanded # Replace "$!{var}" variable references in varref with the expanded
# variables' values, recursively. # variables' values, recursively.
# #
# @param varref [String, Array] Value containing references to variables. # @param varref [nil, String, Array, Proc]
# Value containing references to variables.
# @param lambda_args [Array]
# Arguments to pass to any lambda variable values to be expanded.
# #
# @return [String, Array] # @return [nil, String, Array]
# Expanded value with "$\{var}" variable references replaced. # Expanded value with "$!{var}" variable references replaced.
def expand_varref(varref) def expand_varref(varref, lambda_args)
if varref.is_a?(Array) if varref.is_a?(String)
varref.map do |ent|
expand_varref(ent)
end.flatten
else
if varref =~ /^(.*)\$\{([^}]+)\}(.*)$/ if varref =~ /^(.*)\$\{([^}]+)\}(.*)$/
prefix, varname, suffix = $1, $2, $3 prefix, varname, suffix = $1, $2, $3
varval = expand_varref(self[varname]) varval = expand_varref(self[varname], lambda_args)
if varval.is_a?(String) or varval.nil? if varval.is_a?(String) or varval.nil?
expand_varref("#{prefix}#{varval}#{suffix}") expand_varref("#{prefix}#{varval}#{suffix}", lambda_args)
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}", lambda_args)}.flatten
else else
raise "I do not know how to expand a variable reference to a #{varval.class.name} (from #{varname.inspect} => #{self[varname].inspect})" raise "I do not know how to expand a variable reference to a #{varval.class.name} (from #{varname.inspect} => #{self[varname].inspect})"
end end
else else
varref varref
end end
elsif varref.is_a?(Array)
varref.map do |ent|
expand_varref(ent, lambda_args)
end.flatten
elsif varref.is_a?(Proc)
expand_varref(varref[*lambda_args], lambda_args)
elsif varref.nil?
nil
else
raise "Unknown varref type: #{varref.class} (#{varref.inspect})"
end end
end end
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons module Rscons
# gem version # gem version
VERSION = "1.5.0" VERSION = "1.7.0"
end end
+171 -14
View File
@@ -10,6 +10,7 @@ class Dir
end end
describe Rscons do describe Rscons do
BUILD_TEST_RUN_DIR = "build_test_run" BUILD_TEST_RUN_DIR = "build_test_run"
def rm_rf(dir) def rm_rf(dir)
@@ -120,34 +121,43 @@ describe Rscons do
it 'rebuilds a C module when a header it depends on changes' do it 'rebuilds a C module when a header it depends on changes' do
test_dir('header') test_dir('header')
env = Rscons::Environment.new do |env| make_env = lambda do
env.Program('header', Dir['*.c']) Rscons::Environment.new do |env|
env.Program('header', Dir['*.c'])
end
end end
make_env[]
expect(`./header`).to eq "The value is 2\n" expect(`./header`).to eq "The value is 2\n"
file_sub('header.h') {|line| line.sub(/2/, '5')} file_sub('header.h') {|line| line.sub(/2/, '5')}
env.process make_env[]
expect(`./header`).to eq "The value is 5\n" expect(`./header`).to eq "The value is 5\n"
end end
it 'does not rebuild a C module when its dependencies have not changed' do it 'does not rebuild a C module when its dependencies have not changed' do
test_dir('header') test_dir('header')
env = Rscons::Environment.new do |env| make_env = lambda do
env.Program('header', Dir['*.c']) Rscons::Environment.new do |env|
env.Program('header', Dir['*.c'])
end
end end
env = make_env[]
expect(`./header`).to eq "The value is 2\n" expect(`./header`).to eq "The value is 2\n"
expect(lines).to eq [ expect(lines).to eq [
'CC header.o', 'CC header.o',
"LD header#{env["PROGSUFFIX"]}", "LD header#{env["PROGSUFFIX"]}",
] ]
env.process make_env[]
expect(lines).to eq [] expect(lines).to eq []
end end
it "does not rebuild a C module when only the file's timestamp has changed" do it "does not rebuild a C module when only the file's timestamp has changed" do
test_dir('header') test_dir('header')
env = Rscons::Environment.new do |env| make_env = lambda do
env.Program('header', Dir['*.c']) Rscons::Environment.new do |env|
env.Program('header', Dir['*.c'])
end
end end
env = make_env[]
expect(`./header`).to eq "The value is 2\n" expect(`./header`).to eq "The value is 2\n"
expect(lines).to eq [ expect(lines).to eq [
'CC header.o', 'CC header.o',
@@ -155,7 +165,7 @@ describe Rscons do
] ]
sleep 0.05 sleep 0.05
file_sub('header.c') {|line| line} file_sub('header.c') {|line| line}
env.process make_env[]
expect(lines).to eq [] expect(lines).to eq []
end end
@@ -187,6 +197,19 @@ describe Rscons do
expect(File.exists?('build_two/two.o')).to be_truthy expect(File.exists?('build_two/two.o')).to be_truthy
end end
it "supports trailing slashes at the end of build_dir sources and destinations" do
test_dir("build_dir")
Rscons::Environment.new do |env|
env.append("CPPPATH" => Dir["src/**/*/"])
env.build_dir("src/one/", "build_one/")
env.build_dir("src/two", "build_two")
env.Program("build_dir", Dir["src/**/*.c"])
end
expect(`./build_dir`).to eq "Hello from two()\n"
expect(File.exists?("build_one/one.o")).to be_truthy
expect(File.exists?("build_two/two.o")).to be_truthy
end
it 'uses build directories before build root' do it 'uses build directories before build root' do
test_dir('build_dir') test_dir('build_dir')
env = Rscons::Environment.new do |env| env = Rscons::Environment.new do |env|
@@ -303,11 +326,14 @@ EOF
end end
end end
env = Rscons::Environment.new do |env| make_env = lambda do
env.add_builder(CHGen.new) Rscons::Environment.new do |env|
env.CHGen("inc.c", ["program.c"]) env.add_builder(CHGen.new)
env.Program("program", Dir["*.c"] + ["inc.c"]) env.CHGen("inc.c", ["program.c"])
env.Program("program", %w[program.c inc.c])
end
end end
env = make_env[]
expect(lines).to eq ["CHGen inc.c", "CC program.o", "CC inc.o", "LD program#{env["PROGSUFFIX"]}"] expect(lines).to eq ["CHGen inc.c", "CC program.o", "CC inc.o", "LD program#{env["PROGSUFFIX"]}"]
expect(File.exists?("inc.c")).to be_truthy expect(File.exists?("inc.c")).to be_truthy
@@ -315,7 +341,7 @@ EOF
expect(`./program`).to eq "The value is 42\n" expect(`./program`).to eq "The value is 42\n"
File.open("inc.c", "w") {|fh| fh.puts "int THE_VALUE = 33;"} File.open("inc.c", "w") {|fh| fh.puts "int THE_VALUE = 33;"}
env.process make_env[]
expect(lines).to eq ["CHGen inc.c"] expect(lines).to eq ["CHGen inc.c"]
expect(`./program`).to eq "The value is 42\n" expect(`./program`).to eq "The value is 42\n"
end end
@@ -347,12 +373,16 @@ EOF
it 'allows cloning all attributes of an Environment object' do it 'allows cloning all attributes of an Environment object' do
test_dir('clone_env') test_dir('clone_env')
built_targets = []
env1 = Rscons::Environment.new(echo: :command) do |env| env1 = Rscons::Environment.new(echo: :command) do |env|
env.build_dir('src', 'build') env.build_dir('src', 'build')
env['CFLAGS'] = '-O2' env['CFLAGS'] = '-O2'
env.add_build_hook do |build_op| env.add_build_hook do |build_op|
build_op[:vars]['CPPFLAGS'] = '-DSTRING="Hello"' build_op[:vars]['CPPFLAGS'] = '-DSTRING="Hello"'
end end
env.add_post_build_hook do |build_op|
built_targets << build_op[:target]
end
env.Program('program', Dir['src/*.c']) env.Program('program', Dir['src/*.c'])
end end
@@ -365,6 +395,12 @@ EOF
%Q{gcc -o program#{env1["PROGSUFFIX"]} build/program.o}, %Q{gcc -o program#{env1["PROGSUFFIX"]} build/program.o},
%Q{gcc -o program2#{env2["PROGSUFFIX"]} build/program.o}, %Q{gcc -o program2#{env2["PROGSUFFIX"]} build/program.o},
] ]
expect(built_targets).to eq([
"build/program.o",
"program#{env1['PROGSUFFIX']}",
"build/program.o",
"program2#{env1['PROGSUFFIX']}",
])
end end
it 'builds a C++ program with one source file' do it 'builds a C++ program with one source file' do
@@ -543,4 +579,125 @@ EOF
expect(File.exists?('inc.h')).to be_truthy expect(File.exists?('inc.h')).to be_truthy
expect(`./program`).to eq "The value is 678\n" expect(`./program`).to eq "The value is 678\n"
end end
it "supports lambdas as construction variable values" do
env = Rscons::Environment.new do |env|
env["prefix"] = "-H"
env["suffix"] = "xyz"
env[:cfg] = {val: 44}
env["computed"] = lambda do |args|
"#{args[:env]['prefix']}#{args[:env][:cfg][:val]}#{args[:env]['suffix']}"
end
env["lambda_recurse"] = lambda do |args|
"${prefix}ello"
end
end
e2 = env.clone
e2[:cfg][:val] = 38
expect(env.expand_varref("${computed}")).to eq("-H44xyz")
expect(e2.expand_varref("${computed}")).to eq("-H38xyz")
expect(env.expand_varref("${lambda_recurse}")).to eq("-Hello")
end
it "supports registering build targets from within a build hook" do
test_dir("simple")
Rscons::Environment.new do |env|
env.Program("simple", Dir["*.c"])
env.add_build_hook do |build_op|
if build_op[:target].end_with?(".o")
env.Disassemble("#{build_op[:target]}.txt", build_op[:target])
end
end
end
expect(File.exists?("simple.o")).to be_truthy
expect(File.exists?("simple.o.txt")).to be_truthy
expect(`./simple`).to eq "This is a simple C program\n"
end
it "supports post-build hooks" do
test_dir("simple")
built_targets = []
env = Rscons::Environment.new do |env|
env.Program("simple", Dir["*.c"])
env.add_post_build_hook do |build_op|
built_targets << build_op[:target]
expect(File.exists?(build_op[:target])).to be_truthy
end
end
expect(File.exists?("simple.o")).to be_truthy
expect(`./simple`).to eq "This is a simple C program\n"
expect(built_targets).to eq ["simple.o", "simple#{env["PROGSUFFIX"]}"]
end
it "supports multiple values for CXXSUFFIX" do
test_dir("simple_cc")
File.open("other.cccc", "w") {|fh| fh.puts}
Rscons::Environment.new do |env|
env["CXXSUFFIX"] = %w[.cccc .cc]
env["CXXFLAGS"] += %w[-x c++]
env.Program("simple", Dir["*.cc"] + ["other.cccc"])
end
expect(File.exists?("simple.o")).to be_truthy
expect(File.exists?("other.o")).to be_truthy
expect(`./simple`).to eq "This is a simple C++ program\n"
end
it "supports multiple values for CSUFFIX" do
test_dir("build_dir")
FileUtils.mv("src/one/one.c", "src/one/one.yargh")
Rscons::Environment.new do |env|
env["CSUFFIX"] = %w[.yargh .c]
env["CFLAGS"] += %w[-x c]
env["CPPPATH"] += Dir["src/**/"]
env.Program("build_dir", Dir["src/**/*.{c,yargh}"])
end
expect(File.exists?("src/one/one.o")).to be_truthy
expect(File.exists?("src/two/two.o")).to be_truthy
expect(`./build_dir`).to eq "Hello from two()\n"
end
it "supports multiple values for OBJSUFFIX" do
test_dir("two_sources")
env = Rscons::Environment.new() do |env|
env["OBJSUFFIX"] = %w[.oooo .ooo]
env.Object("one.oooo", "one.c", "CPPFLAGS" => ["-DONE"])
env.Object("two.ooo", "two.c")
env.Program("two_sources", %w[one.oooo two.ooo])
end
expect(File.exists?("two_sources#{env["PROGSUFFIX"]}")).to be_truthy
expect(`./two_sources`).to eq "This is a C program with two sources.\n"
end
it "supports multiple values for LIBSUFFIX" do
test_dir("two_sources")
env = Rscons::Environment.new() do |env|
env["LIBSUFFIX"] = %w[.aaaa .aaa]
env.Library("one.aaaa", "one.c", "CPPFLAGS" => ["-DONE"])
env.Library("two.aaa", "two.c")
env.Program("two_sources", %w[one.aaaa two.aaa])
end
expect(File.exists?("two_sources#{env["PROGSUFFIX"]}")).to be_truthy
expect(`./two_sources`).to eq "This is a C program with two sources.\n"
end
it "supports multiple values for ASSUFFIX" do
test_dir("two_sources")
env = Rscons::Environment.new() do |env|
env["ASSUFFIX"] = %w[.ssss .sss]
env["CFLAGS"] += %w[-S]
env.Object("one.ssss", "one.c", "CPPFLAGS" => ["-DONE"])
env.Object("two.sss", "two.c")
env.Program("two_sources", %w[one.ssss two.sss], "ASFLAGS" => env["ASFLAGS"] + %w[-x assembler])
end
expect(lines).to eq([
"CC one.ssss",
"CC two.sss",
"AS one.o",
"AS two.o",
"LD two_sources#{env["PROGSUFFIX"]}",
])
expect(File.exists?("two_sources#{env["PROGSUFFIX"]}")).to be_truthy
expect(`./two_sources`).to eq "This is a C program with two sources.\n"
end
end end
+18 -6
View File
@@ -2,19 +2,31 @@ module Rscons
module Builders module Builders
describe Object do describe Object do
let(:env) {Environment.new} let(:env) {Environment.new}
let(:cache) {double(Cache)}
subject {Object.new} subject {Object.new}
it "supports overriding CCCMD construction variable" do it "supports overriding CCCMD construction variable" do
cache = "cache" expect(cache).to receive(:up_to_date?).and_return(false)
allow(cache).to receive(:up_to_date?) { false } expect(cache).to receive(:mkdir_p)
allow(cache).to receive(:mkdir_p) { } expect(FileUtils).to receive(:rm_f)
allow(cache).to receive(:register_build) { }
allow(FileUtils).to receive(:rm_f) { }
allow(File).to receive(:exists?) { false }
expect(env).to receive(:execute).with("CC mod.o", ["llc", "mod.c"]).and_return(true) expect(env).to receive(:execute).with("CC mod.o", ["llc", "mod.c"]).and_return(true)
expect(File).to receive(:exists?).and_return(false)
expect(cache).to receive(:register_build)
subject.run("mod.o", ["mod.c"], cache, env, "CCCMD" => ["llc", "${_SOURCES}"]) subject.run("mod.o", ["mod.c"], cache, env, "CCCMD" => ["llc", "${_SOURCES}"])
end end
it "supports overriding DEPFILESUFFIX construction variable" do
expect(cache).to receive(:up_to_date?).and_return(false)
expect(cache).to receive(:mkdir_p)
expect(FileUtils).to receive(:rm_f)
expect(env).to receive(:execute).with(anything, %w[gcc -c -o f.o -MMD -MF f.d in.c]).and_return(true)
expect(File).to receive(:exists?).with("f.d").and_return(false)
expect(cache).to receive(:register_build)
subject.run("f.o", ["in.c"], cache, env, "DEPFILESUFFIX" => ".d")
end
it "raises an error when given a source file with an unknown suffix" do it "raises an error when given a source file with an unknown suffix" do
expect { subject.run("mod.o", ["mod.xyz"], :cache, env, {}) }.to raise_error /unknown input file type: "mod.xyz"/ expect { subject.run("mod.o", ["mod.xyz"], :cache, env, {}) }.to raise_error /unknown input file type: "mod.xyz"/
end end
+8 -1
View File
@@ -247,7 +247,7 @@ module Rscons
env["foo"] = {} env["foo"] = {}
expect(env.expand_varref(["-p${path}", "${flags}"])).to eq ["-pdir1", "-pdir2", "-x", "-y", "-z"] expect(env.expand_varref(["-p${path}", "${flags}"])).to eq ["-pdir1", "-pdir2", "-x", "-y", "-z"]
expect(env.expand_varref("foo")).to eq "foo" expect(env.expand_varref("foo")).to eq "foo"
expect {env.expand_varref("${foo}")}.to raise_error /expand.a.variable.reference/ expect {env.expand_varref("${foo}")}.to raise_error /Unknown.varref.type/
expect(env.expand_varref("${specialflag}")).to eq "-z" expect(env.expand_varref("${specialflag}")).to eq "-z"
expect(env.expand_varref("${path}")).to eq ["dir1", "dir2"] expect(env.expand_varref("${path}")).to eq ["dir1", "dir2"]
end end
@@ -322,6 +322,13 @@ module Rscons
env.depends("foo", "bar", "baz") env.depends("foo", "bar", "baz")
expect(env.instance_variable_get(:@user_deps)).to eq({"foo" => ["bar", "baz"]}) expect(env.instance_variable_get(:@user_deps)).to eq({"foo" => ["bar", "baz"]})
end end
it "expands arguments for construction variable references" do
env = Environment.new
env["foo"] = "foo.exe"
env["bar"] = "bar.c"
env.depends("${foo}", "${bar}", "a.h")
expect(env.instance_variable_get(:@user_deps)).to eq({"foo.exe" => ["bar.c", "a.h"]})
end
end end
describe "#build_sources" do describe "#build_sources" do
+23 -14
View File
@@ -126,26 +126,26 @@ module Rscons
"CPPPATH" => ["dir1", "dir2"], "CPPPATH" => ["dir1", "dir2"],
"compiler" => "${CC}", "compiler" => "${CC}",
"cmd" => ["${CC}", "-c", "${CFLAGS}", "-I${CPPPATH}"], "cmd" => ["${CC}", "-c", "${CFLAGS}", "-I${CPPPATH}"],
"hash" => {}) "lambda" => lambda {|args| "#{args[:v]}--12"})
it "expands to the string itself if the string is not a variable reference" do it "expands to the string itself if the string is not a variable reference" do
expect(v.expand_varref("CC")).to eq("CC") expect(v.expand_varref("CC", :lambda_args)).to eq("CC")
expect(v.expand_varref("CPPPATH")).to eq("CPPPATH") expect(v.expand_varref("CPPPATH", :lambda_args)).to eq("CPPPATH")
expect(v.expand_varref("str")).to eq("str") expect(v.expand_varref("str", :lambda_args)).to eq("str")
end end
it "expands a single variable reference beginning with a '$'" do it "expands a single variable reference beginning with a '$'" do
expect(v.expand_varref("${CC}")).to eq("gcc") expect(v.expand_varref("${CC}", :lambda_args)).to eq("gcc")
expect(v.expand_varref("${CPPPATH}")).to eq(["dir1", "dir2"]) expect(v.expand_varref("${CPPPATH}", :lambda_args)).to eq(["dir1", "dir2"])
end end
it "expands a single variable reference in ${arr} notation" do it "expands a single variable reference in ${arr} notation" do
expect(v.expand_varref("prefix${CFLAGS}suffix")).to eq(["prefix-Wallsuffix", "prefix-O2suffix"]) expect(v.expand_varref("prefix${CFLAGS}suffix", :lambda_args)).to eq(["prefix-Wallsuffix", "prefix-O2suffix"])
expect(v.expand_varref(v["cmd"])).to eq(["gcc", "-c", "-Wall", "-O2", "-Idir1", "-Idir2"]) expect(v.expand_varref(v["cmd"], :lambda_args)).to eq(["gcc", "-c", "-Wall", "-O2", "-Idir1", "-Idir2"])
end end
it "expands a variable reference recursively" do it "expands a variable reference recursively" do
expect(v.expand_varref("${compiler}")).to eq("gcc") expect(v.expand_varref("${compiler}", :lambda_args)).to eq("gcc")
expect(v.expand_varref("${cmd}")).to eq(["gcc", "-c", "-Wall", "-O2", "-Idir1", "-Idir2"]) expect(v.expand_varref("${cmd}", :lambda_args)).to eq(["gcc", "-c", "-Wall", "-O2", "-Idir1", "-Idir2"])
end end
it "resolves multiple variable references in one element by enumerating all combinations" do it "resolves multiple variable references in one element by enumerating all combinations" do
expect(v.expand_varref("cflag: ${CFLAGS}, cpppath: ${CPPPATH}, compiler: ${compiler}")).to eq([ expect(v.expand_varref("cflag: ${CFLAGS}, cpppath: ${CPPPATH}, compiler: ${compiler}", :lambda_args)).to eq([
"cflag: -Wall, cpppath: dir1, compiler: gcc", "cflag: -Wall, cpppath: dir1, compiler: gcc",
"cflag: -O2, cpppath: dir1, compiler: gcc", "cflag: -O2, cpppath: dir1, compiler: gcc",
"cflag: -Wall, cpppath: dir2, compiler: gcc", "cflag: -Wall, cpppath: dir2, compiler: gcc",
@@ -153,10 +153,19 @@ module Rscons
]) ])
end end
it "returns an empty string when a variable reference refers to a non-existent variable" do it "returns an empty string when a variable reference refers to a non-existent variable" do
expect(v.expand_varref("${not_here}")).to eq("") expect(v.expand_varref("${not_here}", :lambda_args)).to eq("")
end end
it "raises an error when a variable reference refers to an unhandled type" do it "calls a lambda with the given lambda arguments" do
expect { v.expand_varref("${hash}") }.to raise_error /I do not know how to expand a variable reference to a Hash/ expect(v.expand_varref("${lambda}", [v: "fez"])).to eq("fez--12")
end
it "raises an error when given an invalid argument" do
expect { v.expand_varref({a: :b}, :lambda_args) }.to raise_error /Unknown varref type: Hash/
end
it "raises an error when an expanded variable is an unexpected type" do
expect(v).to receive(:[]).at_least(1).times.with("bad").and_return("bad_val")
expect(v).to receive(:expand_varref).with("bad_val", :lambda_args).and_return({a: :b})
expect(v).to receive(:expand_varref).and_call_original
expect { v.expand_varref("${bad}", :lambda_args) }.to raise_error /I do not know how to expand a variable reference to a Hash/
end end
end end
end end