Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a8023ae03c | ||
|
|
99ba015630 | ||
|
|
dcf90cc12b | ||
|
|
f93ca62c07 | ||
|
|
d9e3129ad5 | ||
|
|
d5e0475e6c | ||
|
|
b05eed36eb | ||
|
|
7ea0721cc2 | ||
|
|
e654df008d | ||
|
|
92d4dd8396 | ||
|
|
e279959d79 | ||
|
|
469f575564 | ||
|
|
07725581e9 | ||
|
|
ebb69cedc2 | ||
|
|
c98b377ce8 | ||
|
|
a0874e7cf1 | ||
|
|
0b04229e3d | ||
|
|
20f3da3b04 | ||
|
|
a6a79b8ef1 | ||
|
|
6d4f27602e | ||
|
|
453295a49b | ||
|
|
dee2b98f6e | ||
|
|
b186b3102c | ||
|
|
23c94f7841 | ||
|
|
6acb209ef4 | ||
|
|
43e12819d7 | ||
|
|
1bfb7dd100 | ||
|
|
df1e3be829 | ||
|
|
851adae66f | ||
|
|
eb28e09d8c |
No files matched your search
+6
-18
@@ -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
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
PATH
|
||||||
|
remote: .
|
||||||
|
specs:
|
||||||
|
rscons (1.8.1)
|
||||||
|
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)
|
||||||
|
rdoc (4.1.2)
|
||||||
|
json (~> 1.4)
|
||||||
|
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
|
||||||
|
rdoc
|
||||||
|
redcarpet
|
||||||
|
rscons!
|
||||||
|
rspec
|
||||||
|
simplecov
|
||||||
|
yard
|
||||||
@@ -154,6 +154,31 @@ Rscons::Environment.new do |env|
|
|||||||
end
|
end
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Example: Custom Builder Using Environment#add_builder()
|
||||||
|
|
||||||
|
The `add_builder` method of the `Rscons::Environment` class optionally allows
|
||||||
|
you to define and register a builder by providing a name and action block. This
|
||||||
|
can be useful if the builder you are trying to define is easily expressed as a
|
||||||
|
short ruby procedure. When `add_builder` is called in this manner a new builder
|
||||||
|
will be registered with the environment with the given name. When this builder
|
||||||
|
is used it will call the provided block in order to build the target.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
Rscons::Environment.new do |env|
|
||||||
|
env.add_builder(:JsonToYaml) do |target, sources, cache, env, vars|
|
||||||
|
unless cache.up_to_date?(target, :JsonToYaml, sources, env)
|
||||||
|
cache.mkdir_p(File.dirname(target))
|
||||||
|
File.open(target, 'w') do |f|
|
||||||
|
f.write(YAML.dump(JSON.load(IO.read(sources.first))))
|
||||||
|
end
|
||||||
|
cache.register_build(target, :JsonToYaml, sources, env)
|
||||||
|
end
|
||||||
|
target
|
||||||
|
end
|
||||||
|
env.JsonToYaml('foo.yml','foo.json')
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
### Example: Using different compilation flags for some sources
|
### Example: Using different compilation flags for some sources
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
@@ -173,6 +198,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
|
||||||
@@ -207,6 +235,7 @@ 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
|
||||||
* CFile, which builds a C or C++ source file from a lex or yacc input file
|
* CFile, which builds a C or C++ source file from a lex or yacc input file
|
||||||
* Disassemble, which disassembles an object file to a disassembly listing
|
* Disassemble, which disassembles an object file to a disassembly listing
|
||||||
* Library, which collects object files into a static library archive file
|
* Library, which collects object files into a static library archive file
|
||||||
@@ -217,6 +246,18 @@ Rscons ships with a number of builders:
|
|||||||
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.
|
||||||
|
|
||||||
|
#### Command
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
env.Command(target, sources, 'CMD' => command)
|
||||||
|
# Example
|
||||||
|
env.Command("docs.html", "docs.md",
|
||||||
|
CMD => ['pandoc', '-fmarkdown', '-thtml', '-o${_TARGET}', '${_SOURCES}'])
|
||||||
|
```
|
||||||
|
|
||||||
|
The command builder executes a user-defined command in order to produce the
|
||||||
|
desired target file based on the provided source files.
|
||||||
|
|
||||||
#### CFile
|
#### CFile
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
@@ -317,6 +358,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 +383,30 @@ http://rubydoc.info/github/holtrop/rscons/frames.
|
|||||||
|
|
||||||
## Release Notes
|
## Release Notes
|
||||||
|
|
||||||
|
### v1.8.0
|
||||||
|
|
||||||
|
- new Command builder to execute arbitrary user commands
|
||||||
|
- new SimpleBuilder class
|
||||||
|
- create new builders quickly by passing a block to Environment#add_builder
|
||||||
|
- improved YARD documentation
|
||||||
|
- add Environment#dump to debug Environment construction variables
|
||||||
|
|
||||||
|
### 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
|
### v1.5.0
|
||||||
|
|
||||||
- add "json" as a runtime dependency
|
- add "json" as a runtime dependency
|
||||||
|
|||||||
+4
-8
@@ -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
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{ "key": "value" }
|
||||||
@@ -6,18 +6,21 @@ 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/disassemble"
|
require_relative "rscons/builders/disassemble"
|
||||||
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"
|
||||||
require_relative "rscons/builders/program"
|
require_relative "rscons/builders/program"
|
||||||
|
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,
|
||||||
:Disassemble,
|
:Disassemble,
|
||||||
:Library,
|
:Library,
|
||||||
@@ -30,6 +33,8 @@ module Rscons
|
|||||||
class BuildError < RuntimeError; end
|
class BuildError < RuntimeError; end
|
||||||
|
|
||||||
# Remove all generated files.
|
# Remove all generated files.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
def self.clean
|
def self.clean
|
||||||
cache = Cache.instance
|
cache = Cache.instance
|
||||||
# remove all built files
|
# remove all built files
|
||||||
@@ -118,6 +123,8 @@ module Rscons
|
|||||||
# Set the command executer array.
|
# Set the command executer array.
|
||||||
#
|
#
|
||||||
# @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.
|
||||||
def self.command_executer=(val)
|
def self.command_executer=(val)
|
||||||
@@command_executer = val
|
@@command_executer = val
|
||||||
end
|
end
|
||||||
|
|||||||
+17
-1
@@ -9,6 +9,8 @@ module Rscons
|
|||||||
# Return the name of the builder.
|
# Return the name of the builder.
|
||||||
#
|
#
|
||||||
# If not overridden this defaults to the last component of the class name.
|
# If not overridden this defaults to the last component of the class name.
|
||||||
|
#
|
||||||
|
# @return [String] The name of the builder.
|
||||||
def name
|
def name
|
||||||
self.class.name.split(":").last
|
self.class.name.split(":").last
|
||||||
end
|
end
|
||||||
@@ -44,7 +46,7 @@ module Rscons
|
|||||||
# file name from a given source file name.
|
# file name from a given source file name.
|
||||||
#
|
#
|
||||||
# @param target [String] The target file name.
|
# @param target [String] The target file name.
|
||||||
# @param source [String, Array] The source file name(s).
|
# @param source [String] The source file name.
|
||||||
# @param env [Environment] The Environment.
|
# @param env [Environment] The Environment.
|
||||||
#
|
#
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
@@ -54,6 +56,20 @@ module Rscons
|
|||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
raise "This method must be overridden in a subclass"
|
||||||
|
end
|
||||||
|
|
||||||
# Check if the cache is up to date for the target and if not execute the
|
# Check if the cache is up to date for the target and if not execute the
|
||||||
# build command.
|
# build command.
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
module Rscons
|
||||||
|
module Builders
|
||||||
|
# Execute a command that will produce the given target based on the given
|
||||||
|
# sources.
|
||||||
|
#
|
||||||
|
# @since 1.8.0
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# env.Command("docs.html", "docs.md",
|
||||||
|
# CMD => %w[pandoc -fmarkdown -thtml -o${_TARGET} ${_SOURCES}])
|
||||||
|
class Command < 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)
|
||||||
|
vars = vars.merge({
|
||||||
|
"_TARGET" => target,
|
||||||
|
"_SOURCES" => sources,
|
||||||
|
})
|
||||||
|
command = env.build_command("${CMD}", vars)
|
||||||
|
standard_build("CMD #{target}", target, command, sources, env, cache)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -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',
|
||||||
@@ -61,7 +62,7 @@ module Rscons
|
|||||||
# file name from a given source file name.
|
# file name from a given source file name.
|
||||||
#
|
#
|
||||||
# @param target [String] The target file name.
|
# @param target [String] The target file name.
|
||||||
# @param source [String, Array] The source file name(s).
|
# @param source [String] The source file name.
|
||||||
# @param env [Environment] The Environment.
|
# @param env [Environment] The Environment.
|
||||||
#
|
#
|
||||||
# @return [Boolean]
|
# @return [Boolean]
|
||||||
@@ -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}}"))
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
module Rscons
|
||||||
|
module Builders
|
||||||
|
# A Generic builder class whose name and operation is defined at
|
||||||
|
# instantiation.
|
||||||
|
#
|
||||||
|
# @since 1.8.0
|
||||||
|
class SimpleBuilder < Builder
|
||||||
|
# @return [String] The name of this builder.
|
||||||
|
attr_reader :name
|
||||||
|
|
||||||
|
# Create a new builder with the given name and action.
|
||||||
|
#
|
||||||
|
# @param name [String,Symbol] The name of the builder when registered.
|
||||||
|
# @param block [Block]
|
||||||
|
# The action to perform when the builder is processed. The provided
|
||||||
|
# block must return the target file on success or false on failure.
|
||||||
|
# The provided block should have the same signature as {Builder#run}.
|
||||||
|
def initialize(name, &block)
|
||||||
|
@name = name.to_s
|
||||||
|
@block = block
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
@block.call(target, sources, cache, env, vars)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
+22
-6
@@ -63,17 +63,23 @@ module Rscons
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Remove the cache file.
|
# Remove the cache file.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
def clear
|
def clear
|
||||||
FileUtils.rm_f(CACHE_FILE)
|
FileUtils.rm_f(CACHE_FILE)
|
||||||
initialize!
|
initialize!
|
||||||
end
|
end
|
||||||
|
|
||||||
# Clear the cached file checksums.
|
# Clear the cached file checksums.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
def clear_checksum_cache!
|
def clear_checksum_cache!
|
||||||
@lookup_checksums = {}
|
@lookup_checksums = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Write the cache to disk to be loaded next time.
|
# Write the cache to disk to be loaded next time.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
def write
|
def write
|
||||||
if @dirty || (@cache["version"] != VERSION)
|
if @dirty || (@cache["version"] != VERSION)
|
||||||
@cache["version"] = VERSION
|
@cache["version"] = VERSION
|
||||||
@@ -86,9 +92,14 @@ module Rscons
|
|||||||
|
|
||||||
# Check if target(s) are up to date.
|
# Check if target(s) are up to date.
|
||||||
#
|
#
|
||||||
# @param targets [String, Array] The name(s) of the target file(s).
|
# @param targets [String, Array<String>] The name(s) of the target file(s).
|
||||||
# @param command [String, Array] The command used to build the target.
|
# @param command [String, Array, Hash]
|
||||||
# @param deps [Array] List of the target's dependency files.
|
# The command used to build the target. The command parameter can
|
||||||
|
# actually be a String, Array, or Hash and could contain information
|
||||||
|
# other than just the actual command used to build the target. For the
|
||||||
|
# purposes of the Cache, any difference in the command argument will
|
||||||
|
# trigger a rebuild.
|
||||||
|
# @param deps [Array<String>] List of the target's dependency files.
|
||||||
# @param env [Environment] The Rscons::Environment.
|
# @param env [Environment] The Rscons::Environment.
|
||||||
# @param options [Hash] Optional options.
|
# @param options [Hash] Optional options.
|
||||||
# @option options [Boolean] :strict_deps
|
# @option options [Boolean] :strict_deps
|
||||||
@@ -148,9 +159,14 @@ 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] The name of the target(s) built.
|
# @param targets [String, Array<String>] The name of the target(s) built.
|
||||||
# @param command [String, Array] The command used to build the target.
|
# @param command [String, Array, Hash]
|
||||||
# @param deps [Array] List of dependencies for the target.
|
# The command used to build the target. The command parameter can
|
||||||
|
# actually be a String, Array, or Hash and could contain information
|
||||||
|
# other than just the actual command used to build the target. For the
|
||||||
|
# purposes of the Cache, any difference in the command argument will
|
||||||
|
# trigger a rebuild.
|
||||||
|
# @param deps [Array<String>] List of dependencies for the target.
|
||||||
# @param env [Environment] The {Rscons::Environment}.
|
# @param env [Environment] The {Rscons::Environment}.
|
||||||
#
|
#
|
||||||
# @return [void]
|
# @return [void]
|
||||||
|
|||||||
+127
-43
@@ -7,10 +7,10 @@ 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
|
# @return [Symbol] :command, :short, or :off
|
||||||
attr_accessor :echo
|
attr_accessor :echo
|
||||||
|
|
||||||
# @return [String, nil] The build root.
|
# @return [String, nil] The build root.
|
||||||
@@ -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)
|
||||||
@@ -79,7 +79,7 @@ module Rscons
|
|||||||
#
|
#
|
||||||
# Any options that #initialize receives can also be specified here.
|
# Any options that #initialize receives can also be specified here.
|
||||||
#
|
#
|
||||||
# @return a new {Environment} object.
|
# @return [Environment] The newly created {Environment} object.
|
||||||
def clone(options = {})
|
def clone(options = {})
|
||||||
clone = options[:clone] || Set[:variables, :builders]
|
clone = options[:clone] || Set[:variables, :builders]
|
||||||
clone = Set[:variables, :builders, :build_root, :build_dirs, :build_hooks] if clone == :all
|
clone = Set[:variables, :builders, :build_root, :build_dirs, :build_hooks] if clone == :all
|
||||||
@@ -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?
|
||||||
@@ -117,10 +120,32 @@ module Rscons
|
|||||||
|
|
||||||
# Add a {Builder} object to the Environment.
|
# Add a {Builder} object to the Environment.
|
||||||
#
|
#
|
||||||
# @param builder [Builder] The {Builder} object to add.
|
# @overload add_builder(builder)
|
||||||
|
#
|
||||||
|
# Add the given builder to the Environment.
|
||||||
|
#
|
||||||
|
# @param builder [Builder] An instance of the builder to register.
|
||||||
|
#
|
||||||
|
# @overload add_builder(builder,&action)
|
||||||
|
#
|
||||||
|
# Create a new {Builders::SimpleBuilder} instance and add it to the
|
||||||
|
# environment.
|
||||||
|
#
|
||||||
|
# @since 1.8.0
|
||||||
|
#
|
||||||
|
# @param builder [String,Symbol]
|
||||||
|
# The name of the builder to add.
|
||||||
|
#
|
||||||
|
# @param action [Block]
|
||||||
|
# A block that will be called when the builder is executed to generate
|
||||||
|
# a target file. The provided block should have the same prototype as
|
||||||
|
# {Rscons::Builder#run}.
|
||||||
#
|
#
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def add_builder(builder)
|
def add_builder(builder, &action)
|
||||||
|
if not builder.is_a? Rscons::Builder
|
||||||
|
builder = Rscons::Builders::SimpleBuilder.new(builder, &action)
|
||||||
|
end
|
||||||
@builders[builder.name] = builder
|
@builders[builder.name] = builder
|
||||||
var_defs = builder.default_variables(self)
|
var_defs = builder.default_variables(self)
|
||||||
if var_defs
|
if var_defs
|
||||||
@@ -132,6 +157,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 +175,50 @@ 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]
|
||||||
# @param obj_dir [String] Path to the object directory.
|
# Path to the source directory. If a Regexp is given, it will be matched
|
||||||
|
# to source file names.
|
||||||
|
# @param obj_dir [String]
|
||||||
|
# Path to the object directory. If a Regexp is given as src_dir, then
|
||||||
|
# obj_dir can contain backreferences to groups matched from the source
|
||||||
|
# file name.
|
||||||
#
|
#
|
||||||
# @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
|
||||||
|
|
||||||
@@ -188,25 +250,27 @@ module Rscons
|
|||||||
build_fname
|
build_fname
|
||||||
end
|
end
|
||||||
|
|
||||||
# Access a construction variable or environment option.
|
# Get a construction variable's value.
|
||||||
#
|
#
|
||||||
# @see VarSet#[]
|
# @see VarSet#[]
|
||||||
def [](*args)
|
def [](*args)
|
||||||
@varset.send(:[], *args)
|
@varset.send(:[], *args)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set a construction variable or environment option.
|
# Set a construction variable's value.
|
||||||
#
|
#
|
||||||
# @see VarSet#[]=
|
# @see VarSet#[]=
|
||||||
def []=(*args)
|
def []=(*args)
|
||||||
@varset.send(:[]=, *args)
|
@varset.send(:[]=, *args)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Add a set of construction variables or environment options.
|
# Add a set of construction variables to the Environment.
|
||||||
#
|
#
|
||||||
# @see VarSet#append
|
# @param values [VarSet, Hash] New set of variables.
|
||||||
def append(*args)
|
#
|
||||||
@varset.append(*args)
|
# @return [void]
|
||||||
|
def append(values)
|
||||||
|
@varset.append(values)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Build all build targets specified in the Environment.
|
# Build all build targets specified in the Environment.
|
||||||
@@ -216,23 +280,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 +306,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
|
||||||
@@ -250,6 +316,8 @@ module Rscons
|
|||||||
end
|
end
|
||||||
|
|
||||||
# Clear all targets registered for the Environment.
|
# Clear all targets registered for the Environment.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
def clear_targets
|
def clear_targets
|
||||||
@targets = {}
|
@targets = {}
|
||||||
end
|
end
|
||||||
@@ -351,7 +419,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
|
||||||
@@ -372,8 +441,9 @@ module Rscons
|
|||||||
#
|
#
|
||||||
# This method is used internally by Rscons builders.
|
# This method is used internally by Rscons builders.
|
||||||
#
|
#
|
||||||
# @param sources [Array] List of source files to build.
|
# @param sources [Array<String>] List of source files to build.
|
||||||
# @param suffixes [Array] List of suffixes to try to convert source files into.
|
# @param suffixes [Array<String>]
|
||||||
|
# List of suffixes to try to convert source files into.
|
||||||
# @param cache [Cache] The Cache.
|
# @param cache [Cache] The Cache.
|
||||||
# @param vars [Hash] Extra variables to pass to the builder.
|
# @param vars [Hash] Extra variables to pass to the builder.
|
||||||
#
|
#
|
||||||
@@ -402,24 +472,29 @@ module Rscons
|
|||||||
#
|
#
|
||||||
# @param builder [Builder] The Builder to use.
|
# @param builder [Builder] The Builder to use.
|
||||||
# @param target [String] The target output file.
|
# @param target [String] The target output file.
|
||||||
# @param sources [Array] List of source files.
|
# @param sources [Array<String>] List of source files.
|
||||||
# @param cache [Cache] The Cache.
|
# @param cache [Cache] The Cache.
|
||||||
# @param vars [Hash] Extra variables to pass to the builder.
|
# @param vars [Hash] Extra variables to pass to the builder.
|
||||||
#
|
#
|
||||||
# @return [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.
|
||||||
@@ -463,10 +538,10 @@ module Rscons
|
|||||||
# Parse command-line flags for compilation/linking options into separate
|
# Parse command-line flags for compilation/linking options into separate
|
||||||
# construction variables.
|
# construction variables.
|
||||||
#
|
#
|
||||||
# The parsed construction variables are returned in a Hash instead of
|
# For {#parse_flags}, the parsed construction variables are returned in a
|
||||||
# merging them directly to the Environment. They can be merged with
|
# Hash instead of merging them directly to the Environment. They can be
|
||||||
# {#merge_flags}. The {#parse_flags!} version immediately merges the parsed
|
# merged with {#merge_flags}. The {#parse_flags!} version immediately
|
||||||
# flags as well.
|
# merges the parsed flags as well.
|
||||||
#
|
#
|
||||||
# Example:
|
# Example:
|
||||||
# # Import FreeType build options
|
# # Import FreeType build options
|
||||||
@@ -585,6 +660,15 @@ module Rscons
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Print the Environment's construction variables for debugging.
|
||||||
|
def dump
|
||||||
|
varset_hash = @varset.to_h
|
||||||
|
varset_hash.keys.sort_by(&:to_s).each do |var|
|
||||||
|
var_str = var.is_a?(Symbol) ? var.inspect : var
|
||||||
|
puts "#{var_str} => #{varset_hash[var].inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Expand target and source paths before invoking builders.
|
# Expand target and source paths before invoking builders.
|
||||||
|
|||||||
+23
-3
@@ -58,7 +58,7 @@ module Rscons
|
|||||||
#
|
#
|
||||||
# @param values [VarSet, Hash] New set of variables.
|
# @param values [VarSet, Hash] New set of variables.
|
||||||
#
|
#
|
||||||
# @return [self]
|
# @return [VarSet] Returns self.
|
||||||
def append(values)
|
def append(values)
|
||||||
coa!
|
coa!
|
||||||
if values.is_a?(VarSet)
|
if values.is_a?(VarSet)
|
||||||
@@ -83,7 +83,7 @@ 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 [nil, String, Array, Proc]
|
# @param varref [nil, String, Array, Proc]
|
||||||
@@ -92,7 +92,7 @@ module Rscons
|
|||||||
# Arguments to pass to any lambda variable values to be expanded.
|
# Arguments to pass to any lambda variable values to be expanded.
|
||||||
#
|
#
|
||||||
# @return [nil, String, Array]
|
# @return [nil, String, Array]
|
||||||
# Expanded value with "$\{var}" variable references replaced.
|
# Expanded value with "$!{var}" variable references replaced.
|
||||||
def expand_varref(varref, lambda_args)
|
def expand_varref(varref, lambda_args)
|
||||||
if varref.is_a?(String)
|
if varref.is_a?(String)
|
||||||
if varref =~ /^(.*)\$\{([^}]+)\}(.*)$/
|
if varref =~ /^(.*)\$\{([^}]+)\}(.*)$/
|
||||||
@@ -121,6 +121,26 @@ module Rscons
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Return a Hash containing all variables in the VarSet.
|
||||||
|
#
|
||||||
|
# @since 1.8.0
|
||||||
|
#
|
||||||
|
# This method is not terribly efficient. It is intended to be used only by
|
||||||
|
# debugging code to dump out a VarSet's variables.
|
||||||
|
#
|
||||||
|
# @return [Hash] All variables in the VarSet.
|
||||||
|
def to_h
|
||||||
|
result = deep_dup(@my_vars)
|
||||||
|
@coa_vars.reduce(result) do |result, coa_vars|
|
||||||
|
coa_vars.each_pair do |key, value|
|
||||||
|
unless result.include?(key)
|
||||||
|
result[key] = deep_dup(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
# Move all VarSet variables into the copy-on-access list.
|
# Move all VarSet variables into the copy-on-access list.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
module Rscons
|
module Rscons
|
||||||
# gem version
|
# gem version
|
||||||
VERSION = "1.6.0"
|
VERSION = "1.8.1"
|
||||||
end
|
end
|
||||||
@@ -24,5 +24,6 @@ Gem::Specification.new do |gem|
|
|||||||
gem.add_development_dependency "rake"
|
gem.add_development_dependency "rake"
|
||||||
gem.add_development_dependency "simplecov"
|
gem.add_development_dependency "simplecov"
|
||||||
gem.add_development_dependency "yard"
|
gem.add_development_dependency "yard"
|
||||||
|
gem.add_development_dependency "rdoc"
|
||||||
gem.add_development_dependency "redcarpet"
|
gem.add_development_dependency "redcarpet"
|
||||||
end
|
end
|
||||||
+186
-14
@@ -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|
|
||||||
@@ -226,6 +249,27 @@ describe Rscons do
|
|||||||
]
|
]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'supports simple builders' do
|
||||||
|
test_dir('json_to_yaml')
|
||||||
|
Rscons::Environment.new do |env|
|
||||||
|
require 'json'
|
||||||
|
require 'yaml'
|
||||||
|
env.add_builder(:JsonToYaml) do |target, sources, cache, env, vars|
|
||||||
|
unless cache.up_to_date?(target, :JsonToYaml, sources, env)
|
||||||
|
cache.mkdir_p(File.dirname(target))
|
||||||
|
File.open(target, 'w') do |f|
|
||||||
|
f.write(YAML.dump(JSON.load(IO.read(sources.first))))
|
||||||
|
end
|
||||||
|
cache.register_build(target, :JsonToYaml, sources, env)
|
||||||
|
end
|
||||||
|
target
|
||||||
|
end
|
||||||
|
env.JsonToYaml('foo.yml','foo.json')
|
||||||
|
end
|
||||||
|
expect(File.exists?('foo.yml')).to be_truthy
|
||||||
|
expect(IO.read('foo.yml')).to eq("---\nkey: value\n")
|
||||||
|
end
|
||||||
|
|
||||||
it 'cleans built files' do
|
it 'cleans built files' do
|
||||||
test_dir('build_dir')
|
test_dir('build_dir')
|
||||||
Rscons::Environment.new do |env|
|
Rscons::Environment.new do |env|
|
||||||
@@ -303,11 +347,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 +362,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 +394,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 +416,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
|
||||||
@@ -562,4 +619,119 @@ EOF
|
|||||||
expect(e2.expand_varref("${computed}")).to eq("-H38xyz")
|
expect(e2.expand_varref("${computed}")).to eq("-H38xyz")
|
||||||
expect(env.expand_varref("${lambda_recurse}")).to eq("-Hello")
|
expect(env.expand_varref("${lambda_recurse}")).to eq("-Hello")
|
||||||
end
|
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
|
||||||
|
|
||||||
|
it "supports dumping an Environment's construction variables" do
|
||||||
|
test_dir("simple")
|
||||||
|
env = Rscons::Environment.new do |env|
|
||||||
|
env["CFLAGS"] += %w[-O2 -fomit-frame-pointer]
|
||||||
|
env[:foo] = :bar
|
||||||
|
end
|
||||||
|
env.dump
|
||||||
|
result = lines
|
||||||
|
expect(result.include?(%{:foo => :bar})).to be_truthy
|
||||||
|
expect(result.include?(%{CFLAGS => ["-O2", "-fomit-frame-pointer"]})).to be_truthy
|
||||||
|
expect(result.include?(%{CPPPATH => []})).to be_truthy
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
module Rscons
|
||||||
|
describe Builder do
|
||||||
|
describe "#run" do
|
||||||
|
it "raises an error if called directly and not through a subclass" do
|
||||||
|
expect{subject.run(:target, :sources, :cache, :env, :vars)}.to raise_error /This method must be overridden in a subclass/
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Executable
+16
@@ -0,0 +1,16 @@
|
|||||||
|
module Rscons
|
||||||
|
module Builders
|
||||||
|
describe Command do
|
||||||
|
let(:command) { ['pandoc', '-fmarkdown', '-thtml', '-o${_TARGET}', '${_SOURCES}'] }
|
||||||
|
let(:env) {Environment.new}
|
||||||
|
subject {Command.new}
|
||||||
|
|
||||||
|
|
||||||
|
it "invokes the command to build the target" do
|
||||||
|
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)
|
||||||
|
subject.run("foo.html", ["foo.md"], :cache, env, {'CMD' => command})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
module Rscons
|
||||||
|
module Builders
|
||||||
|
describe SimpleBuilder do
|
||||||
|
let(:env) {Environment.new}
|
||||||
|
|
||||||
|
it "should create a new builder with the given name (as a symbol) and action" do
|
||||||
|
builder = Rscons::Builders::SimpleBuilder.new(:Foo) { 0x1234 }
|
||||||
|
expect(builder.name).to eq("Foo")
|
||||||
|
expect(builder.run(1,2,3,4,5)).to eq(0x1234)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should create a new builder with the given name (as a string) and action" do
|
||||||
|
builder = Rscons::Builders::SimpleBuilder.new("Foo") { 0x1234 }
|
||||||
|
expect(builder.name).to eq("Foo")
|
||||||
|
expect(builder.run(1,2,3,4,5)).to eq(0x1234)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -72,6 +72,13 @@ module Rscons
|
|||||||
env.add_builder(Rscons::Builders::Object.new)
|
env.add_builder(Rscons::Builders::Object.new)
|
||||||
expect(env.builders.keys).to eq ["Object"]
|
expect(env.builders.keys).to eq ["Object"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "adds a new simple builder to the list of builders" do
|
||||||
|
env = Environment.new(exclude_builders: true)
|
||||||
|
expect(env.builders.keys).to eq []
|
||||||
|
env.add_builder(:Foo) {}
|
||||||
|
expect(env.builders.keys).to eq ["Foo"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "#get_build_fname" do
|
describe "#get_build_fname" do
|
||||||
@@ -322,6 +329,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
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
module Rscons
|
module Rscons
|
||||||
describe VarSet do
|
describe VarSet do
|
||||||
|
|
||||||
describe '#initialize' do
|
describe '#initialize' do
|
||||||
it "initializes variables from a Hash" do
|
it "initializes variables from a Hash" do
|
||||||
v = VarSet.new({"one" => 1, "two" => :two})
|
v = VarSet.new({"one" => 1, "two" => :two})
|
||||||
@@ -168,5 +169,35 @@ module Rscons
|
|||||||
expect { v.expand_varref("${bad}", :lambda_args) }.to raise_error /I do not know how to expand a variable reference to a Hash/
|
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
|
||||||
|
|
||||||
|
describe "#to_h" do
|
||||||
|
it "returns a Hash of all variables in the VarSet" do
|
||||||
|
v = VarSet.new({"fuz" => "a string", "foo" => 42, "bar" => :baz,
|
||||||
|
"qax" => [3, 6], "qux" => {a: :b}})
|
||||||
|
v2 = v.merge("baz" => "qux", "qax" => [4, 7])
|
||||||
|
v3 = v2.merge("fuz" => nil)
|
||||||
|
v4 = v3.clone
|
||||||
|
v4["fuz"] = :fuzzy
|
||||||
|
v4["foo"] = 43
|
||||||
|
expect(v.to_h).to eq({
|
||||||
|
"fuz" => "a string", "foo" => 42, "bar" => :baz,
|
||||||
|
"qax" => [3, 6], "qux" => {a: :b},
|
||||||
|
})
|
||||||
|
expect(v2.to_h).to eq({
|
||||||
|
"fuz" => "a string", "foo" => 42, "bar" => :baz,
|
||||||
|
"qax" => [4, 7], "qux" => {a: :b}, "baz" => "qux",
|
||||||
|
})
|
||||||
|
expect(v3.to_h).to eq({
|
||||||
|
"fuz" => nil, "foo" => 42, "bar" => :baz,
|
||||||
|
"qax" => [4, 7], "qux" => {a: :b}, "baz" => "qux",
|
||||||
|
})
|
||||||
|
expect(v4.to_h).to eq({
|
||||||
|
"fuz" => :fuzzy, "foo" => 43, "bar" => :baz,
|
||||||
|
"qax" => [4, 7], "qux" => {a: :b}, "baz" => "qux",
|
||||||
|
})
|
||||||
|
expect(VarSet.new.to_h).to eq({})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Reference in new issue
Block a user