Compare commits

...
29 Commits
Author SHA1 Message Date
josh cd266c511e v0.0.12 2013-10-08 11:54:38 -04:00
josh 0f5fee359d cloning an Environment should make a deep copy of its construction variables 2013-10-08 11:54:24 -04:00
josh dc6bac2895 change build_dir build test to test build_dir specified as a Regexp 2013-10-04 21:40:11 -04:00
josh e50aac31c9 tweak custom_builder build test for additional coverage 2013-10-04 21:26:51 -04:00
josh e788f6ec25 claim simplecov coverage from build_tests 2013-10-04 21:05:44 -04:00
josh 8d893d7b72 rspec Module monkey-patching 2013-10-04 20:22:37 -04:00
josh c258dc379f rspec VarSet 2013-10-04 20:18:40 -04:00
josh 0facfb274d turn on simplecov 2013-10-04 19:37:56 -04:00
josh 3339cf5280 Cache: re-encode file names in default encoding 2013-09-20 15:58:55 -04:00
josh e041ea6612 v0.0.11 2013-09-19 17:06:37 -04:00
josh ab51ddcc1b do not try to clean directories that have already been removed 2013-09-19 17:06:20 -04:00
josh 3e4e9cce1c v0.0.10 2013-09-19 16:48:15 -04:00
josh 32d19489cb Cache: encode directory names in __ENCODING__ - close #3 2013-09-19 16:45:35 -04:00
josh 45a0d56c54 recover from, and warn when, .rsconscache loads something other than a Hash 2013-09-19 16:18:07 -04:00
josh 9b954d9d98 v0.0.9 2013-09-19 09:43:17 -04:00
josh e63b4c932f Cache: create missing hash entries from previous versions of the cache file 2013-09-19 09:43:00 -04:00
josh 97e11f45e5 v0.0.8 2013-09-18 21:18:00 -04:00
josh 5c6239ea6d Rscons.clean() removes any directories it created if they are empty 2013-09-18 21:17:42 -04:00
josh 770a22250a add Rscons.clear() class method 2013-09-17 21:48:50 -04:00
josh 983cb7cf21 v0.0.7 2013-09-16 23:03:14 -04:00
josh 986b8f08cc prepend builder-temporary construction variables with "_"; reserve lowercase string variables for user 2013-09-16 22:30:16 -04:00
josh f13664331f create requisite output directories for build targets; refactor common buile functionality into Builder#standard_build() 2013-09-15 15:27:16 -04:00
josh 17c7bf1d31 v0.0.6 2013-09-12 23:37:37 -04:00
josh 64d15602cc add Library default builder 2013-09-12 23:35:34 -04:00
josh e04e67698a add rspec test for overridding construction variables for an individual builer 2013-09-12 23:05:04 -04:00
josh 837dff9374 pass extra construction variables to Builder#run() 2013-09-12 22:57:22 -04:00
josh 75f2418570 refactor some Program functionality into Environment#build_sources() 2013-09-12 22:38:52 -04:00
josh 711e96cec8 write out cache file when raising a build error exception 2013-08-12 14:40:46 -04:00
josh 289d27d41d raise Rscons::BuildError on a build failure 2013-08-12 13:13:22 -04:00
23 changed files with 417 additions and 70 deletions

No files matched your search

