Compare commits

...
32 Commits
Author SHA1 Message Date
josh f33be07a15 v1.9.3 2016-09-30 14:03:35 -04:00
josh c4c2d5c128 Environment#parse_flags should put -std=XXX flags in CCFLAGS, not CFLAGS 2016-09-30 14:02:30 -04:00
josh 3065dc48ec document passing multiple dependencies to env.depends() 2016-05-02 09:10:14 -04:00
josh 9ca2934695 document explicit dependencies 2016-03-02 13:33:00 -05:00
josh 3af6b6be1b v1.9.2 2016-01-12 10:16:00 -05:00
josh 0442b876a5 allow phony targets in conjunction with a build root 2016-01-12 10:14:10 -05:00
josh bab4641b6e v1.9.1 2015-12-10 15:25:29 -05:00
josh 4698fc41b9 add v1.9.1 release notes 2015-12-10 15:25:16 -05:00
josh 952f423860 use ${INCPREFIX} instead of hard-coded "-I" in Preprocess builder 2015-12-10 15:19:41 -05:00
josh d2da5f7b0d bundle update 2015-12-10 15:18:59 -05:00
josh ab14dc16ed change *SUFFIX defaults to arrays; add various C++ file suffixes 2015-06-15 21:58:37 -04:00
josh aa795eea85 v1.9.0 2015-03-15 20:56:37 -04:00
josh 7dffef6bd0 update README.md 2015-02-15 19:52:33 -05:00
josh dce2114733 document Copy builder class 2015-02-15 18:42:37 -05:00
josh 0bbdda2d41 fix Command builder default short description 2015-02-15 18:41:41 -05:00
josh 3b8910075a use class instance variables instead of class variables 2015-02-15 18:38:09 -05:00
josh 34beda844e reorganize Rscons module class methods 2015-02-15 18:33:00 -05:00
josh 7a04bec2ff support phony targets - close #25 2015-02-15 18:29:33 -05:00
josh 7698fbf618 add "rscons" binary - close #24 2015-02-08 18:04:37 -05:00
josh 4eedea2563 add Copy builder as a copy of Install 2015-02-07 09:57:44 -05:00
josh f678803a5d add Install builder - close #6 2015-02-07 09:49:29 -05:00
josh 9c49a09f69 update :spec rake task to allow example pattern as an argument 2015-01-29 09:58:09 -05:00
josh 859f87ddee add a Directory builder - close #27 2015-01-29 09:31:00 -05:00
josh 73bc71a656 test expanding paths after a build target is registered from within a build hook 2015-01-28 17:15:55 -05:00
josh 29a8684f90 support registering multiple build targets with the same path - close #26 2015-01-28 17:10:36 -05:00
josh 02aff35222 allow overriding Command builder short description with CMD_DESC variable -- close #22 2015-01-24 09:40:07 -05:00
josh 897d31ebe0 fix Command builder example invocation 2015-01-21 14:07:28 -05:00
josh 76610a07a9 fix variable references that expand to arrays in build target sources - fix #20 2015-01-20 21:26:45 -05:00
josh cb3cc9a0de update 1.9.0 release notes 2015-01-20 21:18:19 -05:00
josh 0ea842fffe rework Preprocess builder to consider deep dependencies - fix #21 2015-01-20 21:16:21 -05:00
josh d28722a4bb fix Rscons.set_suffix to append the given suffix if the filename has none 2015-01-20 21:13:27 -05:00
josh 8142d0bbc8 add v1.8.1 release notes 2015-01-20 21:12:57 -05:00
23 changed files with 726 additions and 114 deletions

No files matched your search