+1 -1
View File
@@ -1,5 +1,5 @@
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env.append('CPPPATH' => Dir['src/**/*/']) env.append('CPPPATH' => Dir['src/**/*/'])
env.build_dir('src', 'build') env.build_dir(%r{^src/([^/]+)/}, 'build_\\1/')
env.Program('build_dir', Dir['src/**/*.c']) env.Program('build_dir', Dir['src/**/*.c'])
end end
+2 -3
View File
@@ -1,5 +1,5 @@
class MySource < Rscons::Builder class MySource < Rscons::Builder
def run(target, sources, cache, env) def run(target, sources, cache, env, vars = {})
File.open(target, 'w') do |fh| File.open(target, 'w') do |fh|
fh.puts <<EOF fh.puts <<EOF
#define THE_VALUE 5678 #define THE_VALUE 5678
@@ -9,8 +9,7 @@ EOF
end end
end end
Rscons::Environment.new(echo: :short) do |env| Rscons::Environment.new(echo: :short, builders: [MySource.new]) do |env|
env.add_builder(MySource.new)
env.MySource('inc.h', []) env.MySource('inc.h', [])
env.Program('program', Dir['*.c']) env.Program('program', Dir['*.c'])
end end
+4
View File
@@ -0,0 +1,4 @@
Rscons::Environment.new(echo: :command) do |env|
env.Program('library', ['lib.a', 'three.c'])
env.Library("lib.a", ['one.c', 'two.c'], 'CPPFLAGS' => ['-Dmake_lib'])
end
+8
View File
@@ -0,0 +1,8 @@
#include <stdio.h>
#ifdef make_lib
int main(int argc, char *argv[])
{
printf("Library\n");
}
#endif
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+4
View File
@@ -0,0 +1,4 @@
Rscons::Environment.new(echo: :command) do |env|
env.Object("one.o", "one.c", 'CPPFLAGS' => ['-DONE'])
env.Program('two_sources', ['one.o', 'two.c'])
end
+8
View File
@@ -0,0 +1,8 @@
#include <stdio.h>
#ifdef ONE
int main(int argc, char *argv[])
{
printf("This is a C program with two sources.\n");
}
#endif
+3
View File
@@ -0,0 +1,3 @@
#ifdef ONE
#error ONE should not be defined
#endif
+22
View File
@@ -8,13 +8,35 @@ require "rscons/monkey/module"
require "rscons/monkey/string" require "rscons/monkey/string"
# default builders # default builders
require "rscons/builders/library"
require "rscons/builders/object" require "rscons/builders/object"
require "rscons/builders/program" require "rscons/builders/program"
# Namespace module for rscons classes # Namespace module for rscons classes
module Rscons module Rscons
DEFAULT_BUILDERS = [ DEFAULT_BUILDERS = [
Library,
Object, Object,
Program, Program,
] ]
class BuildError < Exception
end
# Remove all generated files
def self.clean
cache = Cache.new
# remove all built files
cache.targets.each do |target|
FileUtils.rm_f(target)
end
# remove all created directories if they are empty
cache.directories.sort {|a, b| b.size <=> a.size}.each do |directory|
next unless File.directory?(directory)
if (Dir.entries(directory) - ['.', '..']).empty?
Dir.rmdir(directory) rescue nil
end
end
Cache.clear
end
end end
+15
View File
@@ -1,3 +1,5 @@
require "fileutils"
module Rscons module Rscons
# 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
@@ -16,5 +18,18 @@ module Rscons
def produces?(target, source, env) def produces?(target, source, env)
false false
end end
# Check if the cache is up to date for the target and if not execute the
# build command.
# Return the name of the target or false on failure.
def standard_build(short_cmd_string, target, command, sources, env, cache)
unless cache.up_to_date?(target, command, sources)
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)
end
target
end
end end
end end
+28
View File
@@ -0,0 +1,28 @@
require 'fileutils'
module Rscons
# A default RScons builder that produces a static library archive.
class Library < Builder
def default_variables(env)
{
'AR' => 'ar',
'LIBSUFFIX' => '.a',
'ARFLAGS' => [],
'ARCOM' => ['$AR', 'rcs', '$ARFLAGS', '$_TARGET', '$_SOURCES']
}
end
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
vars = vars.merge({
'_TARGET' => target,
'_SOURCES' => objects,
})
command = env.build_command(env['ARCOM'], vars)
standard_build("AR #{target}", target, command, objects, env, cache)
end
end
end
end
+17 -15
View File
@@ -11,8 +11,8 @@ module Rscons
'ASSUFFIX' => '.S', 'ASSUFFIX' => '.S',
'ASPPPATH' => '$CPPPATH', 'ASPPPATH' => '$CPPPATH',
'ASPPFLAGS' => '$CPPFLAGS', 'ASPPFLAGS' => '$CPPFLAGS',
'ASDEPGEN' => ['-MMD', '-MF', '$DEPFILE'], 'ASDEPGEN' => ['-MMD', '-MF', '$_DEPFILE'],
'ASCOM' => ['$AS', '-c', '-o', '$TARGET', '$ASDEPGEN', '-I$[ASPPPATH]', '$ASPPFLAGS', '$ASFLAGS', '$SOURCES'], 'ASCOM' => ['$AS', '-c', '-o', '$_TARGET', '$ASDEPGEN', '-I$[ASPPPATH]', '$ASPPFLAGS', '$ASFLAGS', '$_SOURCES'],
'CPPFLAGS' => [], 'CPPFLAGS' => [],
'CPPPATH' => [], 'CPPPATH' => [],
@@ -20,14 +20,14 @@ module Rscons
'CC' => 'gcc', 'CC' => 'gcc',
'CFLAGS' => [], 'CFLAGS' => [],
'CSUFFIX' => '.c', 'CSUFFIX' => '.c',
'CCDEPGEN' => ['-MMD', '-MF', '$DEPFILE'], 'CCDEPGEN' => ['-MMD', '-MF', '$_DEPFILE'],
'CCCOM' => ['$CC', '-c', '-o', '$TARGET', '$CCDEPGEN', '-I$[CPPPATH]', '$CPPFLAGS', '$CFLAGS', '$SOURCES'], 'CCCOM' => ['$CC', '-c', '-o', '$_TARGET', '$CCDEPGEN', '-I$[CPPPATH]', '$CPPFLAGS', '$CFLAGS', '$_SOURCES'],
'CXX' => 'g++', 'CXX' => 'g++',
'CXXFLAGS' => [], 'CXXFLAGS' => [],
'CXXSUFFIX' => '.cc', 'CXXSUFFIX' => '.cc',
'CXXDEPGEN' => ['-MMD', '-MF', '$DEPFILE'], 'CXXDEPGEN' => ['-MMD', '-MF', '$_DEPFILE'],
'CXXCOM' =>['$CXX', '-c', '-o', '$TARGET', '$CXXDEPGEN', '-I$[CPPPATH]', '$CPPFLAGS', '$CXXFLAGS', '$SOURCES'], 'CXXCOM' =>['$CXX', '-c', '-o', '$_TARGET', '$CXXDEPGEN', '-I$[CPPPATH]', '$CPPFLAGS', '$CXXFLAGS', '$_SOURCES'],
} }
end end
@@ -38,12 +38,12 @@ module Rscons
source.has_suffix?(env['CXXSUFFIX'])) source.has_suffix?(env['CXXSUFFIX']))
end end
def run(target, sources, cache, env) def run(target, sources, cache, env, vars = {})
vars = { vars = vars.merge({
'TARGET' => target, '_TARGET' => target,
'SOURCES' => sources, '_SOURCES' => sources,
'DEPFILE' => target.set_suffix('.mf'), '_DEPFILE' => target.set_suffix('.mf'),
} })
com_prefix = if sources.first.has_suffix?(env['ASSUFFIX']) com_prefix = if sources.first.has_suffix?(env['ASSUFFIX'])
'AS' 'AS'
elsif sources.first.has_suffix?(env['CSUFFIX']) elsif sources.first.has_suffix?(env['CSUFFIX'])
@@ -55,11 +55,13 @@ module Rscons
end end
command = env.build_command(env["#{com_prefix}COM"], vars) command = env.build_command(env["#{com_prefix}COM"], vars)
unless cache.up_to_date?(target, command, sources) 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) return false unless env.execute("#{com_prefix} #{target}", command)
deps = sources deps = sources
if File.exists?(vars['DEPFILE']) if File.exists?(vars['_DEPFILE'])
deps += env.parse_makefile_deps(vars['DEPFILE'], target) deps += env.parse_makefile_deps(vars['_DEPFILE'], target)
FileUtils.rm_f(vars['DEPFILE']) FileUtils.rm_f(vars['_DEPFILE'])
end end
cache.register_build(target, command, deps.uniq) cache.register_build(target, command, deps.uniq)
end end
+9 -22
View File
@@ -10,38 +10,25 @@ module Rscons
'LDFLAGS' => [], 'LDFLAGS' => [],
'LIBPATH' => [], 'LIBPATH' => [],
'LIBS' => [], 'LIBS' => [],
'LDCOM' => ['$LD', '-o', '$TARGET', '$LDFLAGS', '$SOURCES', '-L$[LIBPATH]', '-l$[LIBS]'] 'LDCOM' => ['$LD', '-o', '$_TARGET', '$LDFLAGS', '$_SOURCES', '-L$[LIBPATH]', '-l$[LIBS]']
} }
end end
def run(target, sources, cache, env) def run(target, sources, cache, env, vars = {})
# convert sources to object file names # build sources to linkable objects
objects = sources.map do |source| objects = env.build_sources(sources, [env['OBJSUFFIX'], env['LIBSUFFIX']].flatten, cache, vars)
if source.has_suffix?([env['OBJSUFFIX'], env['LIBSUFFIX']])
source
else
o_file = env.get_build_fname(source, env['OBJSUFFIX', :string])
builder = env.builders.values.find { |b| b.produces?(o_file, source, env) }
builder or raise "No builder found to convert input source #{source.inspect} to an object file."
builder.run(o_file, [source], cache, env) or break
end
end
if objects if objects
use_cxx = sources.map do |s| use_cxx = sources.map do |s|
s.has_suffix?(env['CXXSUFFIX']) s.has_suffix?(env['CXXSUFFIX'])
end.any? end.any?
ld_alt = use_cxx ? env['CXX'] : env['CC'] ld_alt = use_cxx ? env['CXX'] : env['CC']
vars = { vars = vars.merge({
'TARGET' => target, '_TARGET' => target,
'SOURCES' => objects, '_SOURCES' => objects,
'LD' => env['LD'] || ld_alt, 'LD' => env['LD'] || ld_alt,
} })
command = env.build_command(env['LDCOM'], vars) command = env.build_command(env['LDCOM'], vars)
unless cache.up_to_date?(target, command, objects) standard_build("LD #{target}", target, command, objects, env, cache)
return false unless env.execute("LD #{target}", command)
cache.register_build(target, command, objects)
end
target
end end
end end
end end
+40 -6
View File
@@ -36,6 +36,11 @@ module Rscons
# ] # ]
# } # }
# } # }
# directories: {
# 'build' => true,
# 'build/one' => true,
# 'build/two' => true,
# }
# } # }
class Cache class Cache
#### Constants #### Constants
@@ -55,10 +60,14 @@ module Rscons
# Create a Cache object and load in the previous contents from the cache # Create a Cache object and load in the previous contents from the cache
# file. # file.
def initialize def initialize
@cache = YAML.load(File.read(CACHE_FILE)) rescue { @cache = YAML.load(File.read(CACHE_FILE)) rescue {}
targets: {}, unless @cache.is_a?(Hash)
version: VERSION, $stderr.puts "Warning: #{CACHE_FILE} was corrupt. Contents:\n#{@cache.inspect}"
} @cache = {}
end
@cache[:targets] ||= {}
@cache[:directories] ||= {}
@cache[:version] ||= VERSION
@lookup_checksums = {} @lookup_checksums = {}
end end
@@ -120,18 +129,43 @@ module Rscons
# @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) def register_build(target, command, deps)
@cache[:targets][target] = { @cache[:targets][target.encode(__ENCODING__)] = {
command: command, command: command,
checksum: calculate_checksum(target), checksum: calculate_checksum(target),
deps: deps.map do |dep| deps: deps.map do |dep|
{ {
fname: dep, fname: dep.encode(__ENCODING__),
checksum: lookup_checksum(dep), checksum: lookup_checksum(dep),
} }
end end
} }
end end
# Return a list of targets that have been built
def targets
@cache[:targets].keys
end
# Make any needed directories and record the ones that are created for
# removal upon a "clean" operation.
def mkdir_p(path)
parts = path.split(/[\\\/]/)
(0..parts.size).each do |i|
subpath = File.join(*parts[0, i + 1]).encode(__ENCODING__)
unless File.exists?(subpath)
FileUtils.mkdir(subpath)
unless @cache[:directories].include?(subpath)
@cache[:directories][subpath] = true
end
end
end
end
# Return a list of directories which were created as a part of the build
def directories
@cache[:directories].keys
end
# Private Instance Methods # Private Instance Methods
private private
+42 -12
View File
@@ -6,14 +6,15 @@ 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
# [Array] of {Builder} objects. # [Hash] of {"builder_name" => builder_object} pairs.
attr_reader :builders attr_reader :builders
# Create an Environment object. # Create an Environment object.
# @param variables [Hash] # @param variables [Hash]
# The variables hash can contain both construction variables, which are # The variables hash can contain construction variables, which are
# uppercase strings (such as "CC" or "LDFLAGS"), and RScons options, # uppercase strings (such as "CC" or "LDFLAGS"), user variables, which
# which are lowercase symbols (such as :echo). # are lowercase strings (such as "sources"), and RScons options, which
# are lowercase symbols (such as :echo).
# 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(variables = {})
@@ -52,7 +53,7 @@ module Rscons
@builders.each do |builder_name, builder| @builders.each do |builder_name, builder|
env.add_builder(builder) env.add_builder(builder)
end end
env.append(@varset) env.append(@varset.clone)
env.append(variables) env.append(variables)
if block_given? if block_given?
@@ -77,7 +78,6 @@ module Rscons
# Source files from src_dir will produce object files under obj_dir. # Source files from src_dir will produce object files under obj_dir.
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) src_dir = src_dir.gsub('\\', '/') if src_dir.is_a?(String)
obj_dir = obj_dir.gsub('\\', '/')
@build_dirs << [src_dir, obj_dir] @build_dirs << [src_dir, obj_dir]
end end
@@ -91,11 +91,10 @@ module Rscons
if src_dir.is_a?(Regexp) if src_dir.is_a?(Regexp)
build_fname.sub!(src_dir, obj_dir) build_fname.sub!(src_dir, obj_dir)
else else
build_fname.sub!(/^#{src_dir}\//, "#{obj_dir}/") build_fname.sub!(%r{^#{src_dir}/}, "#{obj_dir}/")
end end
build_fname.gsub!('\\', '/') build_fname.gsub!('\\', '/')
end end
FileUtils.mkdir_p(File.dirname(build_fname))
build_fname build_fname
end end
@@ -141,7 +140,7 @@ module Rscons
@targets[target][:source], @targets[target][:source],
cache, cache,
self, self,
*@targets[target][:args]) @targets[target][:vars] || {})
else else
false false
end end
@@ -149,8 +148,8 @@ module Rscons
@targets.each do |target, info| @targets.each do |target, info|
next if targets_processed.include?(target) next if targets_processed.include?(target)
unless process_target.call(target) unless process_target.call(target)
$stderr.puts "Error: failed to build #{target}" cache.write
break raise BuildError.new("Failed to build #{target}")
end end
end end
cache.write cache.write
@@ -193,11 +192,15 @@ module Rscons
alias_method :orig_method_missing, :method_missing alias_method :orig_method_missing, :method_missing
def method_missing(method, *args) def method_missing(method, *args)
if @builders.has_key?(method.to_s) if @builders.has_key?(method.to_s)
target, source, *rest = args target, source, vars, *rest = args
unless vars.nil? or vars.is_a?(Hash) or vars.is_a?(VarSet)
raise "Unexpected construction variable set: #{vars.inspect}"
end
source = [source] unless source.is_a?(Array) source = [source] unless source.is_a?(Array)
@targets[target] = { @targets[target] = {
builder: @builders[method.to_s], builder: @builders[method.to_s],
source: source, source: source,
vars: vars,
args: rest, args: rest,
} }
else else
@@ -228,5 +231,32 @@ module Rscons
end end
deps deps
end 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.
# @param sources [Array] List of source files to build.
# @param suffixes [Array] List of suffixes to try to convert source files into.
# @param cache [Cache] The Cache.
# Return a list of the converted file names.
def build_sources(sources, suffixes, cache, vars = {})
sources.map do |source|
if source.has_suffix?(suffixes)
source
else
converted = nil
suffixes.each do |suffix|
converted_fname = get_build_fname(source, suffix)
builder = @builders.values.find { |b| b.produces?(converted_fname, source, self) }
if builder
converted = builder.run(converted_fname, [source], cache, self, vars)
return nil unless converted
break
end
end
converted or raise "Could not find a builder to handle #{source.inspect}."
end
end
end
end end
end end
+6 -2
View File
@@ -8,8 +8,12 @@ module Rscons
# Create a VarSet # Create a VarSet
# @param vars [Hash] Optional initial variables. # @param vars [Hash] Optional initial variables.
def initialize(vars = {}) def initialize(vars = {})
if vars.is_a?(VarSet)
@vars = vars.clone.vars
else
@vars = vars @vars = vars
end end
end
# 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.
@@ -20,9 +24,9 @@ module Rscons
# returned. # returned.
def [](key, type = nil) def [](key, type = nil)
val = @vars[key] val = @vars[key]
if type == :array and val.is_a?(String) if type == :array and not val.is_a?(Array)
[val] [val]
elsif type == :string and val.is_a?(Array) elsif type == :scalar and val.is_a?(Array)
val.first val.first
else else
val val
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons module Rscons
# gem version # gem version
VERSION = "0.0.3" VERSION = "0.0.12"
end end
+72 -8
View File
@@ -13,12 +13,30 @@ describe Rscons do
def build_testdir def build_testdir
if File.exists?("build.rb") if File.exists?("build.rb")
system("ruby -I #{@owd}/lib -r rscons build.rb > build.out") build_rb = File.read("build.rb")
File.open("build.rb", "w") do |fh|
fh.puts(<<EOF + build_rb)
require "simplecov"
SimpleCov.start do
root("#{@owd}")
command_name("build_test_#{@build_test_name}")
add_filter("spec")
end
require "rscons"
EOF
end
IO.popen(%{ruby -I #{@owd}/lib build.rb}) do |io|
io.readlines.reject do |line|
line =~ /^Coverage report/
end
end.map(&:strip)
end end
get_build_output
end end
def test_dir(build_test_directory) def test_dir(build_test_directory)
@build_test_name = build_test_directory
FileUtils.cp_r("build_tests/#{build_test_directory}", 'build_tests_run') FileUtils.cp_r("build_tests/#{build_test_directory}", 'build_tests_run')
Dir.chdir("build_tests_run") Dir.chdir("build_tests_run")
build_testdir build_testdir
@@ -35,10 +53,6 @@ describe Rscons do
end end
end end
def get_build_output
File.read('build.out').lines.map(&:strip)
end
########################################################################### ###########################################################################
# Tests # Tests
########################################################################### ###########################################################################
@@ -116,8 +130,35 @@ describe Rscons do
it 'builds object files in a different build directory' do it 'builds object files in a different build directory' do
lines = test_dir('build_dir') lines = test_dir('build_dir')
`./build_dir`.should == "Hello from two()\n" `./build_dir`.should == "Hello from two()\n"
File.exists?('build/one/one.o').should be_true File.exists?('build_one/one.o').should be_true
File.exists?('build/two/two.o').should be_true File.exists?('build_two/two.o').should be_true
end
it 'cleans built files' do
lines = test_dir('build_dir')
`./build_dir`.should == "Hello from two()\n"
File.exists?('build_one/one.o').should be_true
File.exists?('build_two/two.o').should be_true
Rscons.clean
File.exists?('build_one/one.o').should be_false
File.exists?('build_two/two.o').should be_false
File.exists?('build_one').should be_false
File.exists?('build_two').should be_false
File.exists?('src/one/one.c').should be_true
end
it 'does not clean created directories if other non-rscons-generated files reside there' do
lines = test_dir('build_dir')
`./build_dir`.should == "Hello from two()\n"
File.exists?('build_one/one.o').should be_true
File.exists?('build_two/two.o').should be_true
File.open('build_two/tmp', 'w') { |fh| fh.puts "dum" }
Rscons.clean
File.exists?('build_one/one.o').should be_false
File.exists?('build_two/two.o').should be_false
File.exists?('build_one').should be_false
File.exists?('build_two').should be_true
File.exists?('src/one/one.c').should be_true
end end
it 'allows Ruby classes as custom builders to be used to construct files' do it 'allows Ruby classes as custom builders to be used to construct files' do
@@ -143,4 +184,27 @@ describe Rscons do
`./simple`.should == "This is a simple C++ program\n" `./simple`.should == "This is a simple C++ program\n"
end end
it 'allows overriding construction variables for individual builder calls' do
lines = test_dir('two_sources')
lines.should == [
'gcc -c -o one.o -MMD -MF one.mf -DONE one.c',
'gcc -c -o two.o -MMD -MF two.mf two.c',
'gcc -o two_sources one.o two.o',
]
File.exists?('two_sources').should be_true
`./two_sources`.should == "This is a C program with two sources.\n"
end
it 'builds a static library archive' do
lines = test_dir('library')
lines.should == [
'gcc -c -o one.o -MMD -MF one.mf -Dmake_lib one.c',
'gcc -c -o two.o -MMD -MF two.mf -Dmake_lib two.c',
'ar rcs lib.a one.o two.o',
'gcc -c -o three.o -MMD -MF three.mf three.c',
'gcc -o library lib.a three.o',
]
File.exists?('library').should be_true
`ar t lib.a`.should == "one.o\ntwo.o\n"
end
end end
+11
View File
@@ -1,5 +1,16 @@
module Rscons module Rscons
describe Environment do describe Environment do
describe '.clone' do
it 'should create unique copies of each construction variable' do
env = Environment.new
env["CPPPATH"] << "path1"
env2 = env.clone
env2["CPPPATH"] << "path2"
env["CPPPATH"].should == ["path1"]
env2["CPPPATH"].should == ["path1", "path2"]
end
end
describe '.parse_makefile_deps' do describe '.parse_makefile_deps' do
it 'handles dependencies on one line' do it 'handles dependencies on one line' do
File.should_receive(:read).with('makefile').and_return(<<EOS) File.should_receive(:read).with('makefile').and_return(<<EOS)
+6
View File
@@ -0,0 +1,6 @@
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
+114
View File
@@ -0,0 +1,114 @@
module Rscons
describe VarSet do
describe '.initialize' do
it "initializes variables from a Hash" do
v = VarSet.new({"one" => 1, "two" => :two})
v["one"].should == 1
v["two"].should == :two
end
it "initializes variables from another VarSet" do
v = VarSet.new({"one" => 1})
v2 = VarSet.new(v)
v2["one"].should == 1
end
it "makes a deep copy of the given VarSet" do
v = VarSet.new({"array" => [1, 2, 3]})
v2 = VarSet.new(v)
v["array"] << 4
v["array"].should == [1, 2, 3, 4]
v2["array"].should == [1, 2, 3]
end
end
describe :[] do
v = VarSet.new({"fuz" => "a string", "foo" => 42, "bar" => :baz,
"qax" => [3, 6], "qux" => {a: :b}})
it "allows accessing a variable with its verbatim value if type is not specified" do
v["fuz"].should == "a string"
v["foo"].should == 42
v["bar"].should == :baz
v["qax"].should == [3, 6]
v["qux"].should == {a: :b}
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
describe :[]= do
it "allows assigning to variables" do
v = VarSet.new("CFLAGS" => ["-Wall", "-O3"])
v["CPPPATH"] = ["one", "two"]
v["CFLAGS"].should == ["-Wall", "-O3"]
v["CPPPATH"].should == ["one", "two"]
end
end
describe '.append' do
it "adds values from a Hash to the VarSet" do
v = VarSet.new("LDFLAGS" => "-lgcc")
v.append("LIBS" => "gcc", "LIBPATH" => ["mylibs"])
v.vars.keys.should =~ ["LDFLAGS", "LIBS", "LIBPATH"]
end
it "adds values from another VarSet to the VarSet" do
v = VarSet.new("CPPPATH" => ["mydir"])
v2 = VarSet.new("CFLAGS" => ["-O0"], "CPPPATH" => ["different_dir"])
v.append(v2)
v.vars.keys.should =~ ["CPPPATH", "CFLAGS"]
v["CPPPATH"].should == ["different_dir"]
end
end
describe '.merge' do
it "returns a new VarSet merged with the given Hash" do
v = VarSet.new("foo" => "yoda")
v2 = v.merge("baz" => "qux")
v.vars.keys.should == ["foo"]
v2.vars.keys.should =~ ["foo", "baz"]
end
it "returns a new VarSet merged with the given VarSet" do
v = VarSet.new("foo" => ["a", "b"], "bar" => 42)
v2 = v.merge(VarSet.new("bar" => 33, "baz" => :baz))
v2["foo"] << "c"
v["foo"].should == ["a", "b"]
v["bar"].should == 42
v2["foo"].should == ["a", "b", "c"]
v2["bar"].should == 33
end
end
describe '.expand_varref' do
v = VarSet.new("CFLAGS" => ["-Wall", "-O2"],
"CC" => "gcc",
"CPPPATH" => ["dir1", "dir2"],
"compiler" => "$CC",
"cmd" => ["$CC", "-c", "$CFLAGS", "-I$[CPPPATH]"])
it "expands to the string itself if the string is not a variable reference" do
v.expand_varref("CC").should == "CC"
v.expand_varref("CPPPATH").should == "CPPPATH"
v.expand_varref("str").should == "str"
end
it "expands a single variable reference beginning with a '$'" do
v.expand_varref("$CC").should == "gcc"
v.expand_varref("$CPPPATH").should == ["dir1", "dir2"]
end
it "expands a single variable reference in $[arr] notation" do
v.expand_varref("prefix$[CFLAGS]suffix").should == ["prefix-Wallsuffix", "prefix-O2suffix"]
v.expand_varref(v["cmd"]).should == ["gcc", "-c", "-Wall", "-O2", "-Idir1", "-Idir2"]
end
it "expands a variable reference recursively" do
v.expand_varref("$compiler").should == "gcc"
v.expand_varref("$cmd").should == ["gcc", "-c", "-Wall", "-O2", "-Idir1", "-Idir2"]
end
it "raises an error when array notation is applied to a non-array variable" do
expect { v.expand_varref("$[CC]") }.to raise_error /Array.expected/
end
it "raises an error when a variable reference refers to a non-existent variable" do
expect { v.expand_varref("$not_here") }.to raise_error /Could.not.find.variable..not_here/
end
end
end
end
+4
View File
@@ -1 +1,5 @@
require "simplecov"
SimpleCov.start
require "rscons" require "rscons"