+25 -22
View File
@@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
rscons (1.8.1) rscons (1.9.3)
json (~> 1.0) json (~> 1.0)
GEM GEM
@@ -9,30 +9,30 @@ GEM
specs: specs:
diff-lcs (1.2.5) diff-lcs (1.2.5)
docile (1.1.5) docile (1.1.5)
json (1.8.1) json (1.8.3)
multi_json (1.10.1) rake (10.4.2)
rake (10.3.2) rdoc (4.2.0)
rdoc (4.1.2)
json (~> 1.4) json (~> 1.4)
redcarpet (3.1.2) redcarpet (3.3.3)
rspec (3.0.0) rspec (3.4.0)
rspec-core (~> 3.0.0) rspec-core (~> 3.4.0)
rspec-expectations (~> 3.0.0) rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.0.0) rspec-mocks (~> 3.4.0)
rspec-core (3.0.1) rspec-core (3.4.1)
rspec-support (~> 3.0.0) rspec-support (~> 3.4.0)
rspec-expectations (3.0.1) rspec-expectations (3.4.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.0.0) rspec-support (~> 3.4.0)
rspec-mocks (3.0.1) rspec-mocks (3.4.0)
rspec-support (~> 3.0.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (3.0.0) rspec-support (~> 3.4.0)
simplecov (0.8.2) rspec-support (3.4.1)
simplecov (0.11.1)
docile (~> 1.1.0) docile (~> 1.1.0)
multi_json json (~> 1.8)
simplecov-html (~> 0.8.0) simplecov-html (~> 0.10.0)
simplecov-html (0.8.0) simplecov-html (0.10.0)
yard (0.8.7.4) yard (0.8.7.6)
PLATFORMS PLATFORMS
ruby ruby
@@ -46,3 +46,6 @@ DEPENDENCIES
rspec rspec
simplecov simplecov
yard yard
BUNDLED WITH
1.10.6
+87 -13
View File
@@ -1,6 +1,7 @@
# Rscons # Rscons
Software construction library inspired by SCons and implemented in Ruby Rscons is a software construction framework inspired by SCons and implemented
in Ruby.
[![Gem Version](https://badge.fury.io/rb/rscons.png)](http://badge.fury.io/rb/rscons) [![Gem Version](https://badge.fury.io/rb/rscons.png)](http://badge.fury.io/rb/rscons)
@@ -20,9 +21,10 @@ Or install it yourself as:
## Usage ## Usage
Rscons is a Ruby library. Rscons is implemented as a Ruby library and distributed as a gem.
It can be called from a standalone Ruby script or it can be used with rake and It also provides a "rscons" executable which can evaluate a build script (by
called from your Rakefile. default named Rsconsfile or Rsconsfile.rb).
It can also be used from rake or from a standalone Ruby script.
### Example: Building a C Program ### Example: Building a C Program
@@ -235,13 +237,15 @@ end
Rscons ships with a number of builders: Rscons ships with a number of builders:
* Command, which executes a user-defined command to produce the target * Command, which executes a user-defined command to produce the target.
* CFile, which builds a C or C++ source file from a lex or yacc input file * Copy, which is identical to Install.
* Disassemble, which disassembles an object file to a disassembly listing * CFile, which builds a C or C++ source file from a lex or yacc input file.
* Library, which collects object files into a static library archive file * Disassemble, which disassembles an object file to a disassembly listing.
* Object, which compiles source files to produce an object file * Install, which installs files or directories to a specified destination.
* Preprocess, which invokes the C/C++ preprocessor on a source file * Library, which collects object files into a static library archive file.
* Program, which links object files to produce an executable * Object, which compiles source files to produce an object file.
* Preprocess, which invokes the C/C++ preprocessor on a source file.
* Program, which links object files to produce an executable.
If you want to create an Environment that does not contain any builders, If you want to create an Environment that does not contain any builders,
you can use the `exclude_builders` key to the Environment constructor. you can use the `exclude_builders` key to the Environment constructor.
@@ -249,10 +253,11 @@ you can use the `exclude_builders` key to the Environment constructor.
#### Command #### Command
```ruby ```ruby
env.Command(target, sources, 'CMD' => command) env.Command(target, sources, "CMD" => command)
# Example # Example
env.Command("docs.html", "docs.md", env.Command("docs.html", "docs.md",
CMD => ['pandoc', '-fmarkdown', '-thtml', '-o${_TARGET}', '${_SOURCES}']) "CMD" => ["pandoc", "-fmarkdown", "-thtml", "-o${_TARGET}", "${_SOURCES}"],
"CMD_DESC" => "PANDOC")
``` ```
The command builder executes a user-defined command in order to produce the The command builder executes a user-defined command in order to produce the
@@ -280,6 +285,15 @@ env.Disassemble("module.dis", "module.o")
The Disassemble builder generates a disassembly listing using objdump from The Disassemble builder generates a disassembly listing using objdump from
and object file. and object file.
#### Install
```ruby
env.Install(destination, sources)
# Example
env.Install("dist/bin", "app.exe")
env.Install("dist/share", "share")
```
#### Library #### Library
```ruby ```ruby
@@ -371,6 +385,34 @@ 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 Post-build hooks can invoke commands using the newly-built files, or register
new build targets. new build targets.
### Phony Targets
A build target name given as a Symbol instead of a String is interpreted as a
"phony" target.
Phony targets operate similarly to normal build targets, except that a file is
not expected to be produced by the builder.
Phony targets will still be "rebuilt" if any source or the command is out of
date.
### Explicit Dependencies
A target can be marked as depending on another file that Rscons would not
otherwise know about via the `Environment#depends` function. For example,
to force the linker to re-link a Program output when a linker script changes:
```ruby
Rscons::Environment.new do |env|
env.Program("a.out", "foo.c", "LDFLAGS" => %w[-T linker_script.ld])
env.depends("a.out", "linker_script.ld")
end
```
You can pass multiple dependency files to `Environment#depends`:
```ruby
env.depends("my_app", "config/link.ld", "README.txt", *Dir.glob("assets/**/*"))
```
### Construction Variable Naming ### Construction Variable Naming
* uppercase strings - the default construction variables that Rscons uses * uppercase strings - the default construction variables that Rscons uses
@@ -383,6 +425,38 @@ http://rubydoc.info/github/holtrop/rscons/frames.
## Release Notes ## Release Notes
### v1.9.2
- allow phony targets in conjunction with build roots
### v1.9.1
- change *SUFFIX defaults to arrays
- add various C++ file suffixes
- use ${INCPREFIX} instead of hard-coded "-I" in Preprocess builder
### v1.9.0
#### New Features
- #6 - add Install and Copy builders
- #22 - allow overriding Command builder short description with CMD_DESC variable
- #24 - add "rscons" executable
- #25 - add support for phony targets given as Symbols instead of Strings
- #26 - support registering multiple build targets with the same target name
- #27 - add Directory builder
#### Fixes
- #20 - fix variable references that expand to arrays in build target sources
- #21 - rework Preprocess builder to consider deep dependencies
- fix Rscons.set_suffix to append the given suffix if the filename has none
- remove ${CFLAGS} from default CPP_CMD
### v1.8.1
- fix Environment#dump when construction variables are symbols
### v1.8.0 ### v1.8.0
- new Command builder to execute arbitrary user commands - new Command builder to execute arbitrary user commands
+5 -1
View File
@@ -13,7 +13,11 @@ require "rake/clean"
CLEAN.include %w[build_test_run .yardoc doc coverage] CLEAN.include %w[build_test_run .yardoc doc coverage]
CLOBBER.include %w[pkg] CLOBBER.include %w[pkg]
RSpec::Core::RakeTask.new(:spec) RSpec::Core::RakeTask.new(:spec, :example_string) do |task, args|
if args.example_string
task.rspec_opts = %W[-e "#{args.example_string}" -f documentation]
end
end
YARD::Rake::YardocTask.new do |yard| YARD::Rake::YardocTask.new do |yard|
yard.files = ['lib/**/*.rb'] yard.files = ['lib/**/*.rb']
Executable
+3
View File
@@ -0,0 +1,3 @@
require "rscons/cli"
Rscons::Cli.run(ARGV)
+1
View File
@@ -0,0 +1 @@
#define BAR xyz42abc
+2
View File
@@ -0,0 +1,2 @@
#include "bar.h"
BAR
+31 -12
View File
@@ -6,9 +6,11 @@ require_relative "rscons/varset"
require_relative "rscons/version" require_relative "rscons/version"
# default builders # default builders
require_relative "rscons/builders/command"
require_relative "rscons/builders/cfile" require_relative "rscons/builders/cfile"
require_relative "rscons/builders/command"
require_relative "rscons/builders/directory"
require_relative "rscons/builders/disassemble" require_relative "rscons/builders/disassemble"
require_relative "rscons/builders/install"
require_relative "rscons/builders/library" require_relative "rscons/builders/library"
require_relative "rscons/builders/object" require_relative "rscons/builders/object"
require_relative "rscons/builders/preprocess" require_relative "rscons/builders/preprocess"
@@ -17,12 +19,16 @@ require_relative "rscons/builders/simple_builder"
# Namespace module for rscons classes # Namespace module for rscons classes
module Rscons module Rscons
# Names of the default builders which will be added to all newly created # Names of the default builders which will be added to all newly created
# {Environment} objects. # {Environment} objects.
DEFAULT_BUILDERS = [ DEFAULT_BUILDERS = [
:Command,
:CFile, :CFile,
:Command,
:Copy,
:Directory,
:Disassemble, :Disassemble,
:Install,
:Library, :Library,
:Object, :Object,
:Preprocess, :Preprocess,
@@ -32,10 +38,12 @@ module Rscons
# Class to represent a fatal error while building a target. # Class to represent a fatal error while building a target.
class BuildError < RuntimeError; end class BuildError < RuntimeError; end
class << self
# Remove all generated files. # Remove all generated files.
# #
# @return [void] # @return [void]
def self.clean def clean
cache = Cache.instance cache = Cache.instance
# remove all built files # remove all built files
cache.targets.each do |target| cache.targets.each do |target|
@@ -56,25 +64,34 @@ module Rscons
# @param path [String] the path to examine. # @param path [String] the path to examine.
# #
# @return [Boolean] Whether the given path is an absolute filesystem path. # @return [Boolean] Whether the given path is an absolute filesystem path.
def self.absolute_path?(path) def absolute_path?(path)
path =~ %r{^(/|\w:[\\/])} path =~ %r{^(/|\w:[\\/])}
end end
# Return whether the given target is a phony target.
#
# @param target [Symbol, String] Target name.
#
# @return [Boolean] Whether the given target is a phony target.
def phony_target?(target)
target.is_a?(Symbol)
end
# Return a new path by changing the suffix in path to suffix. # Return a new path by changing the suffix in path to suffix.
# #
# @param path [String] The path to alter. # @param path [String] The path to alter.
# @param suffix [String] The new filename suffix, e.g. ".exe". # @param suffix [String] The new filename suffix, e.g. ".exe".
# #
# @return [String] New path. # @return [String] New path.
def self.set_suffix(path, suffix) def set_suffix(path, suffix)
path.sub(/\.[^.]*$/, suffix) path.sub(/\.[^.]*$/, "") + suffix
end end
# Return the system shell and arguments for executing a shell command. # Return the system shell and arguments for executing a shell command.
# #
# @return [Array<String>] The shell and flag. # @return [Array<String>] The shell and flag.
def self.get_system_shell def get_system_shell
@@shell ||= @shell ||=
begin begin
test_shell = lambda do |*args| test_shell = lambda do |*args|
begin begin
@@ -106,8 +123,8 @@ module Rscons
# that it is running in MSYS then ["env"] will be returned. # that it is running in MSYS then ["env"] will be returned.
# #
# @return [Array<String>] Command used to execute commands. # @return [Array<String>] Command used to execute commands.
def self.command_executer def command_executer
@@command_executer ||= @command_executer ||=
if Object.const_get("RUBY_PLATFORM") =~ /mingw/ if Object.const_get("RUBY_PLATFORM") =~ /mingw/
if ENV.keys.find {|key| key =~ /MSYS/} if ENV.keys.find {|key| key =~ /MSYS/}
begin begin
@@ -125,8 +142,10 @@ module Rscons
# @param val [Array<String>] Command used to execute commands. # @param val [Array<String>] Command used to execute commands.
# #
# @return [Array<String>] Command used to execute commands. # @return [Array<String>] Command used to execute commands.
def self.command_executer=(val) def command_executer=(val)
@@command_executer = val @command_executer = val
end
end end
end end
+2
View File
@@ -87,8 +87,10 @@ module Rscons
# The name of the target on success or false on failure. # The name of the target on success 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, env) unless cache.up_to_date?(target, command, sources, env)
unless Rscons.phony_target?(target)
cache.mkdir_p(File.dirname(target)) cache.mkdir_p(File.dirname(target))
FileUtils.rm_f(target) FileUtils.rm_f(target)
end
return false unless env.execute(short_cmd_string, command) return false unless env.execute(short_cmd_string, command)
cache.register_build(target, command, sources, env) cache.register_build(target, command, sources, env)
end end
+2 -1
View File
@@ -25,7 +25,8 @@ module Rscons
"_SOURCES" => sources, "_SOURCES" => sources,
}) })
command = env.build_command("${CMD}", vars) command = env.build_command("${CMD}", vars)
standard_build("CMD #{target}", target, command, sources, env, cache) cmd_desc = vars["CMD_DESC"] || "Command"
standard_build("#{cmd_desc} #{target}", target, command, sources, env, cache)
end end
end end
end end
+31
View File
@@ -0,0 +1,31 @@
module Rscons
module Builders
# The Directory builder creates a directory.
class Directory < Builder
# Run the builder to produce a build target.
#
# @param target [String] Target file name.
# @param sources [Array<String>] Source file name(s).
# @param cache [Cache] The Cache object.
# @param env [Environment] The Environment executing the builder.
# @param vars [Hash,VarSet] Extra construction variables.
#
# @return [String,false]
# Name of the target file on success or false on failure.
def run(target, sources, cache, env, vars)
if File.directory?(target)
target
elsif File.exists?(target)
$stderr.puts "Error: `#{target}' already exists and is not a directory"
false
else
puts "Directory #{target}"
cache.mkdir_p(target)
target
end
end
end
end
end
+64
View File
@@ -0,0 +1,64 @@
require "pathname"
module Rscons
module Builders
# The Install builder copies files/directories to new locations.
class Install < Builder
# Run the builder to produce a build target.
#
# @param target [String] Target file name.
# @param sources [Array<String>] Source file name(s).
# @param cache [Cache] The Cache object.
# @param env [Environment] The Environment executing the builder.
# @param vars [Hash,VarSet] Extra construction variables.
#
# @return [String,false]
# Name of the target file on success or false on failure.
def run(target, sources, cache, env, vars)
target_is_dir = (sources.length > 1) ||
Dir.exists?(sources.first) ||
Dir.exists?(target)
outdir = target_is_dir ? target : File.dirname(target)
# Collect the list of files to copy over.
file_map = {}
if target_is_dir
sources.each do |src|
if Dir.exists? src
Dir.glob("#{src}/**/*", File::FNM_DOTMATCH).select do |f|
File.file?(f)
end.each do |subfile|
subpath = Pathname.new(subfile).relative_path_from(Pathname.new(src)).to_s
file_map[subfile] = "#{outdir}/#{subpath}"
end
else
file_map[src] = "#{outdir}/#{File.basename(src)}"
end
end
else
file_map[sources.first] = target
end
printed_message = false
file_map.each do |src, dest|
# Check the cache and copy if necessary
unless cache.up_to_date?(dest, :Copy, [src], env)
unless printed_message
puts "#{name} #{target}"
printed_message = true
end
cache.mkdir_p(File.dirname(dest))
FileUtils.cp(src, dest, :preserve => true)
end
cache.register_build(dest, :Copy, [src], env)
end
target if (target_is_dir ? Dir.exists?(target) : File.exists?(target))
end
end
# The Copy builder is identical to the Install builder.
class Copy < Install; end
end
end
+5 -5
View File
@@ -18,7 +18,7 @@ module Rscons
# @return [Hash] Default construction variables for the builder. # @return [Hash] Default construction variables for the builder.
def default_variables(env) def default_variables(env)
{ {
'OBJSUFFIX' => '.o', 'OBJSUFFIX' => ['.o'],
'DEPFILESUFFIX' => '.mf', 'DEPFILESUFFIX' => '.mf',
'CPPDEFPREFIX' => '-D', 'CPPDEFPREFIX' => '-D',
@@ -26,7 +26,7 @@ module Rscons
'AS' => '${CC}', 'AS' => '${CC}',
'ASFLAGS' => [], 'ASFLAGS' => [],
'ASSUFFIX' => '.S', 'ASSUFFIX' => ['.S'],
'ASPPPATH' => '${CPPPATH}', 'ASPPPATH' => '${CPPPATH}',
'ASPPFLAGS' => '${CPPFLAGS}', 'ASPPFLAGS' => '${CPPFLAGS}',
'ASDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'], 'ASDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'],
@@ -40,19 +40,19 @@ module Rscons
'CC' => 'gcc', 'CC' => 'gcc',
'CFLAGS' => [], 'CFLAGS' => [],
'CSUFFIX' => '.c', 'CSUFFIX' => ['.c'],
'CCDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'], 'CCDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'],
'CCCMD' => ['${CC}', '-c', '-o', '${_TARGET}', '${CCDEPGEN}', '${INCPREFIX}${CPPPATH}', '${CPPFLAGS}', '${CFLAGS}', '${CCFLAGS}', '${_SOURCES}'], 'CCCMD' => ['${CC}', '-c', '-o', '${_TARGET}', '${CCDEPGEN}', '${INCPREFIX}${CPPPATH}', '${CPPFLAGS}', '${CFLAGS}', '${CCFLAGS}', '${_SOURCES}'],
'CXX' => 'g++', 'CXX' => 'g++',
'CXXFLAGS' => [], 'CXXFLAGS' => [],
'CXXSUFFIX' => '.cc', 'CXXSUFFIX' => ['.cc', '.cpp', '.cxx', '.C'],
'CXXDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'], 'CXXDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'],
'CXXCMD' =>['${CXX}', '-c', '-o', '${_TARGET}', '${CXXDEPGEN}', '${INCPREFIX}${CPPPATH}', '${CPPFLAGS}', '${CXXFLAGS}', '${CCFLAGS}', '${_SOURCES}'], 'CXXCMD' =>['${CXX}', '-c', '-o', '${_TARGET}', '${CXXDEPGEN}', '${INCPREFIX}${CPPPATH}', '${CPPFLAGS}', '${CXXFLAGS}', '${CCFLAGS}', '${_SOURCES}'],
'DC' => 'gdc', 'DC' => 'gdc',
'DFLAGS' => [], 'DFLAGS' => [],
'DSUFFIX' => '.d', 'DSUFFIX' => ['.d'],
'D_IMPORT_PATH' => [], 'D_IMPORT_PATH' => [],
'DCCMD' => ['${DC}', '-c', '-o', '${_TARGET}', '${INCPREFIX}${D_IMPORT_PATH}', '${DFLAGS}', '${_SOURCES}'], 'DCCMD' => ['${DC}', '-c', '-o', '${_TARGET}', '${INCPREFIX}${D_IMPORT_PATH}', '${DFLAGS}', '${_SOURCES}'],
} }
+27 -7
View File
@@ -1,7 +1,10 @@
require "fileutils"
module Rscons module Rscons
module Builders module Builders
# The Preprocess builder invokes the C preprocessor # The Preprocess builder invokes the C preprocessor
class Preprocess < Builder class Preprocess < Builder
# Return default construction variables for the builder. # Return default construction variables for the builder.
# #
# @param env [Environment] The Environment using the builder. # @param env [Environment] The Environment using the builder.
@@ -9,7 +12,9 @@ module Rscons
# @return [Hash] Default construction variables for the builder. # @return [Hash] Default construction variables for the builder.
def default_variables(env) def default_variables(env)
{ {
"CPP_CMD" => ["${_PREPROCESS_CC}", "-E", "-o", "${_TARGET}", "-I${CPPPATH}", "${CPPFLAGS}", "${CFLAGS}", "${_SOURCES}"], "CPP_CMD" => %w[
${_PREPROCESS_CC} -E ${_PREPROCESS_DEPGEN}
-o ${_TARGET} ${INCPREFIX}${CPPPATH} ${CPPFLAGS} ${_SOURCES}],
} }
end end
@@ -24,17 +29,32 @@ module Rscons
# @return [String,false] # @return [String,false]
# Name of the target file on success or false on failure. # Name of the target file on success or false on failure.
def run(target, sources, cache, env, vars) def run(target, sources, cache, env, vars)
pp_cc = if sources.find {|s| s.end_with?(*env.expand_varref("${CXXSUFFIX}", vars))} if sources.find {|s| s.end_with?(*env.expand_varref("${CXXSUFFIX}", vars))}
"${CXX}" pp_cc = "${CXX}"
depgen = "${CXXDEPGEN}"
else else
"${CC}" pp_cc = "${CC}"
depgen = "${CCDEPGEN}"
end end
vars = vars.merge("_PREPROCESS_CC" => pp_cc, vars = vars.merge("_PREPROCESS_CC" => pp_cc,
"_PREPROCESS_DEPGEN" => depgen,
"_TARGET" => target, "_TARGET" => target,
"_SOURCES" => sources) "_SOURCES" => sources,
"_DEPFILE" => Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars)))
command = env.build_command("${CPP_CMD}", vars) command = env.build_command("${CPP_CMD}", vars)
standard_build("Preprocess #{target}", target, command, sources, env, cache) unless cache.up_to_date?(target, command, sources, env)
end cache.mkdir_p(File.dirname(target))
return false unless env.execute("Preprocess #{target}", command)
deps = sources
if File.exists?(vars["_DEPFILE"])
deps += Environment.parse_makefile_deps(vars["_DEPFILE"], nil)
FileUtils.rm_f(vars["_DEPFILE"])
end
cache.register_build(target, command, deps.uniq, env)
end
target
end
end end
end end
end end
+36 -9
View File
@@ -56,6 +56,9 @@ module Rscons
# Name of the file to store cache information in # Name of the file to store cache information in
CACHE_FILE = ".rsconscache" CACHE_FILE = ".rsconscache"
# Prefix for phony cache entries.
PHONY_PREFIX = ":PHONY:"
# 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
@@ -92,7 +95,8 @@ module Rscons
# Check if target(s) are up to date. # Check if target(s) are up to date.
# #
# @param targets [String, Array<String>] The name(s) of the target file(s). # @param targets [Symbol, String, Array<String>]
# The name(s) of the target file(s).
# @param command [String, Array, Hash] # @param command [String, Array, Hash]
# The command used to build the target. The command parameter can # The command used to build the target. The command parameter can
# actually be a String, Array, or Hash and could contain information # actually be a String, Array, or Hash and could contain information
@@ -120,19 +124,25 @@ module Rscons
# stored in the cache file # stored in the cache file
def up_to_date?(targets, command, deps, env, options = {}) def up_to_date?(targets, command, deps, env, options = {})
Array(targets).each do |target| Array(targets).each do |target|
cache_key = get_cache_key(target)
unless Rscons.phony_target?(target)
# target file must exist on disk # target file must exist on disk
return false unless File.exists?(target) return false unless File.exists?(target)
end
# target must be registered in the cache # target must be registered in the cache
return false unless @cache["targets"].has_key?(target) return false unless @cache["targets"].has_key?(cache_key)
unless Rscons.phony_target?(target)
# target must have the same checksum as when it was built last # target must have the same checksum as when it was built last
return false unless @cache["targets"][target]["checksum"] == lookup_checksum(target) return false unless @cache["targets"][cache_key]["checksum"] == lookup_checksum(target)
end
# command used to build target must be identical # command used to build target must be identical
return false unless @cache["targets"][target]["command"] == Digest::MD5.hexdigest(command.inspect) return false unless @cache["targets"][cache_key]["command"] == Digest::MD5.hexdigest(command.inspect)
cached_deps = @cache["targets"][target]["deps"] || [] cached_deps = @cache["targets"][cache_key]["deps"] || []
cached_deps_fnames = cached_deps.map { |dc| dc["fname"] } cached_deps_fnames = cached_deps.map { |dc| dc["fname"] }
if options[:strict_deps] if options[:strict_deps]
# depedencies passed in must exactly equal those in the cache # depedencies passed in must exactly equal those in the cache
@@ -144,7 +154,7 @@ module Rscons
# set of user dependencies must match # set of user dependencies must match
user_deps = env.get_user_deps(target) || [] user_deps = env.get_user_deps(target) || []
cached_user_deps = @cache["targets"][target]["user_deps"] || [] cached_user_deps = @cache["targets"][cache_key]["user_deps"] || []
cached_user_deps_fnames = cached_user_deps.map { |dc| dc["fname"] } cached_user_deps_fnames = cached_user_deps.map { |dc| dc["fname"] }
return false unless user_deps == cached_user_deps_fnames return false unless user_deps == cached_user_deps_fnames
@@ -159,7 +169,8 @@ module Rscons
# Store cache information about target(s) built by a builder. # Store cache information about target(s) built by a builder.
# #
# @param targets [String, Array<String>] The name of the target(s) built. # @param targets [Symbol, String, Array<String>]
# The name of the target(s) built.
# @param command [String, Array, Hash] # @param command [String, Array, Hash]
# The command used to build the target. The command parameter can # The command used to build the target. The command parameter can
# actually be a String, Array, or Hash and could contain information # actually be a String, Array, or Hash and could contain information
@@ -172,9 +183,10 @@ module Rscons
# @return [void] # @return [void]
def register_build(targets, command, deps, env) def register_build(targets, command, deps, env)
Array(targets).each do |target| Array(targets).each do |target|
@cache["targets"][target] = { target_checksum = Rscons.phony_target?(target) ? "" : calculate_checksum(target)
@cache["targets"][get_cache_key(target)] = {
"command" => Digest::MD5.hexdigest(command.inspect), "command" => Digest::MD5.hexdigest(command.inspect),
"checksum" => calculate_checksum(target), "checksum" => target_checksum,
"deps" => deps.map do |dep| "deps" => deps.map do |dep|
{ {
"fname" => dep, "fname" => dep,
@@ -228,6 +240,21 @@ module Rscons
private private
# Return a String key based on the target name to use in the on-disk cache.
#
# @param target_name [Symbol, String]
# Target name.
#
# @return [String]
# Key name.
def get_cache_key(target_name)
if Rscons.phony_target?(target_name)
PHONY_PREFIX + target_name.to_s
else
target_name
end
end
# 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!
+78
View File
@@ -0,0 +1,78 @@
require "rscons"
require "optparse"
module Rscons
# Command-Line Interface functionality.
module Cli
# Default files to look for to execute if none specified.
DEFAULT_RSCONSFILES = %w[Rsconsfile Rsconsfile.rb]
class << self
# Run the Rscons CLI.
#
# @param argv [Array]
# Command-line parameters.
#
# @return [void]
def run(argv)
argv = argv.dup
rsconsfile = nil
OptionParser.new do |opts|
opts.banner = "Usage: #{$0} [options]"
opts.separator ""
opts.separator "Options:"
opts.on("-c", "--clean", "Perform clean operation") do
Rscons.clean
exit 0
end
opts.on("-f FILE", "Execute FILE (default Rsconsfile)") do |f|
rsconsfile = f
end
opts.on_tail("--version", "Show version") do
puts "Rscons version #{Rscons::VERSION}"
exit 0
end
opts.on_tail("-h", "--help", "Show this help.") do
puts opts
exit 0
end
end.parse!(argv)
if rsconsfile
unless File.exists?(rsconsfile)
$stderr.puts "Cannot read #{rsconsfile}"
exit 1
end
else
rsconsfile = DEFAULT_RSCONSFILES.find do |f|
File.exists?(f)
end
unless rsconsfile
$stderr.puts "Could not find the Rsconsfile to execute."
$stderr.puts "Looked in: [#{DEFAULT_RSCONSFILES.join(", ")}]"
exit 1
end
end
begin
load rsconsfile
rescue Rscons::BuildError => e
$stderr.puts e.message
exit 1
end
exit 0
end
end
end
end
+26 -18
View File
@@ -280,33 +280,35 @@ module Rscons
# #
# @return [void] # @return [void]
def process def process
expand_paths!
while @targets.size > 0 while @targets.size > 0
expand_paths!
targets = @targets targets = @targets
@targets = {} @targets = {}
cache = Cache.instance cache = Cache.instance
cache.clear_checksum_cache! cache.clear_checksum_cache!
targets_processed = {} targets_processed = Set.new
process_target = proc do |target| process_target = proc do |target|
targets_processed[target] ||= begin unless targets_processed.include?(target)
targets[target][:sources].each do |src| targets_processed << target
targets[target].each do |target_params|
target_params[: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(target_params[:builder],
target, target,
targets[target][:sources], target_params[:sources],
cache, cache,
targets[target][:vars] || {}) target_params[:vars] || {})
unless result unless result
raise BuildError.new("Failed to build #{target}") raise BuildError.new("Failed to build #{target}")
end end
result end
end end
end end
begin begin
targets.each do |target, target_params| targets.each_key do |target|
process_target.call(target) process_target.call(target)
end end
ensure ensure
@@ -404,7 +406,8 @@ module Rscons
# #
# @return [void] # @return [void]
def add_target(target, builder, sources, vars, args) def add_target(target, builder, sources, vars, args)
@targets[target] = { @targets[target] ||= []
@targets[target] << {
builder: builder, builder: builder,
sources: sources, sources: sources,
vars: vars, vars: vars,
@@ -506,8 +509,12 @@ module Rscons
# #
# @return [String] The expanded path. # @return [String] The expanded path.
def expand_path(path) def expand_path(path)
if Rscons.phony_target?(path)
path
else
path.sub(%r{^\^(?=[\\/])}, @build_root) path.sub(%r{^\^(?=[\\/])}, @build_root)
end end
end
# Execute a command using the system shell. # Execute a command using the system shell.
# #
@@ -611,8 +618,6 @@ module Rscons
elsif word == "-pthread" elsif word == "-pthread"
append["CCFLAGS", [word]] append["CCFLAGS", [word]]
append["LDFLAGS", [word]] append["LDFLAGS", [word]]
elsif word =~ /^-std=/
append["CFLAGS", [word]]
elsif word =~ /^-Wa,(.*)$/ elsif word =~ /^-Wa,(.*)$/
append["ASFLAGS", $1.split(",")] append["ASFLAGS", $1.split(",")]
elsif word =~ /^-Wl,(.*)$/ elsif word =~ /^-Wl,(.*)$/
@@ -679,14 +684,16 @@ module Rscons
# #
# @return [void] # @return [void]
def expand_paths! def expand_paths!
@targets = @targets.reduce({}) do |result, (target, target_params)| @targets = @targets.reduce({}) do |result, (target, target_params_list)|
target = expand_path(target) if @build_root
target = expand_varref(target)
result[target] = target_params_list.map do |target_params|
sources = target_params[:sources].map do |source| sources = target_params[:sources].map do |source|
source = expand_path(source) if @build_root source = expand_path(source) if @build_root
expand_varref(source) expand_varref(source)
end.flatten
target_params.merge(sources: sources)
end end
target = expand_path(target) if @build_root
target = expand_varref(target)
result[target] = target_params.merge(sources: sources)
result result
end end
end end
@@ -696,7 +703,8 @@ module Rscons
# This method is used internally by Rscons builders. # This method is used internally by Rscons builders.
# #
# @param mf_fname [String] File name of the Makefile to read. # @param mf_fname [String] File name of the Makefile to read.
# @param target [String] Name of the target to gather dependencies for. # @param target [String, nil]
# Name of the target to gather dependencies for, nil for any/all.
# #
# @return [Array<String>] Paths of dependency files. # @return [Array<String>] Paths of dependency files.
def self.parse_makefile_deps(mf_fname, target) def self.parse_makefile_deps(mf_fname, target)
@@ -709,7 +717,7 @@ module Rscons
buildup += ' ' + line buildup += ' ' + line
if buildup =~ /^(.*): (.*)$/ if buildup =~ /^(.*): (.*)$/
mf_target, mf_deps = $1.strip, $2 mf_target, mf_deps = $1.strip, $2
if mf_target == target if target.nil? or mf_target == target
deps += mf_deps.split(' ').map(&:strip) deps += mf_deps.split(' ').map(&:strip)
end end
end end
+2
View File
@@ -116,6 +116,8 @@ module Rscons
expand_varref(varref[*lambda_args], lambda_args) expand_varref(varref[*lambda_args], lambda_args)
elsif varref.nil? elsif varref.nil?
nil nil
elsif varref.is_a?(Symbol)
varref
else else
raise "Unknown varref type: #{varref.class} (#{varref.inspect})" raise "Unknown varref type: #{varref.class} (#{varref.inspect})"
end end
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons module Rscons
# gem version # gem version
VERSION = "1.8.1" VERSION = "1.9.3"
end end
+232
View File
@@ -734,4 +734,236 @@ EOF
expect(result.include?(%{CPPPATH => []})).to be_truthy expect(result.include?(%{CPPPATH => []})).to be_truthy
end end
it "considers deep dependencies when deciding whether to rerun Preprocess builder" do
test_dir("preprocess")
env = Rscons::Environment.new do |env|
env.Preprocess("pp", "foo.h")
end
expect(File.read("pp")).to match(%r{xyz42abc}m)
expect(lines).to eq(["Preprocess pp"])
env.Preprocess("pp", "foo.h")
env.process
expect(lines).to eq([])
File.open("bar.h", "w") do |fh|
fh.puts "#define BAR abc88xyz"
end
$ttt = true
env.Preprocess("pp", "foo.h")
env.process
expect(lines).to eq(["Preprocess pp"])
expect(File.read("pp")).to match(%r{abc88xyz}m)
end
it "allows construction variable references which expand to arrays in sources of a build target" do
test_dir("simple")
Rscons::Environment.new do |env|
env["sources"] = Dir["*.c"]
env.Program("simple", "${sources}")
end
expect(File.exists?("simple.o")).to be_truthy
expect(`./simple`).to eq "This is a simple C program\n"
end
it "supports registering multiple build targets with the same target path" do
test_dir("build_dir")
Rscons::Environment.new do |env|
env["CPPPATH"] << "src/two"
env.Object("one.o", "src/one/one.c")
env.Object("one.o", "src/two/two.c")
end
expect(File.exists?("one.o")).to be_truthy
expect(lines).to eq([
"CC one.o",
"CC one.o",
])
end
it "expands target and source paths when builders are registered in build hooks" do
test_dir("build_dir")
Rscons::Environment.new do |env|
env["CPPPATH"] << "src/two"
env.Object("one.o", "src/one/one.c")
env.add_post_build_hook do |build_op|
if build_op[:target] == "one.o"
env["MODULE"] = "two"
env.Object("${MODULE}.o", "src/${MODULE}/${MODULE}.c")
end
end
end
expect(File.exists?("one.o")).to be_truthy
expect(File.exists?("two.o")).to be_truthy
expect(lines).to eq([
"CC one.o",
"CC two.o",
])
end
context "Directory builder" do
it "creates the requested directory" do
test_dir("simple")
Rscons::Environment.new do |env|
env.Directory("teh_dir")
end
expect(File.directory?("teh_dir")).to be_truthy
expect(lines).to eq(["Directory teh_dir"])
end
it "succeeds when the requested directory already exists" do
test_dir("simple")
FileUtils.mkdir("teh_dir")
Rscons::Environment.new do |env|
env.Directory("teh_dir")
end
expect(File.directory?("teh_dir")).to be_truthy
expect(lines).to eq([])
end
it "fails when the target path is a file" do
test_dir("simple")
FileUtils.touch("teh_dir")
expect do
Rscons::Environment.new do |env|
env.Directory("teh_dir")
end
end.to raise_error /Failed to build teh_dir/
expect(lines).to eq([
"Error: `teh_dir' already exists and is not a directory",
])
end
end
context "Install buildler" do
let(:base_env) do
test_dir("build_dir")
Rscons::Environment.new do |env|
env["CPPPATH"] = Dir["src/**/"]
env["sources"] = Dir["src/**/*.c"]
env.Program("simple.exe", "${sources}")
end
end
it "copies a file to the target file name" do
env = base_env.clone do |env|
lines
env.Install("inst.exe", "simple.exe")
end
expect(lines).to eq(["Install inst.exe"])
env.Install("inst.exe", "simple.exe")
env.process
expect(lines).to eq([])
expect(File.exists?("inst.exe")).to be_truthy
expect(File.read("inst.exe", mode: "rb")).to eq(File.read("simple.exe", mode: "rb"))
FileUtils.rm("inst.exe")
env.Install("inst.exe", "simple.exe")
env.process
expect(lines).to eq(["Install inst.exe"])
end
it "operates the same as a Copy builder" do
env = base_env.clone do |env|
lines
env.Copy("inst.exe", "simple.exe")
end
expect(lines).to eq(["Copy inst.exe"])
env.Copy("inst.exe", "simple.exe")
env.process
expect(lines).to eq([])
expect(File.exists?("inst.exe")).to be_truthy
expect(File.read("inst.exe", mode: "rb")).to eq(File.read("simple.exe", mode: "rb"))
FileUtils.rm("inst.exe")
env.Copy("inst.exe", "simple.exe")
env.process
expect(lines).to eq(["Copy inst.exe"])
end
it "copies a file to the target directory name" do
env = base_env.clone do |env|
lines
env.Directory("inst")
env.Install("inst", "simple.exe")
end
expect(lines).to eq([
"Directory inst",
"Install inst",
])
env.Install("inst", "simple.exe")
env.process
expect(lines).to eq([])
expect(File.exists?("inst/simple.exe")).to be_truthy
expect(File.read("inst/simple.exe", mode: "rb")).to eq(File.read("simple.exe", mode: "rb"))
end
it "copies a directory to the non-existent target directory name" do
env = base_env.clone do |env|
lines
env.Install("dist/src", "src")
end
expect(lines).to eq(["Install dist/src"])
env.Install("dist/src", "src")
env.process
expect(lines).to eq([])
%w[src/one/one.c src/two/two.c src/two/two.h].each do |f|
expect(File.exists?("dist/#{f}")).to be_truthy
expect(File.read("dist/#{f}", mode: "rb")).to eq(File.read(f, mode: "rb"))
end
end
it "copies a directory to the existent target directory name" do
env = base_env.clone do |env|
lines
env.Directory("dist/src")
env.Install("dist/src", "src")
end
expect(lines).to eq([
"Directory dist/src",
"Install dist/src",
])
env.Install("dist/src", "src")
env.process
expect(lines).to eq([])
%w[src/one/one.c src/two/two.c src/two/two.h].each do |f|
expect(File.exists?("dist/#{f}")).to be_truthy
expect(File.read("dist/#{f}", mode: "rb")).to eq(File.read(f, mode: "rb"))
end
FileUtils.rm("dist/src/two/two.c")
env.Install("dist/src", "src")
env.process
expect(lines).to eq(["Install dist/src"])
end
end
context "phony targets" do
[false, true].each do |with_build_root|
it "allows specifying a Symbol as a target name and reruns the builder if the sources or command have changed" do
test_dir("simple")
env = Rscons::Environment.new do |env|
env.build_root = "bld" if with_build_root
env.add_builder(:Checker) do |target, sources, cache, env, vars|
unless cache.up_to_date?(target, :Checker, sources, env)
puts "Checker #{sources.first}" if env.echo != :off
cache.register_build(target, :Checker, sources, env)
end
target
end
env.Program("simple.exe", "simple.c")
env.Checker(:checker, "simple.exe")
end
expect(lines).to eq(["CC #{with_build_root ? 'bld/' : ''}simple.o", "LD simple.exe", "Checker simple.exe"])
env.clone do |env|
env.Checker(:checker, "simple.exe")
end
expect(lines).to eq([])
File.open("simple.exe", "w") do |fh|
fh.puts "Changed simple.exe"
end
env.clone do |env|
env.Checker(:checker, "simple.exe")
end
expect(lines).to eq(["Checker simple.exe"])
end
end
end
end end
+6 -4
View File
@@ -1,16 +1,18 @@
module Rscons module Rscons
module Builders module Builders
describe Command do describe Command do
let(:command) { ['pandoc', '-fmarkdown', '-thtml', '-o${_TARGET}', '${_SOURCES}'] } let(:command) { ['pandoc', '-fmarkdown', '-thtml', '-o${_TARGET}', '${_SOURCES}'] }
let(:env) {Environment.new} let(:env) {Environment.new}
subject {Command.new} subject {Command.new}
it "invokes the command to build the target" do it "invokes the command to build the target" do
expected_cmd = ['pandoc', '-fmarkdown', '-thtml', '-ofoo.html', 'foo.md'] expected_cmd = ['pandoc', '-fmarkdown', '-thtml', '-ofoo.html', 'foo.md']
expect(subject).to receive(:standard_build).with("CMD foo.html", "foo.html", expected_cmd, ["foo.md"], env, :cache) expect(subject).to receive(:standard_build).with("PANDOC foo.html", "foo.html", expected_cmd, ["foo.md"], env, :cache)
subject.run("foo.html", ["foo.md"], :cache, env, {'CMD' => command}) subject.run("foo.html", ["foo.md"], :cache, env,
end "CMD" => command, "CMD_DESC" => "PANDOC")
end
end end
end end
end end
+35 -5
View File
@@ -1,18 +1,48 @@
module Rscons module Rscons
module Builders module Builders
describe Preprocess do describe Preprocess do
let(:env) {Environment.new} let(:env) {Environment.new}
subject {Preprocess.new} subject {Preprocess.new}
it "supports overriding CC construction variable" do it "supports overriding CC construction variable" do
expect(subject).to receive(:standard_build).with("Preprocess module.pp", "module.pp", ["my_cpp", "-E", "-o", "module.pp", "module.c"], ["module.c"], env, :cache) cache = double(Cache)
subject.run("module.pp", ["module.c"], :cache, env, "CC" => "my_cpp") command = %w[my_cpp -E -MMD -MF module.mf -o module.pp module.c]
expect(cache).to receive(:up_to_date?).with("module.pp", command, %w[module.c], env).and_return(false)
expect(cache).to receive(:mkdir_p).with(".")
expect(env).to receive(:execute).with("Preprocess module.pp", command).and_return(true)
expect(File).to receive(:exists?).with("module.mf").and_return(true)
expect(Environment).to receive(:parse_makefile_deps).with("module.mf", nil).and_return(%w[module.c one.h two.h])
expect(FileUtils).to receive(:rm_f).with("module.mf")
expect(cache).to receive(:register_build).with("module.pp", command, %w[module.c one.h two.h], env)
expect(subject.run("module.pp", ["module.c"], cache, env, "CC" => "my_cpp")).to eq("module.pp")
end end
it "supports overriding CPP_CMD construction variable" do it "supports overriding CPP_CMD construction variable" do
expect(subject).to receive(:standard_build).with("Preprocess module.pp", "module.pp", ["my_cpp", "module.c"], ["module.c"], env, :cache) cache = double(Cache)
subject.run("module.pp", ["module.c"], :cache, env, "CPP_CMD" => ["my_cpp", "${_SOURCES}"]) command = %w[my_cpp module.c]
end expect(cache).to receive(:up_to_date?).with("module.pp", command, %w[module.c], env).and_return(false)
expect(cache).to receive(:mkdir_p).with(".")
expect(env).to receive(:execute).with("Preprocess module.pp", command).and_return(true)
expect(File).to receive(:exists?).with("module.mf").and_return(true)
expect(Environment).to receive(:parse_makefile_deps).with("module.mf", nil).and_return(%w[module.c one.h two.h])
expect(FileUtils).to receive(:rm_f).with("module.mf")
expect(cache).to receive(:register_build).with("module.pp", command, %w[module.c one.h two.h], env)
expect(subject.run("module.pp", ["module.c"], cache, env, "CPP_CMD" => ["my_cpp", "${_SOURCES}"])).to eq("module.pp")
end
it "returns false if executing the preprocessor fails" do
cache = double(Cache)
command = %w[gcc -E -MMD -MF module.mf -o module.pp module.c]
expect(cache).to receive(:up_to_date?).with("module.pp", command, %w[module.c], env).and_return(false)
expect(cache).to receive(:mkdir_p).with(".")
expect(env).to receive(:execute).with("Preprocess module.pp", command).and_return(false)
expect(subject.run("module.pp", ["module.c"], cache, env, {})).to eq(false)
end
end end
end end
end end
+8 -9
View File
@@ -305,10 +305,10 @@ module Rscons
env.Object("target.o", ["src1.c", "src2.c"], var: "val") env.Object("target.o", ["src1.c", "src2.c"], var: "val")
target = env.instance_variable_get(:@targets)["target.o"] target = env.instance_variable_get(:@targets)["target.o"]
expect(target).to_not be_nil expect(target).to_not be_nil
expect(target[:builder].is_a?(Builder)).to be_truthy expect(target[0][:builder].is_a?(Builder)).to be_truthy
expect(target[:sources]).to eq ["src1.c", "src2.c"] expect(target[0][:sources]).to eq ["src1.c", "src2.c"]
expect(target[:vars]).to eq({var: "val"}) expect(target[0][:vars]).to eq({var: "val"})
expect(target[:args]).to eq [] expect(target[0][:args]).to eq []
end end
it "raises an error when vars is not a Hash" do it "raises an error when vars is not a Hash" do
@@ -396,24 +396,23 @@ module Rscons
describe "#parse_flags" do describe "#parse_flags" do
it "executes the shell command and parses the returned flags when the input argument begins with !" do it "executes the shell command and parses the returned flags when the input argument begins with !" do
env = Environment.new env = Environment.new
env["CFLAGS"] = ["-g"] env["CCFLAGS"] = ["-g"]
expect(env).to receive(:shell).with("my_command").and_return(%[-arch my_arch -Done=two -include ii -isysroot sr -Iincdir -Llibdir -lmy_lib -mno-cygwin -mwindows -pthread -std=c99 -Wa,'asm,args 1 2' -Wl,linker,"args 1 2" -Wp,cpp,args,1,2 -arbitrary +other_arbitrary some_lib /a/b/c/lib]) expect(env).to receive(:shell).with("my_command").and_return(%[-arch my_arch -Done=two -include ii -isysroot sr -Iincdir -Llibdir -lmy_lib -mno-cygwin -mwindows -pthread -std=c99 -Wa,'asm,args 1 2' -Wl,linker,"args 1 2" -Wp,cpp,args,1,2 -arbitrary +other_arbitrary some_lib /a/b/c/lib])
rv = env.parse_flags("!my_command") rv = env.parse_flags("!my_command")
expect(rv).to eq({ expect(rv).to eq({
"CCFLAGS" => %w[-arch my_arch -include ii -isysroot sr -mno-cygwin -pthread -arbitrary +other_arbitrary], "CCFLAGS" => %w[-arch my_arch -include ii -isysroot sr -mno-cygwin -pthread -std=c99 -arbitrary +other_arbitrary],
"LDFLAGS" => %w[-arch my_arch -isysroot sr -mno-cygwin -mwindows -pthread] + ["linker", "args 1 2"] + %w[+other_arbitrary], "LDFLAGS" => %w[-arch my_arch -isysroot sr -mno-cygwin -mwindows -pthread] + ["linker", "args 1 2"] + %w[+other_arbitrary],
"CPPPATH" => %w[incdir], "CPPPATH" => %w[incdir],
"LIBS" => %w[my_lib some_lib /a/b/c/lib], "LIBS" => %w[my_lib some_lib /a/b/c/lib],
"LIBPATH" => %w[libdir], "LIBPATH" => %w[libdir],
"CPPDEFINES" => %w[one=two], "CPPDEFINES" => %w[one=two],
"CFLAGS" => %w[-std=c99],
"ASFLAGS" => ["asm", "args 1 2"], "ASFLAGS" => ["asm", "args 1 2"],
"CPPFLAGS" => %w[cpp args 1 2], "CPPFLAGS" => %w[cpp args 1 2],
}) })
expect(env["CFLAGS"]).to eq(["-g"]) expect(env["CCFLAGS"]).to eq(["-g"])
expect(env["ASFLAGS"]).to eq([]) expect(env["ASFLAGS"]).to eq([])
env.merge_flags(rv) env.merge_flags(rv)
expect(env["CFLAGS"]).to eq(["-g", "-std=c99"]) expect(env["CCFLAGS"]).to eq(%w[-g -arch my_arch -include ii -isysroot sr -mno-cygwin -pthread -std=c99 -arbitrary +other_arbitrary])
expect(env["ASFLAGS"]).to eq(["asm", "args 1 2"]) expect(env["ASFLAGS"]).to eq(["asm", "args 1 2"])
end end
end end
+17 -7
View File
@@ -24,13 +24,23 @@ describe Rscons do
end end
end end
describe ".set_suffix" do
it "changes the suffix to the new one" do
expect(Rscons.set_suffix("foo.c", ".h")).to eq("foo.h")
end
it "appends a suffix if the given file name does not have one" do
expect(Rscons.set_suffix("bazz", ".d")).to eq("bazz.d")
end
end
describe ".get_system_shell" do describe ".get_system_shell" do
before(:each) do before(:each) do
Rscons.class_variable_set(:@@shell, nil) Rscons.instance_variable_set(:@shell, nil)
end end
after(:each) do after(:each) do
Rscons.class_variable_set(:@@shell, nil) Rscons.instance_variable_set(:@shell, nil)
end end
it "uses the SHELL environment variable if it tests successfully" do it "uses the SHELL environment variable if it tests successfully" do
@@ -70,11 +80,11 @@ describe Rscons do
context "command executer" do context "command executer" do
describe ".command_executer" do describe ".command_executer" do
before(:each) do before(:each) do
Rscons.class_variable_set(:@@command_executer, nil) Rscons.instance_variable_set(:@command_executer, nil)
end end
after(:each) do after(:each) do
Rscons.class_variable_set(:@@command_executer, nil) Rscons.instance_variable_set(:@command_executer, nil)
end end
it "returns ['env'] if mingw platform in MSYS and 'env' works" do it "returns ['env'] if mingw platform in MSYS and 'env' works" do
@@ -105,10 +115,10 @@ describe Rscons do
end end
describe ".command_executer=" do describe ".command_executer=" do
it "overrides the value of @@command_executer" do it "overrides the value of @command_executer" do
Rscons.class_variable_set(:@@command_executer, ["env"]) Rscons.instance_variable_set(:@command_executer, ["env"])
Rscons.command_executer = [] Rscons.command_executer = []
expect(Rscons.class_variable_get(:@@command_executer)).to eq([]) expect(Rscons.instance_variable_get(:@command_executer)).to eq([])
end end
end end
end end