Compare commits

...
44 Commits
Author SHA1 Message Date
josh f5ab51c477 Add ^^/ shortcut to top-level build directory - close #146 2022-01-21 20:45:05 -05:00
josh b0f2bbb7d5 Support environment variable to set rscons build directory - close #145 2022-01-17 18:02:58 -05:00
josh c1dcfa297f Add FileUtils class methods to script DSL - close #144 2022-01-17 16:41:36 -05:00
josh 034dbcd9a6 Update ruby-doc.org URL 2022-01-17 16:03:08 -05:00
josh b5d5fe7a7b Add 'sh' script DSL method - close #142 2022-01-17 15:57:34 -05:00
josh 610b8f1266 Add Size builder - close #143 2022-01-17 12:50:38 -05:00
josh a316c4f922 Support naming environments - close #140 2022-01-13 12:56:23 -05:00
josh 97dbaeb82d Move rsconscache into build directory - close #136 2022-01-09 23:08:28 -05:00
josh a2f72c6b87 v2.3.0 2021-12-04 09:43:03 -05:00
josh 7055cad73c Add v2.3.0 release notes 2021-12-04 09:27:24 -05:00
josh 4548f4e3c9 Document phony targets - close #141 2021-12-01 23:00:12 -05:00
josh 19dbab3426 Run subsidiary rscons binary from rscons() method if found - close #129 2021-11-28 22:36:24 -05:00
josh 69f5bea2b2 Fix specs for dependency file moving - #135 2021-11-28 15:10:25 -05:00
josh 2ee9dda49d Write dependency file to build directory when user invokes Object builder directly - close #135 2021-11-18 20:18:19 -05:00
josh f011b23499 Add PATH manipulation methods - close #126 2021-11-17 21:39:58 -05:00
josh 80d52f25b8 Document CMD_STDOUT variable for Command builder - close #134 2021-11-15 21:52:11 -05:00
josh 0face546e3 Clarify failed command error message indicating to run -F - close #133 2021-11-15 21:45:18 -05:00
josh ca747232cd Support passing a directory to rscons() method - close #128 2021-11-13 11:36:25 -05:00
josh a7b46093e9 Document -f command line option - close #130 2021-11-13 11:07:15 -05:00
josh 09892eed63 Add DEVELOPING.md 2021-11-07 21:09:42 -05:00
josh 117df43f64 Document rscons() build script method for subsidiary build scripts 2021-10-27 23:02:04 -04:00
josh acc12822b6 Add description for user_guide task 2021-10-27 22:42:31 -04:00
josh 8c7b43f60c Show a message when entering/leaving a subsidiary script directory 2021-10-22 16:48:44 -04:00
josh 7c8becc3f9 env.depends() does not work with build-root-relative "^/" paths - close #121 2021-10-22 16:03:10 -04:00
josh d1a35501ef Support subsidiary Rsconscript files - close #125 2021-10-22 13:58:38 -04:00
josh 28a245f0ab build_dist: Remove lines marked just for specs 2021-10-22 13:58:38 -04:00
josh 87a6d9f04f Do not run simplecov on dspec specs 2021-10-22 13:58:38 -04:00
josh 556c821cc6 Commonize error handling across all operations 2021-10-21 18:35:58 -04:00
josh 441fc9bc65 bundle update 2021-09-02 15:37:33 -04:00
josh 1861874f5c bundle update 2021-01-11 15:15:42 -05:00
dependabot[bot] 6d51c5cbde Bump redcarpet from 3.5.0 to 3.5.1
Bumps [redcarpet](https://github.com/vmg/redcarpet) from 3.5.0 to 3.5.1.
- [Release notes](https://github.com/vmg/redcarpet/releases)
- [Changelog](https://github.com/vmg/redcarpet/blob/master/CHANGELOG.md)
- [Commits](https://github.com/vmg/redcarpet/compare/v3.5.0...v3.5.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-11 19:21:14 +00:00
josh 73af83803a add v2.2.0 release notes 2020-10-13 19:04:29 -04:00
josh 353f4f45bc v2.2.0 2020-10-13 18:59:18 -04:00
josh 5bac91f92f add failure messages for failed configuration checks - close #119 2020-10-12 21:01:04 -04:00
josh bbe9563ceb compiler checks should support cross-compilers and freestanding compilers - close #118 2020-10-08 19:23:17 -04:00
josh 5ec74604c6 improve support for MSYS2 - close #120 2020-10-08 19:04:50 -04:00
josh 8e06efe61e v2.1.0 2020-04-16 16:52:58 -04:00
josh 0756874ddb update CHANGELOG for v2.1.0 2020-04-16 16:51:16 -04:00
josh dded5e2648 ruby 2.7 compatibility - #117 2020-04-16 15:48:45 -04:00
josh fbe60f6ba2 stick with simplecov 0.15.x for now 2020-04-16 15:02:01 -04:00
josh 8dd9799666 Revert "update simplecov"
This reverts commit 01ca86de8b.
2020-04-16 15:02:01 -04:00
josh f2c755c8bb update bundle 2020-04-16 13:07:20 -04:00
josh 01ca86de8b update simplecov 2020-02-29 14:27:21 -05:00
josh 943fabd0d1 update some gems 2020-02-29 10:47:46 -05:00
49 changed files with 1322 additions and 377 deletions

No files matched your search

+30
View File
@@ -1,3 +1,33 @@
## v2.3.0
### New Features
- #125 - Support subsidiary Rsconscript files
- #126 - Add PATH manipulation methods
### Fixes
- #121 - env.depends() does not work with built-root-relative "^/" paths
- #130 - Document -f command line option
- #133 - Clarify failed command error message indicating to run -F
- #134 - Document CMD_STDOUT variable for Command builder
- #135 - Write dependency file to build directory when user invokes Object builder directly
- #141 - Document phony targets
## v2.2.0
### New Features
- #120 - improve support for MSYS2
- #119 - add failure messages for failed configuration checks
- #118 - compiler checks should support cross-compilers and freestanding compilers
## v2.1.0
### New Features
- #117 - ruby 2.7 compatibility
## v2.0.2
### Fixes
+14
View File
@@ -0,0 +1,14 @@
# Developing
# Specs
To run the rscons specs, the following commands must be available:
* gcc
* clang
* g++
* clang++
* gdc
* ldc
* flex
* bison
+10 -6
View File
@@ -1,10 +1,14 @@
source 'https://rubygems.org'
gem "json"
gem "rspec"
gem "rake"
gem "simplecov"
gem "yard"
gem "rdoc"
gem "redcarpet"
gem "syntax"
gem "simplecov", "~> 0.15.0"
if RbConfig::CONFIG["host"]["msys"]
gem "json", "2.1.0"
else
gem "json"
gem "yard"
gem "rdoc"
gem "redcarpet"
gem "syntax"
end
+21 -21
View File
@@ -1,32 +1,32 @@
GEM
remote: https://rubygems.org/
specs:
diff-lcs (1.3)
docile (1.3.2)
json (2.2.0)
rake (12.3.2)
rdoc (6.1.1)
redcarpet (3.4.0)
rspec (3.8.0)
rspec-core (~> 3.8.0)
rspec-expectations (~> 3.8.0)
rspec-mocks (~> 3.8.0)
rspec-core (3.8.2)
rspec-support (~> 3.8.0)
rspec-expectations (3.8.4)
diff-lcs (1.4.4)
docile (1.1.5)
json (2.5.1)
rake (13.0.6)
rdoc (6.3.2)
redcarpet (3.5.1)
rspec (3.10.0)
rspec-core (~> 3.10.0)
rspec-expectations (~> 3.10.0)
rspec-mocks (~> 3.10.0)
rspec-core (3.10.1)
rspec-support (~> 3.10.0)
rspec-expectations (3.10.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-mocks (3.8.1)
rspec-support (~> 3.10.0)
rspec-mocks (3.10.2)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-support (3.8.2)
simplecov (0.15.0)
rspec-support (~> 3.10.0)
rspec-support (3.10.2)
simplecov (0.15.1)
docile (~> 1.1.0)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
syntax (1.2.2)
yard (0.9.20)
yard (0.9.26)
PLATFORMS
ruby
@@ -38,9 +38,9 @@ DEPENDENCIES
rdoc
redcarpet
rspec
simplecov
simplecov (~> 0.15.0)
syntax
yard
BUNDLED WITH
1.17.3
2.1.4
+17 -9
View File
@@ -6,7 +6,6 @@ rescue Bundler::BundlerError => e
end
require "rspec/core/rake_task"
require "yard"
require "rake/clean"
require "fileutils"
@@ -18,12 +17,16 @@ task :build_dist do
end
RSpec::Core::RakeTask.new(:spec, :example_string) do |task, args|
ENV["specs"] = "1"
if args.example_string
ENV["partial_specs"] = "1"
task.rspec_opts = %W[-e "#{args.example_string}" -f documentation]
end
end
task :spec => :build_dist
task :spec do
ENV.delete("specs")
end
# dspec task is useful to test the distributable release script, but is not
# useful for coverage information.
@@ -32,16 +35,11 @@ task :dspec, [:example_string] => :build_dist do |task, args|
FileUtils.mkdir_p("test")
FileUtils.cp("dist/rscons", "test/rscons.rb")
ENV["dist_specs"] = "1"
Rake::Task["spec"].invoke(args.example_string)
Rake::Task["spec"].execute(args.example_string)
ENV.delete("dist_specs")
FileUtils.rm_f(Dir.glob(".rscons-*"))
end
YARD::Rake::YardocTask.new do |yard|
yard.files = ['lib/**/*.rb']
yard.options = ["-ogen/yard"]
end
task :gen_large_project, [:size] => :build_dist do |task, args|
size = (args.size || 10000).to_i
FileUtils.rm_rf("large_project")
@@ -88,8 +86,17 @@ EOF
FileUtils.cp("dist/rscons", "large_project")
end
task :user_guide do
system("ruby", "-Ilib", "rb/gen_user_guide.rb")
unless RbConfig::CONFIG["host"]["msys"]
require "yard"
YARD::Rake::YardocTask.new do |yard|
yard.files = ['lib/**/*.rb']
yard.options = ["-ogen/yard"]
end
desc "Build user guide"
task :user_guide do
system("ruby", "-Ilib", "rb/gen_user_guide.rb")
end
end
task :default => :spec
@@ -97,6 +104,7 @@ task :default => :spec
task :all => [
:build_dist,
:spec,
:dspec,
:yard,
:user_guide,
]
@@ -1,4 +1,5 @@
configure do
check_d_compiler
check_d_import "std.stdio", check_d_import_path: ["./usr1"]
check_d_import "frobulous", check_d_import_path: ["./usr2"]
end
@@ -1,3 +1,4 @@
configure do
check_d_compiler
check_d_import "std.stdio"
end
+5
View File
@@ -0,0 +1,5 @@
configure do
check_c_compiler "foo123c", fail: false, on_fail: "Install the foo123 package"
check_d_compiler "foo123d", fail: false
check_cxx_compiler "foo123cxx", on_fail: lambda {puts "Install the foo123cxx package"}
end
@@ -1,6 +0,0 @@
build do
Environment.new do |env|
env.Program("program.exe", "program.c")
env.Install("install/program.exe", "program.exe")
end
end
@@ -1,8 +0,0 @@
#include <stdio.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
sleep(2);
return 0;
}
+4
View File
@@ -0,0 +1,4 @@
build do
sh "echo", "hi there"
sh(["echo 1 2"])
end
+4
View File
@@ -0,0 +1,4 @@
build do
sh "foobar42"
sh "echo", "continued"
end
+4
View File
@@ -0,0 +1,4 @@
build do
sh "foobar42", continue: true
sh "echo", "continued"
end
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
echo "foobar!"
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
exit 42
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
echo "flex!"
+10
View File
@@ -0,0 +1,10 @@
path_prepend "path_prepend"
path_append "path_append"
build do
Environment.new do |env|
system("flex")
system("foobar")
env.Object("simple.o", "simple.c")
end
end
+6
View File
@@ -0,0 +1,6 @@
build do
Environment.new do |env|
env.Program("simple.exe", glob("*.c"))
env.Size("simple.size", "simple.exe")
end
end
@@ -0,0 +1,18 @@
class FileBuilder < Builder
def self.name
"File"
end
def run(options)
FileUtils.mkdir_p(File.dirname(@target))
File.binwrite(@target, ENV["file_contents"])
true
end
end
build do
Environment.new do |env|
env.add_builder(FileBuilder)
env.File("^/file.txt")
program = env.Program("^/simple.exe", Dir["*.c"])
env.depends("^/simple.exe", "^/file.txt")
end
end
+10
View File
@@ -0,0 +1,10 @@
configure do
rscons "sub/Rsconscript"
rscons "sub/Rsconscript2", "configure"
puts "top configure"
end
build do
rscons "sub/Rsconscript2", "build"
puts "top build"
end
+10
View File
@@ -0,0 +1,10 @@
configure do
rscons "sub"
rscons "sub", "-f", "Rsconscript2", "configure"
puts "top configure"
end
build do
rscons "sub", "-f", "Rsconscript2", "build"
puts "top build"
end
+9
View File
@@ -0,0 +1,9 @@
configure do
rscons "sub/Rsconscript_fail"
puts "top configure"
end
build do
rscons "sub/Rsconscript2", "build"
puts "top build"
end
@@ -0,0 +1,4 @@
build do
rscons "second", "build"
puts "top build"
end
+3
View File
@@ -0,0 +1,3 @@
build do
puts "second build"
end
+7
View File
@@ -0,0 +1,7 @@
configure do
puts "sub Rsconscript configure"
end
build do
puts "sub Rsconscript build"
end
+7
View File
@@ -0,0 +1,7 @@
configure do
puts "sub Rsconscript2 configure"
end
build do
puts "sub Rsconscript2 build"
end
@@ -0,0 +1,8 @@
configure do
puts "sub Rsconscript configure"
check_program "foobarfailure"
end
build do
puts "sub Rsconscript build"
end
+6
View File
@@ -0,0 +1,6 @@
build do
Environment.new(name: "typical") do |env|
env["CPPPATH"] += glob("src/**")
env.Program("^/typical.exe", glob("src/**/*.c"))
end
end
+3 -2
View File
@@ -1,9 +1,10 @@
build do
Environment.new(echo: :command) do |env|
env.append('CPPPATH' => glob('src/**').sort)
env.append("CPPPATH" => glob("src/**"))
FileUtils.mkdir_p(env.build_root)
FileUtils.mv("src/one/one.c", env.build_root)
FileUtils.mv("src/two/two.c", Rscons.application.build_dir)
env.Object("^/one.o", "^/one.c")
env.Program("program.exe", glob('src/**/*.c') + ["^/one.o"])
env.Program("^^/program.exe", ["^/one.o", "^^/two.c"])
end
end
+9
View File
@@ -0,0 +1,9 @@
build do
base_env = Environment.new do |env|
env["CPPPATH"] += glob("src/**")
end
base_env.clone(name: "typical") do |env|
env.Program("^/typical.exe", glob("src/**/*.c"))
end
end
+11
View File
@@ -0,0 +1,11 @@
build do
mkdir "foo"
cd "foo" do
mkdir_p ["bar/baz", "bar/booz"]
end
mv "foo/bar", "foobar"
rmdir "foo"
touch "foobar/booz/a.txt"
cp "foobar/booz/a.txt", "foobar/baz/b.txt"
rm_rf "foobar/booz"
end
+281 -60
View File
@@ -78,10 +78,22 @@ cache file in order to avoid rebuilding a target when it is already up to date.
### Build Directory
Rscons was designed to store temporary build artifacts (for example, object
files, dependency files, etc...) in a `build` directory.
files, dependency files, etc...) and build system metadata in a
"build directory".
This keeps files generated by the build cleanly separated from user-controlled
source files.
In contrast to other build systems or build system generators, rscons executes
from the project base directory (up to the user) rather than executing from
*within* the build directory.
This keeps any file paths printed by compilers (such as in warning or error
messages) accurate relative to the project directory, so that the user does not
need to translate any paths to the correct path within a terminal or editor
application, for example.
By default a build directory named "build" is used, but this can be overridden
by the user by using the `-b`/`--build` command-line option.
## Getting Started
To use Rscons on your project, you must:
@@ -94,7 +106,7 @@ To use Rscons on your project, you must:
Rscons is designed to be distributed as a stand-alone single file script that
can be copied into and versioned in a project's source tree.
The only dependency required to run Rscons is to have a Ruby interpreter
The only requirement to run Rscons is that the system has a Ruby interpreter
installed.
The latest release can be downloaded from [https://github.com/holtrop/rscons/releases](https://github.com/holtrop/rscons/releases).
Simply copy the `rscons` executable script into the desired location within
@@ -209,6 +221,171 @@ called `myprog.exe` which is to be built from all C source files found
The `Rsconscript` file is a Ruby script.
##> Build Script Methods
`rscons` provides several methods that a build script can use.
* `glob` (see ${#Finding Files: The glob Method})
* `path_append` (see ${#PATH Management})
* `path_components` (see ${#PATH Management})
* `path_prepend` (see ${#PATH Management})
* `path_set` (see ${#PATH Management})
* `rscons` (see ${#Using Subsidiary Build Scripts: The rscons Method})
* `sh` (see (${#Executing Commands: The sh Method})
Additionally, the following methods from the Ruby
[FileUtils](https://ruby-doc.org/stdlib-3.1.0/libdoc/fileutils/rdoc/FileUtils.html)
module are made available for the build script to call directly:
* `cd`
* `chmod`
* `chmod_R`
* `chown`
* `chown_R`
* `cp`
* `cp_lr`
* `cp_r`
* `install`
* `ln`
* `ln_s`
* `ln_sf`
* `mkdir`
* `mkdir_p`
* `mv`
* `pwd`
* `rm`
* `rm_f`
* `rm_r`
* `rm_rf`
* `rmdir`
* `touch`
###> Finding Files: The glob Method
The [`glob`](../yard/Rscons/Script/GlobalDsl.html#glob-instance_method) method can be
used to find files matching the patterns specified.
It supports a syntax similar to the Ruby [Dir.glob method](https://ruby-doc.org/core-3.1.0/Dir.html#method-c-glob) but operates more deterministically.
Example use:
```ruby
build do
Environment.new do |env|
env.Program("mytests", glob("src/**/*.cc", "test/**/*.cc"))
end
end
```
This example would build the `mytests` executable from all `.cc` source files
found recursively under the `src` or `test` directory.
###> PATH Management
`rscons` provides methods for management of the `PATH` environment variable.
The
[`path_append`](../yard/Rscons/Script/GlobalDsl.html#path_append-instance_method)
and
[`path_prepend`](../yard/Rscons/Script/GlobalDsl.html#path_prepend-instance_method)
methods can be used to append or prepend a path to the `PATH` environment
variable.
```ruby
path_prepend "i686-elf-gcc/bin"
```
The
[`path_set`](../yard/Rscons/Script/GlobalDsl.html#path_set-instance_method)
method sets the `PATH` environment variable to the given Array or String.
The
[`path_components`](../yard/Rscons/Script/GlobalDsl.html#path_components-instance_method)
method returns an Array of the components in the `PATH`
environment variable.
###> Using Subsidiary Build Scripts: The rscons Method
The
[`rscons`](../yard/Rscons/Script/GlobalDsl.html#rscons-instance_method)
build script method can be used to invoke an rscons subprocess to
perform an operation using a subsidiary rscons build script.
This can be used, for example, when a subproject is imported and a top-level
`configure` or `build` operation should also perform the same operation in the
subproject directory.
The first argument to the `rscons` method specifies either a directory name, or
the path to the subsidiary Rsconscript file to execute.
Any additional arguments are passed to `rscons` when it executes the subsidiary
script.
`rscons` will change working directories to the directory containing the
subsidiary script when executing it.
For example:
```ruby
configure do
rscons "subproject", "configure"
end
build do
rscons "subproject/Rsconscript", "build"
end
```
It is also perfectly valid to perform a different operation in the subsidiary
script from the one being performed in the top-level script.
For example, in a project that requires a particular cross compiler, the
top-level `configure` script could build the necessary cross compiler using a
subsidiary build script.
This could look something like:
```ruby
configure do
rscons "cross/Rsconscript"
check_c_compiler "i686-elf-gcc"
end
```
This would build, and if necessary first configure, using the cross/Rsconscript
subsidiary build script.
Subsidiary build scripts are executed from within the directory containing the
build script.
###> Executing Commands: The sh Method
The
[`sh`](../yard/Rscons/Script/GlobalDsl.html#sh-instance_method)
build script method can be used to directly execute commands.
The `sh` method accepts either a single String argument or an Array of Strings.
When an Array is given, if the array length is greater than 1, then the command
will not be executed and interpreted by the system shell.
Otherwise, it will be executed and interpreted by the system shell.
For example:
```ruby
build do
# Run "make" in imported "subcomponent" directory.
sh "cd subcomponent; make"
# Move a file around.
sh "mv", "subcomponent/file with spaces.txt", "new_name.txt"
end
```
If the command fails, rscons will normally print the error and terminate
execution.
If the `:continue` option is set, then rscons will not terminate execution.
For example:
```ruby
build do
# This command will fail and a message will be printed.
sh "false", continue: true
# However, due to the :continue option being set, execution will continue.
sh "echo hi"
end
```
##> Configuration Operations
A `configure` block is optional.
@@ -246,6 +423,15 @@ configure do
end
```
Global configuration options may be supplied to the compiler checks as well.
Example:
```ruby
configure do
check_c_compiler "x86_64-elf-gcc", on_fail: "Install x86_64-elf cross toolchain first!"
end
```
###> Checking for a Header File
The following methods can be used to check for the presence of a header file:
@@ -272,18 +458,6 @@ end
Optionally specifies an array of paths to look for the header file in.
##### `:fail`
If the `:fail` option is set to `false`, then the absence of the header file
will not result in the configure option failing.
The `:fail` option defaults to `true` if the `:set_define` option is not
defined, and defaults to `false` if the `:set_define` option is defined.
##### `:set_define`
If set, a build define of the specified String will be added to the
`CPPDEFINES` construction variable array if the requested header is found.
###> Checking for a D Import
The `check_d_import` method can be used to check for the presence of D import.
@@ -327,18 +501,6 @@ end
Optionally specifies an array of paths to look for the library in.
##### `:fail`
If the `:fail` option is set to `false`, then the absence of the library
will not result in the configure option failing.
The `:fail` option defaults to `true` if the `:set_define` option is not
defined, and defaults to `false` if the `:set_define` option is defined.
##### `:set_define`
If set, a build define of the specified String will be added to the
`CPPDEFINES` construction variable array if the requested library is found.
##### `:use`
If not set, the library will be used by default in all `Environment` objects.
@@ -387,18 +549,6 @@ used to look for package configuration flags for the specified package.
If the `:program` option is given, the program specified will be used to look
for configuration flags.
##### `:fail`
If the `:fail` option is set to `false`, then the absence of the package or
program requested will not result in the configure option failing.
The `:fail` option defaults to `true` if the `:set_define` option is not
defined, and defaults to `false` if the `:set_define` option is defined.
##### `:set_define`
If set, a build define of the specified String will be added to the
`CPPDEFINES` construction variable array if the requested package is found.
##### `:use`
If not set, the library will be used by default in all `Environment` objects.
@@ -457,6 +607,46 @@ configure do
end
```
###> Global Configuration Check Options
#### `:fail`
If the `:fail` option is set to `false`, then the absence of the package or
program requested will not result in the configure option failing.
The `:fail` option defaults to `true` if the `:set_define` option is not
defined, and defaults to `false` if the `:set_define` option is defined.
#### `:on_fail`
The `:on_fail` option can be set to a String or a Proc object. If the
configuration operation fails (or would fail), the given message is printed
or the Proc is called.
Examples:
```ruby
configure do
check_c_compiler "special-gcc", on_fail: "First install special gcc!"
end
configure do
package_hint = lambda do
puts "The following packages must be installed to build this project:"
puts "- libsdl2-dev"
puts "- libsdl2-image-dev"
puts "- libsdl2-net-dev"
end
check_lib "SDL2", on_fail: package_hint
check_lib "SDL2_image", on_fail: package_hint
check_lib "SDL2_net", on_fail: package_hint
end
```
#### `:set_define`
If set, a build define of the specified String will be added to the
`CPPDEFINES` construction variable array if the requested package is found.
##> Build Operations
The `build` block is used to create Environments and register build targets.
@@ -479,21 +669,26 @@ source files found recursively under the `src` directory.
An Environment includes:
- a name
- a collection of construction variables
- a collection of build hooks
- a collection of user-registered build targets
- a build root
All build targets must be registered within an `Environment`.
If the user does not specify a name for the environment, a name will be
automatically generated based on the Environment's internal ID, for example
"e.1".
The Environment's build root is a directory created within the top-level
Rscons build directory.
It is based on the Environment name.
By default it holds all intermediate files generated by Rscons that are needed
to produce a user-specified build target.
For example, for the `Rsconscript`:
```ruby
build do
Environment.new do |env|
Environment.new(name: "myproj") do |env|
env.Program("myprog.exe", glob("src/**/*.c"))
end
end
@@ -501,28 +696,11 @@ end
Rscons will place an object file and dependency file corresponding to each C
source file under the Environment's build root.
Assuming a top-level build directory of "build", the Environment's build root
would be "build/myproj".
This keeps the intermediate generated build artifacts separate from the source
files.
###> Specifying Source Files: The glob Method
The [`glob`](../yard/Rscons/Script/Dsl.html#glob-instance_method) method can be
used to find files matching the patterns specified.
It supports a syntax similar to the Ruby [Dir.glob method](https://ruby-doc.org/core-2.5.1/Dir.html#method-c-glob) but operates more deterministically.
Example use:
```ruby
build do
Environment.new do |env|
env.Program("mytests", glob("src/**/*.cc", "test/**/*.cc"))
end
end
```
This example would build the `mytests` executable from all `.cc` source files
found recursively under the `src` or `test` directory.
###> Construction Variables
Construction variables are values assigned to keys within an Environment.
@@ -593,20 +771,33 @@ There are several default builders that are built-in to Rscons:
library.
* `SharedObject`, which compiles source files to produce an object file, in a
way that is able to be used to create a shared library.
* `Size`, which runs the 'size' utility on an executable file.
####> The Command Builder
```ruby
env.Command(target, sources, "CMD" => command)
# Example
env.Command("docs.html", "docs.md",
env.Command("user_guide.html", "user_guide.md",
"CMD" => ["pandoc", "-fmarkdown", "-thtml", "-o${_TARGET}", "${_SOURCES}"],
"CMD_DESC" => "PANDOC")
"CMD_DESC" => "Generating user guide:")
```
The `Command` builder executes a user-defined command in order to produce the
desired target file based on the provided source files.
The `Command` builder supports the following construction variables:
* `CMD` (required) specifies the command to execute (an array of strings).
`CMD` is expanded for variable references, so the tokens `${_TARGET}` and
`${_SOURCES}` can be used, for example.
* `CMD_DESC` (optional) specifies the short text description to print when
the builder executes. The given description is followed by the target file
name.
* `CMD_STDOUT` (optional) specifies a file to redirect standard output to.
`CMD_STDOUT` is expanded for variable references, so the token `${_TARGET}`
can be used, for example.
####> The CFile Builder
```ruby
@@ -796,6 +987,36 @@ allows it to be used to create a shared library are added.
Although it can be called explicitly, it is more commonly implicitly called by
the `SharedLibrary` builder.
####> The Size Builder
```ruby
env.Size(target, sources)
# Example
env.Program("program.exe", glob("*.c"))
env.Size("program.size", "program.exe")
```
The `Size` builder runs the "size" executable on the given source file and
stores its output in the target file.
The size executable can be specified with the `SIZE` construction variable,
and flags can be specified with `SIZEFLAGS`.
###> Phony Targets
rscons supports phony build targets.
Normally, a builder produces an output file, and executes whenever the input
files or command have changed.
A phony build target can be used to register a builder that does not produce
an output file.
A custom builder can take some action when the input files change even if it
does not produce an output file.
Such a builder could perform verification or run a test on its source files,
possibly failing if some conditions are not met.
It could also simply output something to the console, such as an analysis of
the source file, whenever it changes.
A phony target is signified by passing a Symbol instead of a String as the
first parameter (target) to a builder method.
###> Explicit Dependencies
A target can be marked as depending on another file that Rscons would not
+6 -4
View File
@@ -33,10 +33,11 @@ module Rscons
:Program,
:SharedLibrary,
:SharedObject,
:Size,
]
# Class to represent a fatal error while building a target.
class BuildError < RuntimeError; end
# Class to represent a fatal error during an Rscons operation.
class RsconsError < RuntimeError; end
class << self
@@ -81,7 +82,7 @@ module Rscons
end
if ENV["SHELL"] and ENV["SHELL"] != "" and test_shell[ENV["SHELL"], "-c"]
[ENV["SHELL"], "-c"]
elsif Object.const_get("RUBY_PLATFORM") =~ /mingw/
elsif Object.const_get("RUBY_PLATFORM") =~ /mingw|msys/
if test_shell["sh", "-c"]
# Using Rscons from MSYS should use MSYS's shell.
["sh", "-c"]
@@ -102,7 +103,7 @@ module Rscons
# @return [Array<String>] Command used to execute commands.
def command_executer
@command_executer ||=
if Object.const_get("RUBY_PLATFORM") =~ /mingw/
if Object.const_get("RUBY_PLATFORM") =~ /mingw|msys/
if ENV.keys.find {|key| key =~ /MSYS/}
begin
if IO.popen(["env", "echo", "success"]) {|io| io.read.strip} == "success"
@@ -146,6 +147,7 @@ require_relative "rscons/builders/program"
require_relative "rscons/builders/shared_library"
require_relative "rscons/builders/shared_object"
require_relative "rscons/builders/simple_builder"
require_relative "rscons/builders/size"
# language support
require_relative "rscons/builders/lang/asm"
+1 -1
View File
@@ -84,7 +84,7 @@ module Rscons
# @return [Boolean]
# Whether to output ANSI color escape codes.
def do_ansi?(io)
if RUBY_PLATFORM =~ /mingw/
if RUBY_PLATFORM =~ /mingw|msys/
(ENV["TERM"] == "xterm") && %w[fifo characterSpecial].include?(io.stat.ftype)
else
io.tty?
+13 -7
View File
@@ -5,6 +5,10 @@ module Rscons
# Functionality for an instance of the rscons application invocation.
class Application
# @return [String]
# Top-level build directory.
attr_accessor :build_dir
# @return [Boolean]
# Whether to output ANSI color escape sequences.
attr_accessor :do_ansi_color
@@ -23,6 +27,8 @@ module Rscons
# Create Application instance.
def initialize
@build_dir = ENV["RSCONS_BUILD_DIR"] || "build"
ENV.delete("RSCONS_BUILD_DIR")
@n_threads = Util.determine_n_threads
@vars = VarSet.new
@operations = Set.new
@@ -136,8 +142,8 @@ module Rscons
env.process
end
0
rescue BuildError => be
Ansi.write($stderr, :red, be.message, :reset, "\n")
rescue RsconsError => e
Ansi.write($stderr, :red, e.message, :reset, "\n")
1
end
end
@@ -171,11 +177,8 @@ module Rscons
# Exit code.
def distclean
cache = Cache.instance
build_dir = cache["configuration_data"]["build_dir"]
clean
if build_dir
FileUtils.rm_rf(build_dir)
end
FileUtils.rm_rf(@build_dir)
cache.clear
0
end
@@ -193,7 +196,10 @@ module Rscons
co = ConfigureOp.new(options)
begin
@script.configure(co)
rescue ConfigureOp::ConfigureFailure
rescue RsconsError => e
if e.message and e.message != ""
$stderr.puts e.message
end
Ansi.write($stderr, :red, "Configuration failed", :reset, "\n")
rv = 1
end
+3 -3
View File
@@ -75,10 +75,10 @@ module Rscons
@vars["_SOURCES"] = @sources
depfilesuffix = @env.expand_varref("${DEPFILESUFFIX}", vars)
@vars["_DEPFILE"] =
if @vars[:direct]
@env.get_build_fname(target, depfilesuffix, self.class)
if @vars[:direct] || !@target.start_with?("#{@env.build_root}/")
@env.get_build_fname(@target, depfilesuffix, self.class)
else
"#{target}#{depfilesuffix}"
"#{@target}#{depfilesuffix}"
end
@cache.mkdir_p(File.dirname(@vars["_DEPFILE"]))
command = @env.build_command(@command_template, @vars)
+24
View File
@@ -0,0 +1,24 @@
module Rscons
module Builders
# Run the "size" utility on an executable and store its results in the
# target file.
# input file.
#
# Examples::
# env.Size("^/project.size", "^/project.elf")
class Size < Builder
# Run the builder to produce a build target.
def run(options)
if @command
finalize_command
else
@vars["_SOURCES"] = @sources
command = @env.build_command("${SIZECMD}", @vars)
standard_command("Size <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command, stdout: @target)
end
end
end
end
end
+9 -7
View File
@@ -51,9 +51,6 @@ module Rscons
# }
class Cache
# Name of the file to store cache information in
CACHE_FILE = ".rsconscache"
# Prefix for phony cache entries.
PHONY_PREFIX = ":PHONY:"
@@ -70,6 +67,11 @@ module Rscons
initialize!
end
# Get the path to the cache file.
def cache_file
File.join(Rscons.application.build_dir, ".rsconscache")
end
# Access cache value.
def [](key)
@cache[key]
@@ -84,7 +86,7 @@ module Rscons
#
# @return [void]
def clear
FileUtils.rm_f(CACHE_FILE)
FileUtils.rm_f(cache_file)
initialize!
end
@@ -100,7 +102,7 @@ module Rscons
# @return [void]
def write
@cache["version"] = VERSION
File.open(CACHE_FILE, "w") do |fh|
File.open(cache_file, "w") do |fh|
fh.puts(JSON.dump(@cache))
end
end
@@ -360,9 +362,9 @@ module Rscons
# Create a Cache object and load in the previous contents from the cache
# file.
def initialize!
@cache = JSON.load(File.read(CACHE_FILE)) rescue {}
@cache = JSON.load(File.read(cache_file)) rescue {}
unless @cache.is_a?(Hash)
$stderr.puts "Warning: #{CACHE_FILE} was corrupt. Contents:\n#{@cache.inspect}"
$stderr.puts "Warning: #{cache_file} was corrupt. Contents:\n#{@cache.inspect}"
@cache = {}
end
@cache["targets"] ||= {}
+8 -10
View File
@@ -6,11 +6,14 @@ USAGE = <<EOF
Usage: #{$0} [global options] [operation] [operation options]
Global options:
-b BUILD, --build=BUILD Set build directory (default: build)
-f FILE Use FILE as Rsconscript
-F, --show-failure Show failed command log from previous build and exit
--version Show rscons version and exit
-h, --help Show rscons help and exit
-j N, --nthreads=N Set number of threads (local default: #{Rscons.application.n_threads})
-r COLOR, --color=COLOR Set color mode (off, auto, force)
-v, --verbose Run verbosely
--version Show rscons version and exit
Operations:
configure Configure the project
@@ -21,12 +24,7 @@ Operations:
uninstall Uninstall project from installation destination
Configure options:
-b BUILD, --build=BUILD Set build directory (default: build)
--prefix=PREFIX Set installation prefix (default: /usr/local)
Build options:
-j N, --nthreads=N Set number of threads (local default: #{Rscons.application.n_threads})
EOF
module Rscons
@@ -58,6 +56,10 @@ module Rscons
private
def add_global_options(opts)
opts.on("-b", "--build DIR") do |build_dir|
Rscons.application.build_dir = build_dir
end
opts.on("-j NTHREADS") do |n_threads|
Rscons.application.n_threads = n_threads.to_i
end
@@ -158,10 +160,6 @@ module Rscons
end
def parse_configure_args(opts, argv, options)
opts.on("-b", "--build DIR") do |build_dir|
options[:build_dir] = build_dir
end
opts.on("--prefix PREFIX") do |prefix|
options[:prefix] = prefix
end
+46 -53
View File
@@ -5,37 +5,29 @@ module Rscons
# Class to manage a configure operation.
class ConfigureOp
# Exception raised when a configuration error occurs.
class ConfigureFailure < Exception; end
# Create a ConfigureOp.
#
# @param options [Hash]
# Optional parameters.
# @option options [String] :build_dir
# Build directory.
# @option options [String] :prefix
# Install prefix.
# @option options [String] :project_name
# Project name.
def initialize(options)
# Default options.
options[:build_dir] ||= "build"
options[:prefix] ||= "/usr/local"
@work_dir = "#{options[:build_dir]}/configure"
@work_dir = "#{Rscons.application.build_dir}/_configure"
FileUtils.mkdir_p(@work_dir)
@log_fh = File.open("#{@work_dir}/config.log", "wb")
cache = Cache.instance
cache["failed_commands"] = []
cache["configuration_data"] = {}
cache["configuration_data"]["build_dir"] = options[:build_dir]
cache["configuration_data"]["prefix"] = options[:prefix]
if project_name = options[:project_name]
Ansi.write($stdout, "Configuring ", :cyan, project_name, :reset, "...\n")
else
$stdout.puts "Configuring project..."
end
Ansi.write($stdout, "Setting build directory... ", :green, options[:build_dir], :reset, "\n")
Ansi.write($stdout, "Setting prefix... ", :green, options[:prefix], :reset, "\n")
store_merge("prefix" => options[:prefix])
end
@@ -62,6 +54,10 @@ module Rscons
# @return [void]
def check_c_compiler(*ccc)
$stdout.write("Checking for C compiler... ")
options = {}
if ccc.last.is_a?(Hash)
options = ccc.slice!(-1)
end
if ccc.empty?
# Default C compiler search array.
ccc = %w[gcc clang]
@@ -69,7 +65,7 @@ module Rscons
cc = ccc.find do |cc|
test_c_compiler(cc)
end
complete(cc ? 0 : 1, success_message: cc)
complete(cc ? 0 : 1, options.merge(success_message: cc))
end
# Check for a working C++ compiler.
@@ -80,6 +76,10 @@ module Rscons
# @return [void]
def check_cxx_compiler(*ccc)
$stdout.write("Checking for C++ compiler... ")
options = {}
if ccc.last.is_a?(Hash)
options = ccc.slice!(-1)
end
if ccc.empty?
# Default C++ compiler search array.
ccc = %w[g++ clang++]
@@ -87,7 +87,7 @@ module Rscons
cc = ccc.find do |cc|
test_cxx_compiler(cc)
end
complete(cc ? 0 : 1, success_message: cc)
complete(cc ? 0 : 1, options.merge(success_message: cc))
end
# Check for a working D compiler.
@@ -98,6 +98,10 @@ module Rscons
# @return [void]
def check_d_compiler(*cdc)
$stdout.write("Checking for D compiler... ")
options = {}
if cdc.last.is_a?(Hash)
options = cdc.slice!(-1)
end
if cdc.empty?
# Default D compiler search array.
cdc = %w[gdc ldc2]
@@ -105,7 +109,7 @@ module Rscons
dc = cdc.find do |dc|
test_d_compiler(dc)
end
complete(dc ? 0 : 1, success_message: dc)
complete(dc ? 0 : 1, options.merge(success_message: dc))
end
# Check for a package or configure program output.
@@ -387,11 +391,15 @@ module Rscons
else
!options[:set_define]
end
color = should_fail ? :red : :yellow
Ansi.write($stdout, color, "#{fail_message}\n")
if options[:on_fail].is_a?(String)
$stdout.puts(options[:on_fail])
elsif options[:on_fail].is_a?(Proc)
options[:on_fail].call
end
if should_fail
Ansi.write($stdout, :red, "#{fail_message}\n")
raise ConfigureFailure.new
else
Ansi.write($stdout, :yellow, "#{fail_message}\n")
raise RsconsError.new
end
end
end
@@ -408,22 +416,17 @@ module Rscons
def test_c_compiler(cc)
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
fh.puts <<-EOF
#include <stdio.h>
int main(int argc, char * argv[]) {
printf("Success.\\n");
return 0;
int fun(int val) {
return val * 2;
}
EOF
end
command = %W[#{cc} -o #{@work_dir}/cfgtest.exe #{@work_dir}/cfgtest.c]
command = %W[#{cc} -c -o #{@work_dir}/cfgtest.o #{@work_dir}/cfgtest.c]
merge = {"CC" => cc}
_, _, status = log_and_test_command(command)
if status == 0
stdout, _, status = log_and_test_command(["#{@work_dir}/cfgtest.exe"])
if status == 0 and stdout =~ /Success/
store_merge(merge)
true
end
store_merge(merge)
true
end
end
@@ -437,23 +440,18 @@ module Rscons
def test_cxx_compiler(cc)
File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh|
fh.puts <<-EOF
#include <iostream>
using namespace std;
int main(int argc, char * argv[]) {
cout << "Success." << endl;
return 0;
template<typename T>
T fun(T val) {
return val * 2;
}
EOF
end
command = %W[#{cc} -o #{@work_dir}/cfgtest.exe #{@work_dir}/cfgtest.cxx]
command = %W[#{cc} -c -o #{@work_dir}/cfgtest.o #{@work_dir}/cfgtest.cxx]
merge = {"CXX" => cc}
_, _, status = log_and_test_command(command)
if status == 0
stdout, _, status = log_and_test_command(["#{@work_dir}/cfgtest.exe"])
if status == 0 and stdout =~ /Success/
store_merge(merge)
true
end
store_merge(merge)
true
end
end
@@ -467,9 +465,8 @@ module Rscons
def test_d_compiler(dc)
File.open("#{@work_dir}/cfgtest.d", "wb") do |fh|
fh.puts <<-EOF
import std.stdio;
int main() {
writeln("Success.");
import core.math;
int fun() {
return 0;
}
EOF
@@ -477,29 +474,25 @@ module Rscons
[:gdc, :ldc2].find do |dc_test|
case dc_test
when :gdc
command = %W[#{dc} -o #{@work_dir}/cfgtest.exe #{@work_dir}/cfgtest.d]
command = %W[#{dc} -c -o #{@work_dir}/cfgtest.o #{@work_dir}/cfgtest.d]
merge = {"DC" => dc}
when :ldc2
command = %W[#{dc} -of #{@work_dir}/cfgtest.exe #{@work_dir}/cfgtest.d]
# ldc2 on Windows expect an object file suffix of .obj.
ldc_objsuffix = RUBY_PLATFORM =~ /mingw|msys/ ? ".obj" : ".o"
command = %W[#{dc} -c -of #{@work_dir}/cfgtest#{ldc_objsuffix} #{@work_dir}/cfgtest.d]
env = BasicEnvironment.new
merge = {
"DC" => dc,
"DCCMD" => env["DCCMD"].map {|e| if e == "-o"; "-of"; else; e; end},
"LDCMD" => env["LDCMD"].map {|e| if e == "-o"; "-of"; else; e; end},
"DCCMD" => env["DCCMD"].map {|e| e.sub(/^-o$/, "-of")},
"LDCMD" => env["LDCMD"].map {|e| e.sub(/^-o$/, "-of")},
"DDEPGEN" => ["-deps=${_DEPFILE}"],
}
if RUBY_PLATFORM =~ /mingw/
# ldc2 on Windows expect an object file suffix of .obj.
merge["OBJSUFFIX"] = %w[.obj]
end
merge["OBJSUFFIX"] = [ldc_objsuffix]
end
_, _, status = log_and_test_command(command)
if status == 0
stdout, _, status = log_and_test_command(["#{@work_dir}/cfgtest.exe"])
if status == 0 and stdout =~ /Success/
store_merge(merge)
true
end
store_merge(merge)
true
end
end
end
+4 -1
View File
@@ -1,6 +1,6 @@
module Rscons
on_windows = RUBY_PLATFORM =~ /mingw|cygwin/
on_windows = RUBY_PLATFORM =~ /mingw|msys|cygwin/
pic_flags = on_windows ? [] : %w[-fPIC]
# Default Rscons construction variables.
@@ -80,6 +80,9 @@ module Rscons
"SHLIBLINKPREFIX" => "-l",
"SHLIBPREFIX" => on_windows ? "" : "lib",
"SHLIBSUFFIX" => on_windows ? ".dll" : ".so",
"SIZE" => "size",
"SIZECMD" => %w[${SIZE} ${SIZEFLAGS} ${_SOURCES}],
"SIZEFLAGS" => [],
"YACC" => "bison",
"YACCSUFFIX" => %w[.y .yy],
"YACC_CMD" => %w[${YACC} ${YACC_FLAGS} -o ${_TARGET} ${_SOURCES}],
+22 -16
View File
@@ -1,7 +1,6 @@
require "fileutils"
require "set"
require "shellwords"
require "thwait"
module Rscons
# The Environment class is the main programmatic interface to Rscons. It
@@ -52,6 +51,10 @@ module Rscons
# global Rscons.application.n_threads value.
attr_accessor :n_threads
# @return [String]
# Environment name.
attr_reader :name
# Create an Environment object.
#
# @param options [Hash]
@@ -59,6 +62,9 @@ module Rscons
# :command, :short, or :off (default :short)
# @option options [Boolean] :exclude_builders
# Whether to omit adding default builders (default false)
# @option options [String, nil] :name
# Environment name. This determines the folder name used to store all
# environment build files under the top-level build directory.
# @option options [String, Array<String>] :use
# Use flag(s). If specified, any configuration flags which were saved
# with a corresponding `:use` value will be applied to this Environment.
@@ -98,7 +104,8 @@ module Rscons
else
:short
end
@build_root = "#{Cache.instance["configuration_data"]["build_dir"]}/e.#{@id}"
@name = options[:name] || "e.#{@id}"
@build_root = "#{Rscons.application.build_dir}/#{@name}"
@n_threads = Rscons.application.n_threads
if block_given?
@@ -126,14 +133,14 @@ module Rscons
#
# @return [Environment] The newly created {Environment} object.
def clone(options = {})
options = options.dup
clone = options[:clone] || :all
clone = Set[:variables, :builders, :build_hooks] if clone == :all
clone = Set[] if clone == :none
clone = Set.new(clone) if clone.is_a?(Array)
clone.delete(:builders) if options[:exclude_builders]
env = self.class.new(
echo: options[:echo] || @echo,
exclude_builders: true)
options[:echo] ||= @echo
env = self.class.new(options.merge(exclude_builders: true))
if clone.include?(:builders)
@builders.each do |builder_name, builder|
env.add_builder(builder)
@@ -287,9 +294,9 @@ module Rscons
unless @process_failures.empty?
msg = @process_failures.join("\n")
if Cache.instance["failed_commands"].size > 0
msg += "\nUse -F to view the failed command log from the previous build operation"
msg += "\nUse `#{Util.command_to_execute_me} -F` to view the failed command log from the previous build operation"
end
raise BuildError.new(msg)
raise RsconsError.new(msg)
end
end
@@ -347,12 +354,12 @@ module Rscons
if target.is_a?(Builder)
target = target.target
end
target = expand_varref(target.to_s)
target = expand_path(expand_varref(target.to_s))
user_deps = user_deps.map do |ud|
if ud.is_a?(Builder)
ud = ud.target
end
expand_varref(ud)
expand_path(expand_varref(ud))
end
@user_deps[target] ||= []
@user_deps[target] = (@user_deps[target] + user_deps).uniq
@@ -468,10 +475,12 @@ module Rscons
output_fname
end
# Expand a path to be relative to the Environment's build root.
# Expand paths.
#
# Paths beginning with "^/" are expanded by replacing "^" with the
# Environment's build root.
# Environment's build root (e.g. "build/envname").
# Paths beginning with "^^/" are expanded by replacing "^^" with the
# top-level build directory (e.g. "build").
#
# @param path [String, Array<String>]
# The path(s) to expand.
@@ -486,7 +495,7 @@ module Rscons
expand_path(path)
end
else
path.sub(%r{^\^(?=[\\/])}, @build_root).gsub("\\", "/")
path.sub(%r{^\^\^(?=[\\/])}, Rscons.application.build_dir).sub(%r{^\^(?=[\\/])}, @build_root).gsub("\\", "/")
end
end
@@ -702,10 +711,7 @@ module Rscons
!thread.alive?
end
else
if @threads.empty?
raise "No threads to wait for"
end
ThreadsWait.new(*@threads.keys).next_wait
Util.wait_for_thread(*@threads.keys)
end
end
+187 -27
View File
@@ -3,32 +3,8 @@ module Rscons
# The Script class encapsulates the state of a build script.
class Script
# DSL available to the Rsconscript.
class Dsl
# Create a Dsl.
def initialize(script)
@script = script
end
# Set the project name.
def project_name(project_name)
@script.project_name = project_name
end
# Whether to automatically configure (default true).
def autoconf(autoconf)
@script.autoconf = autoconf
end
# Enter build block.
def build(&block)
@script.operations["build"] = block
end
# Enter configuration block.
def configure(&block)
@script.operations["configure"] = block
end
# Global DSL methods.
class GlobalDsl
# Return a list of paths matching the specified pattern(s).
#
@@ -56,10 +32,194 @@ module Rscons
end
end.sort
end
# Return path components from the PATH variable.
#
# @return [Array<String>]
# Path components from the PATH variable.
def path_components
ENV["PATH"].split(File::PATH_SEPARATOR)
end
# Prepend a path component to the PATH variable.
#
# @param path [String]
# Path to prepend.
#
# @return [void]
def path_prepend(path)
path_set([File.expand_path(path)] + path_components)
end
# Append a path component to the PATH variable.
#
# @param path [String]
# Path to append.
#
# @return [void]
def path_append(path)
path_set(path_components + [File.expand_path(path)])
end
# Set the PATH variable.
#
# @param new_path [String, Array<String>]
# New PATH variable value as an array or string.
#
# @return [void]
def path_set(new_path)
if new_path.is_a?(Array)
new_path = new_path.join(File::PATH_SEPARATOR)
end
ENV["PATH"] = new_path
end
# Invoke rscons in a subprocess for a subsidiary Rsconscript file.
#
# @param path [String]
# Path to subsidiary Rsconscript to execute, or path to subsidiary
# directory to run rscons in.
# @param args[Array<String>]
# Arguments to pass to rscons subprocess.
def rscons(path, *args)
rscons_path = File.expand_path($0)
path = File.expand_path(path)
if File.directory?(path)
command = [*args]
dir = path
else
command = ["-f", path, *args]
dir = File.dirname(path)
end
if File.exist?("#{dir}/rscons")
rscons_path = "#{dir}/rscons"
end
command = [rscons_path] + command
print_dir = dir != "." && dir != File.expand_path(Dir.pwd)
if ENV["specs"] and not ENV["dist_specs"] # specs
command = ["ruby", $LOAD_PATH.map {|p| ["-I", p]}, command].flatten # specs
end # specs
puts "rscons: Entering directory '#{dir}'" if print_dir
result = system(*command, chdir: dir)
puts "rscons: Leaving directory '#{dir}'" if print_dir
unless result
raise RsconsError.new("Failed command: " + command.join(" "))
end
end
# Execute a shell command, exiting on failure.
# The behavior to exit on failure is suppressed if the +:continue+
# option is given.
#
# @overload sh(command, options = {})
# @param command [String, Array<String>]
# Command to execute. The command is executed and interpreted by the
# system shell when given as a single string. It is not passed to the
# system shell if the array size is greater than 1.
# @param options [Hash]
# Options.
# @option options [Boolean] :continue
# If set to +true+, rscons will continue executing afterward, even if
# the command fails.
#
# @overload sh(*command, options = {})
# @param command [String, Array<String>]
# Command to execute. The command is executed and interpreted by the
# system shell when given as a single string. It is not passed to the
# system shell if the array size is greater than 1.
# @param options [Hash]
# Options.
# @option options [Boolean] :continue
# If set to +true+, rscons will continue executing afterward, even if
# the command fails.
def sh(*command)
options = {}
if command.last.is_a?(Hash)
options = command.slice!(-1)
end
if command.size == 1 && command[0].is_a?(Array)
command = command[0]
end
if Rscons.application.verbose
if command.size > 1
puts Util.command_to_s(command)
else
puts command[0]
end
end
begin
system(*command, exception: true)
rescue StandardError => e
message = "#{e.backtrace[2]}: #{e.message}"
if options[:continue]
Ansi.write($stderr, :red, message, :reset, "\n")
else
raise RsconsError.new(message)
end
end
end
[
:cd,
:chmod,
:chmod_R,
:chown,
:chown_R,
:cp,
:cp_lr,
:cp_r,
:install,
:ln,
:ln_s,
:ln_sf,
:mkdir,
:mkdir_p,
:mv,
:pwd,
:rm,
:rm_f,
:rm_r,
:rm_rf,
:rmdir,
:touch,
].each do |method|
define_method(method) do |*args, **kwargs, &block|
FileUtils.__send__(method, *args, **kwargs, &block)
end
end
end
# Top-level DSL available to the Rsconscript.
class Dsl < GlobalDsl
# Create a Dsl.
def initialize(script)
@script = script
end
# Set the project name.
def project_name(project_name)
@script.project_name = project_name
end
# Whether to automatically configure (default true).
def autoconf(autoconf)
@script.autoconf = autoconf
end
# Enter build block.
def build(&block)
@script.operations["build"] = block
end
# Enter configuration block.
def configure(&block)
@script.operations["configure"] = block
end
end
# DSL available to the 'configure' block.
class ConfigureDsl
class ConfigureDsl < GlobalDsl
# Create a ConfigureDsl.
#
# @param configure_op [ConfigureOp]
+42 -5
View File
@@ -9,7 +9,7 @@ module Rscons
#
# @return [Boolean] Whether the given path is an absolute filesystem path.
def absolute_path?(path)
if RUBY_PLATFORM =~ /mingw/
if RUBY_PLATFORM =~ /mingw|msys/
path =~ %r{^(?:\w:)?[\\/]}
else
path.start_with?("/")
@@ -48,7 +48,7 @@ module Rscons
# @return [String]
# The string representation of the command.
def command_to_s(command)
command.map { |c| c =~ /\s/ ? "'#{c}'" : c }.join(' ')
command.map { |c| c[" "] ? "'#{c.gsub("'", "'\\\\''")}'" : c }.join(" ")
end
# Determine the number of threads to use by default.
@@ -68,8 +68,8 @@ module Rscons
case RbConfig::CONFIG["host_os"]
when /linux/
return File.read("/proc/cpuinfo").scan(/^processor\s*:/).size
when /mswin|mingw/
if `wmic cpu get NumberOfLogicalProcessors /value` =~ /NumberOfLogicalProcessors=(\d+)/
when /mswin|mingw|msys/
if `wmic cpu get NumberOfLogicalProcessors -value` =~ /NumberOfLogicalProcessors=(\d+)/
return $1.to_i
end
when /darwin/
@@ -191,6 +191,40 @@ module Rscons
deps
end
# Wait for any of a number of threads to complete.
#
# @param threads [Array<Thread>]
# Threads to wait for.
#
# @return [Thread]
# The Thread that completed.
def wait_for_thread(*threads)
if threads.empty?
raise "No threads to wait for"
end
queue = Queue.new
threads.each do |thread|
# Create a wait thread for each thread we're waiting for.
Thread.new do
begin
thread.join
ensure
queue.push(thread)
end
end
end
# Wait for any thread to complete.
queue.pop
end
# Command that can be run to execute this instance of rscons from the
# current working directory.
def command_to_execute_me
command = Pathname.new(File.expand_path($0)).relative_path_from(Dir.pwd).to_s
command = "./#{command}" unless command["/"]
command
end
private
# Check if a directory contains a certain executable.
@@ -199,11 +233,14 @@ module Rscons
# Directory to look in.
# @param executable [String]
# Executable to look for.
#
# @return [String, nil]
# Executable found.
def test_path_for_executable(path_entry, executable)
is_executable = lambda do |path|
File.file?(path) and File.executable?(path)
end
if RbConfig::CONFIG["host_os"] =~ /mswin|windows|mingw/i
if RbConfig::CONFIG["host_os"] =~ /mswin|windows|mingw|msys/i
if File.directory?(path_entry)
executable = executable.downcase
dir_entries = Dir.entries(path_entry)
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons
# Project version.
VERSION = "2.0.2"
VERSION = "2.3.0"
end
+7 -4
View File
@@ -44,7 +44,7 @@ combine_files[START_FILE]
# Strip Ruby comment lines and empty lines to save some space, but do not
# remove lines that are in heredoc sections. This isn't terribly robust to be
# used in the wild, but works for the heredoc instances for this project.
stripped_comments = []
stripped = []
heredoc_end = nil
combined_file.each do |line|
if line =~ /<<-?([A-Z]+)/
@@ -53,17 +53,20 @@ combined_file.each do |line|
if heredoc_end and line =~ /^\s*#{heredoc_end}/
heredoc_end = nil
end
if heredoc_end or not (line =~ /^\s*(#[^!].*)?$/)
stripped_comments << line
if line !~ /#\sspecs/
if heredoc_end or line !~ /^\s*(#[^!].*)?$/
stripped << line
end
end
end
require "zlib"
require "base64"
compressed_script = Zlib::Deflate.deflate(stripped_comments.join)
compressed_script = Zlib::Deflate.deflate(stripped.join)
encoded_compressed_script = Base64.encode64(compressed_script).gsub("\n", "")
hash = Digest::MD5.hexdigest(encoded_compressed_script)
FileUtils.rm_rf(DIST)
FileUtils.mkdir_p(DIST)
File.open("#{DIST}/#{PROG_NAME}", "wb", 0755) do |fh|
fh.write(<<EOF)
+406 -105
View File
@@ -55,6 +55,8 @@ describe Rscons do
end
end
let(:passenv) {{}}
def test_dir(build_test_directory)
Dir.chdir(@owd)
rm_rf(@build_test_run_dir)
@@ -118,21 +120,23 @@ describe Rscons do
command_name = "#{command_prefix}#{@statics[:build_test_id]}"
File.open("_simplecov_setup.rb", "w") do |fh|
fh.puts <<EOF
require "bundler"
Bundler.setup
require "simplecov"
class MyFormatter
def format(*args)
unless ENV["dist_specs"]
require "bundler"
Bundler.setup
require "simplecov"
class MyFormatter
def format(*args)
end
end
end
SimpleCov.start do
root(#{@owd.inspect})
command_name(#{command_name.inspect})
filters.clear
add_filter do |src|
!(src.filename[SimpleCov.root])
SimpleCov.start do
root(#{@owd.inspect})
command_name(#{command_name.inspect})
filters.clear
add_filter do |src|
!(src.filename[SimpleCov.root])
end
formatter(MyFormatter)
end
formatter(MyFormatter)
end
# force color off
ENV["TERM"] = nil
@@ -143,8 +147,9 @@ EOF
end
end
stdout, stderr, status = nil, nil, nil
Bundler.with_clean_env do
Bundler.with_unbundled_env do
env = ENV.to_h
env.merge!(passenv)
path = ["#{@build_test_run_dir}/_bin", "#{env["PATH"]}"]
if options[:path]
path = Array(options[:path]) + path
@@ -187,6 +192,10 @@ EOF
end
end
def nr(str)
str.gsub("\r", "")
end
###########################################################################
# Tests
###########################################################################
@@ -196,7 +205,24 @@ EOF
result = run_rscons
expect(result.stderr).to eq ""
expect(File.exists?('build/e.1/simple.c.o')).to be_truthy
expect(`./simple.exe`).to eq "This is a simple C program\n"
expect(nr(`./simple.exe`)).to eq "This is a simple C program\n"
end
it "uses the build directory specified with -b" do
test_dir("simple")
result = run_rscons(rscons_args: %w[-b b])
expect(result.stderr).to eq ""
expect(Dir.exist?("build")).to be_falsey
expect(File.exists?("b/e.1/simple.c.o")).to be_truthy
end
it "uses the build directory specified by an environment variable" do
test_dir("simple")
passenv["RSCONS_BUILD_DIR"] = "b2"
result = run_rscons
expect(result.stderr).to eq ""
expect(Dir.exist?("build")).to be_falsey
expect(File.exists?("b2/e.1/simple.c.o")).to be_truthy
end
it "allows specifying a Builder object as the source to another build target" do
@@ -204,7 +230,7 @@ EOF
result = run_rscons(rsconscript: "builder_as_source.rb")
expect(result.stderr).to eq ""
expect(File.exists?("simple.o")).to be_truthy
expect(`./simple.exe`).to eq "This is a simple C program\n"
expect(nr(`./simple.exe`)).to eq "This is a simple C program\n"
end
it 'prints commands as they are executed' do
@@ -232,18 +258,18 @@ EOF
result = run_rscons
expect(result.stderr).to eq ""
expect(File.exists?('build/e.1/header.c.o')).to be_truthy
expect(`./header.exe`).to eq "The value is 2\n"
expect(nr(`./header.exe`)).to eq "The value is 2\n"
end
it 'rebuilds a C module when a header it depends on changes' do
test_dir('header')
result = run_rscons
expect(result.stderr).to eq ""
expect(`./header.exe`).to eq "The value is 2\n"
expect(nr(`./header.exe`)).to eq "The value is 2\n"
file_sub('header.h') {|line| line.sub(/2/, '5')}
result = run_rscons
expect(result.stderr).to eq ""
expect(`./header.exe`).to eq "The value is 5\n"
expect(nr(`./header.exe`)).to eq "The value is 5\n"
end
it 'does not rebuild a C module when its dependencies have not changed' do
@@ -254,7 +280,7 @@ EOF
%r{Compiling header.c},
%r{Linking header.exe},
])
expect(`./header.exe`).to eq "The value is 2\n"
expect(nr(`./header.exe`)).to eq "The value is 2\n"
result = run_rscons
expect(result.stderr).to eq ""
expect(result.stdout).to eq ""
@@ -268,7 +294,7 @@ EOF
%r{Compiling header.c},
%r{Linking header.exe},
])
expect(`./header.exe`).to eq "The value is 2\n"
expect(nr(`./header.exe`)).to eq "The value is 2\n"
sleep 0.05
file_sub('header.c') {|line| line}
result = run_rscons
@@ -312,14 +338,14 @@ EOF
]
end
it "expands target and source paths starting with ^/ to be relative to the build root" do
it "expands target and source paths starting with ^/ and ^^/" do
test_dir("typical")
result = run_rscons(rsconscript: "carat.rb")
result = run_rscons(rsconscript: "carat.rb", rscons_args: %w[-b bld])
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{gcc -c -o build/e.1/one.o -MMD -MF build/e.1/one.o.mf -Isrc -Isrc/one -Isrc/two build/e.1/one.c},
%r{gcc -c -o build/e.1/src/two/two.c.o -MMD -MF build/e.1/src/two/two.c.o.mf -Isrc -Isrc/one -Isrc/two src/two/two.c},
%r{gcc -o program.exe build/e.1/src/two/two.c.o build/e.1/one.o},
%r{gcc -c -o bld/e.1/one.o -MMD -MF bld/e.1/one.o.mf -Isrc -Isrc/one -Isrc/two bld/e.1/one.c},
%r{gcc -c -o bld/e.1/bld/two.c.o -MMD -MF bld/e.1/bld/two.c.o.mf -Isrc -Isrc/one -Isrc/two bld/two.c},
%r{gcc -o bld/program.exe bld/e.1/one.o bld/e.1/bld/two.c.o},
])
end
@@ -328,7 +354,7 @@ EOF
result = run_rscons
expect(result.stderr).to eq ""
expect(File.exists?('foo.yml')).to be_truthy
expect(IO.read('foo.yml')).to eq("---\nkey: value\n")
expect(nr(IO.read('foo.yml'))).to eq("---\nkey: value\n")
end
it "raises an error when a side-effect file is registered for a build target that is not registered" do
@@ -337,20 +363,6 @@ EOF
expect(result.stderr).to match /Could not find a registered build target "foo"/
end
it "allows replacing a running executable when performing an 'install' operation" do
test_dir("install_running_exe")
result = run_rscons(op: "install")
expect(result.stderr).to eq ""
fcontents = File.read("program.c", mode: "rb")
File.open("program.c", "wb") do |fh|
fh.write(fcontents.sub(/2/, '3'))
end
pid = Process.spawn("install/program.exe")
result = run_rscons(op: "install")
expect(result.stderr).to eq ""
Process.wait(pid)
end
context "clean operation" do
it 'cleans built files' do
test_dir("simple")
@@ -428,7 +440,7 @@ EOF
%r{Linking program.exe},
])
expect(File.exists?('inc.h')).to be_truthy
expect(`./program.exe`).to eq "The value is 5678\n"
expect(nr(`./program.exe`)).to eq "The value is 5678\n"
end
it 'supports custom builders with multiple targets' do
@@ -444,13 +456,13 @@ EOF
])
expect(File.exists?("inc.c")).to be_truthy
expect(File.exists?("inc.h")).to be_truthy
expect(`./program.exe`).to eq "The value is 42\n"
expect(nr(`./program.exe`)).to eq "The value is 42\n"
File.open("inc.c", "w") {|fh| fh.puts "int THE_VALUE = 33;"}
result = run_rscons(rsconscript: "multiple_targets.rb")
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [%r{CHGen inc.c}])
expect(`./program.exe`).to eq "The value is 42\n"
expect(nr(`./program.exe`)).to eq "The value is 42\n"
end
it 'raises an error when a custom builder returns an invalid value from #run' do
@@ -518,7 +530,7 @@ EOF
result = run_rscons
expect(result.stderr).to eq ""
expect(File.exists?('build/e.1/simple.cc.o')).to be_truthy
expect(`./simple.exe`).to eq "This is a simple C++ program\n"
expect(nr(`./simple.exe`)).to eq "This is a simple C++ program\n"
end
it "links with the C++ linker when object files were built from C++ sources" do
@@ -526,7 +538,7 @@ EOF
result = run_rscons(rsconscript: "link_objects.rb")
expect(result.stderr).to eq ""
expect(File.exists?("simple.o")).to be_truthy
expect(`./simple.exe`).to eq "This is a simple C++ program\n"
expect(nr(`./simple.exe`)).to eq "This is a simple C++ program\n"
end
it 'allows overriding construction variables for individual builder calls' do
@@ -534,12 +546,12 @@ EOF
result = run_rscons
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{gcc -c -o one.o -MMD -MF one.o.mf -DONE one.c},
%r{gcc -c -o one.o -MMD -MF build/e.1/one.o.mf -DONE one.c},
%r{gcc -c -o build/e.1/two.c.o -MMD -MF build/e.1/two.c.o.mf two.c},
%r{gcc -o two_sources.exe one.o build/e.1/two.c.o},
])
expect(File.exists?("two_sources.exe")).to be_truthy
expect(`./two_sources.exe`).to eq "This is a C program with two sources.\n"
expect(nr(`./two_sources.exe`)).to eq "This is a C program with two sources.\n"
end
it 'builds a static library archive' do
@@ -554,7 +566,7 @@ EOF
%r{gcc -o library.exe lib.a build/e.1/three.c.o},
])
expect(File.exists?("library.exe")).to be_truthy
expect(`ar t lib.a`).to eq "one.c.o\ntwo.c.o\n"
expect(nr(`ar t lib.a`)).to eq "one.c.o\ntwo.c.o\n"
end
it 'supports build hooks to override construction variables' do
@@ -566,7 +578,7 @@ EOF
%r{gcc -c -o build/e.1/src/two/two.c.o -MMD -MF build/e.1/src/two/two.c.o.mf -Isrc/one -Isrc/two -O2 src/two/two.c},
%r{gcc -o build_hook.exe build/e.1/src/one/one.c.o build/e.1/src/two/two.c.o},
])
expect(`./build_hook.exe`).to eq "Hello from two()\n"
expect(nr(`./build_hook.exe`)).to eq "Hello from two()\n"
end
it 'supports build hooks to override the entire vars hash' do
@@ -574,8 +586,8 @@ EOF
result = run_rscons(rsconscript: "build_hooks_override_vars.rb")
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{gcc -c -o one.o -MMD -MF one.o.mf -Isrc -Isrc/one -Isrc/two -O1 src/two/two.c},
%r{gcc -c -o two.o -MMD -MF two.o.mf -Isrc -Isrc/one -Isrc/two -O2 src/two/two.c},
%r{gcc -c -o one.o -MMD -MF build/e.1/one.o.mf -Isrc -Isrc/one -Isrc/two -O1 src/two/two.c},
%r{gcc -c -o two.o -MMD -MF build/e.1/two.o.mf -Isrc -Isrc/one -Isrc/two -O2 src/two/two.c},
])
expect(File.exists?('one.o')).to be_truthy
expect(File.exists?('two.o')).to be_truthy
@@ -592,7 +604,7 @@ EOF
%r{Linking simple.exe},
])
expect(File.exists?('build/e.1/simple.c.o')).to be_truthy
expect(`./simple.exe`).to eq "This is a simple C program\n"
expect(nr(`./simple.exe`)).to eq "This is a simple C program\n"
File.open("program.ld", "w") {|fh| fh.puts("2")}
result = run_rscons(rsconscript: "user_dependencies.rb")
@@ -609,15 +621,38 @@ EOF
expect(result.stdout).to eq ""
end
it "supports building D sources with gdc" do
test_dir("d")
result = run_rscons
it "rebuilds when user-specified dependencies using ^ change" do
test_dir("simple")
passenv["file_contents"] = "1"
result = run_rscons(rsconscript: "user_dependencies_carat.rb")
expect(result.stderr).to eq ""
slines = lines(result.stdout)
verify_lines(slines, [%r{gdc -c -o build/e.1/main.d.o -MMD -MF build/e.1/main.d.o.mf main.d}])
verify_lines(slines, [%r{gdc -c -o build/e.1/mod.d.o -MMD -MF build/e.1/mod.d.o.mf mod.d}])
verify_lines(slines, [%r{gdc -o hello-d.exe build/e.1/main.d.o build/e.1/mod.d.o}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
verify_lines(lines(result.stdout), [
%r{Compiling simple.c},
%r{Linking .*simple.exe},
])
passenv["file_contents"] = "2"
result = run_rscons(rsconscript: "user_dependencies_carat.rb")
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [%r{Linking .*simple.exe}])
result = run_rscons(rsconscript: "user_dependencies_carat.rb")
expect(result.stderr).to eq ""
expect(result.stdout).to eq ""
end
unless RUBY_PLATFORM =~ /mingw|msys/
it "supports building D sources with gdc" do
test_dir("d")
result = run_rscons
expect(result.stderr).to eq ""
slines = lines(result.stdout)
verify_lines(slines, [%r{gdc -c -o build/e.1/main.d.o -MMD -MF build/e.1/main.d.o.mf main.d}])
verify_lines(slines, [%r{gdc -c -o build/e.1/mod.d.o -MMD -MF build/e.1/mod.d.o.mf mod.d}])
verify_lines(slines, [%r{gdc -o hello-d.exe build/e.1/main.d.o build/e.1/mod.d.o}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
end
end
it "supports building D sources with ldc2" do
@@ -625,8 +660,8 @@ EOF
result = run_rscons(rsconscript: "build-ldc2.rb")
expect(result.stderr).to eq ""
slines = lines(result.stdout)
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.d.o(bj)? -deps=build/e.1/main.d.o.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o.mf mod.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.d.o(bj)? -deps=build/e.1/main.d.o(bj)?.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o(bj)?.mf mod.d}])
verify_lines(slines, [%r{ldc2 -of hello-d.exe build/e.1/main.d.o(bj)? build/e.1/mod.d.o(bj)?}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
end
@@ -636,8 +671,8 @@ EOF
result = run_rscons(rsconscript: "build-ldc2.rb")
expect(result.stderr).to eq ""
slines = lines(result.stdout)
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.d.o(bj)? -deps=build/e.1/main.d.o.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o.mf mod.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.d.o(bj)? -deps=build/e.1/main.d.o(bj)?.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o(bj)?.mf mod.d}])
verify_lines(slines, [%r{ldc2 -of hello-d.exe build/e.1/main.d.o(bj)? build/e.1/mod.d.o(bj)?}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
contents = File.read("mod.d", mode: "rb").sub("42", "33")
@@ -647,13 +682,13 @@ EOF
result = run_rscons(rsconscript: "build-ldc2.rb")
expect(result.stderr).to eq ""
slines = lines(result.stdout)
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.d.o(bj)? -deps=build/e.1/main.d.o.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o.mf mod.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.d.o(bj)? -deps=build/e.1/main.d.o(bj)?.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o(bj)?.mf mod.d}])
verify_lines(slines, [%r{ldc2 -of hello-d.exe build/e.1/main.d.o(bj)? build/e.1/mod.d.o(bj)?}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 33!"
end
unless ENV["omit_gdc_tests"]
unless RUBY_PLATFORM =~ /mingw|msys/
it "links with the D linker when object files were built from D sources" do
test_dir("d")
result = run_rscons(rsconscript: "link_objects.rb")
@@ -689,7 +724,7 @@ EOF
result = run_rscons(rsconscript: "shared_library_d.rb")
expect(result.stderr).to eq ""
slines = lines(result.stdout)
if RUBY_PLATFORM =~ /mingw/
if RUBY_PLATFORM =~ /mingw|msys/
verify_lines(slines, [%r{Linking mine.dll}])
else
verify_lines(slines, [%r{Linking libmine.so}])
@@ -715,7 +750,7 @@ EOF
result = run_rscons(rsconscript: "preprocess.rb")
expect(result.stderr).to eq ""
expect(File.read("simplepp.c")).to match /# \d+ "simple.c"/
expect(`./simple.exe`).to eq "This is a simple C program\n"
expect(nr(`./simple.exe`)).to eq "This is a simple C program\n"
end
it "supports preprocessing C++ sources" do
@@ -723,7 +758,7 @@ EOF
result = run_rscons(rsconscript: "preprocess.rb")
expect(result.stderr).to eq ""
expect(File.read("simplepp.cc")).to match /# \d+ "simple.cc"/
expect(`./simple.exe`).to eq "This is a simple C++ program\n"
expect(nr(`./simple.exe`)).to eq "This is a simple C++ program\n"
end
it "supports invoking builders with no sources" do
@@ -741,14 +776,14 @@ EOF
%r{Linking program.exe},
])
expect(File.exists?('inc.h')).to be_truthy
expect(`./program.exe`).to eq "The value is 678\n"
expect(nr(`./program.exe`)).to eq "The value is 678\n"
end
it "supports lambdas as construction variable values" do
test_dir "custom_builder"
result = run_rscons(rsconscript: "cvar_lambda.rb")
expect(result.stderr).to eq ""
expect(`./program.exe`).to eq "The value is 5678\n"
expect(nr(`./program.exe`)).to eq "The value is 5678\n"
end
it "supports registering build targets from within a build hook" do
@@ -757,7 +792,7 @@ EOF
expect(result.stderr).to eq ""
expect(File.exists?("build/e.1/simple.c.o")).to be_truthy
expect(File.exists?("build/e.1/simple.c.o.txt")).to be_truthy
expect(`./simple.exe`).to eq "This is a simple C program\n"
expect(nr(`./simple.exe`)).to eq "This is a simple C program\n"
end
it "supports multiple values for CXXSUFFIX" do
@@ -767,7 +802,7 @@ EOF
expect(result.stderr).to eq ""
expect(File.exists?("build/e.1/simple.cc.o")).to be_truthy
expect(File.exists?("build/e.1/other.cccc.o")).to be_truthy
expect(`./simple.exe`).to eq "This is a simple C++ program\n"
expect(nr(`./simple.exe`)).to eq "This is a simple C++ program\n"
end
it "supports multiple values for CSUFFIX" do
@@ -777,7 +812,7 @@ EOF
expect(result.stderr).to eq ""
expect(File.exists?("build/e.1/src/one/one.yargh.o")).to be_truthy
expect(File.exists?("build/e.1/src/two/two.c.o")).to be_truthy
expect(`./program.exe`).to eq "Hello from two()\n"
expect(nr(`./program.exe`)).to eq "Hello from two()\n"
end
it "supports multiple values for OBJSUFFIX" do
@@ -787,7 +822,7 @@ EOF
expect(File.exists?("two_sources.exe")).to be_truthy
expect(File.exists?("one.oooo")).to be_truthy
expect(File.exists?("two.ooo")).to be_truthy
expect(`./two_sources.exe`).to eq "This is a C program with two sources.\n"
expect(nr(`./two_sources.exe`)).to eq "This is a C program with two sources.\n"
end
it "supports multiple values for LIBSUFFIX" do
@@ -795,7 +830,7 @@ EOF
result = run_rscons(rsconscript: "libsuffix.rb")
expect(result.stderr).to eq ""
expect(File.exists?("two_sources.exe")).to be_truthy
expect(`./two_sources.exe`).to eq "This is a C program with two sources.\n"
expect(nr(`./two_sources.exe`)).to eq "This is a C program with two sources.\n"
end
it "supports multiple values for ASSUFFIX" do
@@ -810,7 +845,7 @@ EOF
%r{Linking two_sources.exe},
])
expect(File.exists?("two_sources.exe")).to be_truthy
expect(`./two_sources.exe`).to eq "This is a C program with two sources.\n"
expect(nr(`./two_sources.exe`)).to eq "This is a C program with two sources.\n"
end
it "supports dumping an Environment's construction variables" do
@@ -849,7 +884,7 @@ EOF
result = run_rscons(rsconscript: "cvar_array.rb")
expect(result.stderr).to eq ""
expect(File.exists?("build/e.1/simple.c.o")).to be_truthy
expect(`./simple.exe`).to eq "This is a simple C program\n"
expect(nr(`./simple.exe`)).to eq "This is a simple C program\n"
end
it "supports registering multiple build targets with the same target path" do
@@ -940,7 +975,7 @@ EOF
result = run_rscons
expect(result.stderr).to eq ""
slines = lines(result.stdout)
if RUBY_PLATFORM =~ /mingw/
if RUBY_PLATFORM =~ /mingw|msys/
verify_lines(slines, [%r{Linking mine.dll}])
expect(File.exists?("mine.dll")).to be_truthy
else
@@ -952,7 +987,7 @@ EOF
expect(result.stderr).to eq ""
expect(result.stdout).to eq ""
ld_library_path_prefix = (RUBY_PLATFORM =~ /mingw/ ? "" : "LD_LIBRARY_PATH=. ")
ld_library_path_prefix = (RUBY_PLATFORM =~ /mingw|msys/ ? "" : "LD_LIBRARY_PATH=. ")
expect(`#{ld_library_path_prefix}./test-shared.exe`).to match /Hi from one/
expect(`./test-static.exe`).to match /Hi from one/
end
@@ -971,7 +1006,7 @@ EOF
result = run_rscons(rsconscript: "shared_library_cxx.rb")
expect(result.stderr).to eq ""
slines = lines(result.stdout)
if RUBY_PLATFORM =~ /mingw/
if RUBY_PLATFORM =~ /mingw|msys/
verify_lines(slines, [%r{Linking mine.dll}])
else
verify_lines(slines, [%r{Linking libmine.so}])
@@ -981,7 +1016,7 @@ EOF
expect(result.stderr).to eq ""
expect(result.stdout).to eq ""
ld_library_path_prefix = (RUBY_PLATFORM =~ /mingw/ ? "" : "LD_LIBRARY_PATH=. ")
ld_library_path_prefix = (RUBY_PLATFORM =~ /mingw|msys/ ? "" : "LD_LIBRARY_PATH=. ")
expect(`#{ld_library_path_prefix}./test-shared.exe`).to match /Hi from one/
expect(`./test-static.exe`).to match /Hi from one/
end
@@ -1048,7 +1083,7 @@ EOF
result = run_rscons
expect(result.stderr).to match /Failed to build/
expect(result.stderr).to match /^Use.*-F.*to view the failed command log/
expect(result.stderr).to match %r{^Use .*/rscons(\.rb)? -F.*to view the failed command log}
expect(result.status).to_not eq 0
result = run_rscons(rscons_args: %w[-F])
@@ -1058,6 +1093,23 @@ EOF
expect(result.status).to eq 0
end
it "stores build artifacts in a directory according to Environment name" do
test_dir "typical"
result = run_rscons
expect(File.exist?("build/typical/typical.exe")).to be_truthy
expect(File.exist?("build/typical/src/one/one.c.o")).to be_truthy
end
it "names Environment during clone" do
test_dir "typical"
result = run_rscons(rsconscript: "clone_and_name.rb")
expect(File.exist?("build/typical/typical.exe")).to be_truthy
expect(File.exist?("build/typical/src/one/one.c.o")).to be_truthy
expect(Dir.exist?("build/e.1")).to be_falsey
end
context "colored output" do
it "does not output in color with --color=off" do
test_dir("simple")
@@ -1112,7 +1164,7 @@ EOF
result = run_rscons(rsconscript: "command_builder.rb")
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [%r{BuildIt simple.exe}])
expect(`./simple.exe`).to eq "This is a simple C program\n"
expect(nr(`./simple.exe`)).to eq "This is a simple C program\n"
result = run_rscons(rsconscript: "command_builder.rb")
expect(result.stderr).to eq ""
@@ -1278,7 +1330,8 @@ EOF
context "Cache management" do
it "prints a warning when the cache is corrupt" do
test_dir("simple")
File.open(Rscons::Cache::CACHE_FILE, "w") do |fh|
FileUtils.mkdir("build")
File.open("build/.rsconscache", "w") do |fh|
fh.puts("[1]")
end
result = run_rscons
@@ -1457,6 +1510,25 @@ EOF
expect(result.status).to eq 0
end
it "allows prepending and appending to PATH" do
test_dir "simple"
result = run_rscons(rsconscript: "pathing.rb")
expect(result.stderr).to eq ""
expect(result.stdout).to match /flex!/
expect(result.stdout).to match /foobar!/
expect(File.exist?("simple.o")).to be_truthy
end
it "writes the dependency file to the build root" do
test_dir "simple"
result = run_rscons(rsconscript: "distclean.rb")
expect(result.stderr).to eq ""
expect(result.stdout).to match /Compiling simple\.c/
expect(File.exist?("simple.o")).to be_truthy
expect(File.exist?("simple.o.mf")).to be_falsey
expect(File.exist?("build/e.1/simple.o.mf")).to be_truthy
end
context "debugging" do
it "prints a message when the target does not exist" do
test_dir("simple")
@@ -1550,7 +1622,7 @@ EOF
result = run_rscons(rsconscript: "override_depfilesuffix.rb")
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{gcc -c -o simple.o -MMD -MF simple.o.deppy simple.c},
%r{gcc -c -o simple.o -MMD -MF build/e.1/simple.o.deppy simple.c},
])
end
@@ -1593,7 +1665,7 @@ EOF
result = run_rscons(rsconscript: "shared_library_set_shld.rb")
expect(result.stderr).to eq ""
slines = lines(result.stdout)
if RUBY_PLATFORM =~ /mingw/
if RUBY_PLATFORM =~ /mingw|msys/
verify_lines(slines, [%r{Linking mine.dll}])
else
verify_lines(slines, [%r{Linking libmine.so}])
@@ -1608,6 +1680,21 @@ EOF
end
end
context "Size builder" do
it "generates a size file" do
test_dir "simple"
result = run_rscons(rsconscript: "size.rb")
verify_lines(lines(result.stdout), [
/Linking .*simple\.exe/,
/Size .*simple\.exe .*simple\.size/,
])
expect(File.exist?("simple.exe")).to be_truthy
expect(File.exist?("simple.size")).to be_truthy
expect(File.read("simple.size")).to match /text.*data.*bss/
end
end
context "multi-threading" do
it "waits for subcommands in threads for builders that support threaded commands" do
test_dir("simple")
@@ -1711,6 +1798,22 @@ EOF
expect(result.status).to_not eq 0
end
it "automatically runs the configure operation if the project is not yet configured in the given build directory" do
test_dir "configure"
result = run_rscons(rsconscript: "check_c_compiler.rb")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match /Checking for C compiler\.\.\./
expect(Dir.exist?("build/_configure")).to be_truthy
result = run_rscons(rsconscript: "check_c_compiler.rb", rscons_args: %w[--build=bb])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match /Checking for C compiler\.\.\./
expect(Dir.exist?("bb/_configure")).to be_truthy
end
context "check_c_compiler" do
{"check_c_compiler.rb" => "when no arguments are given",
"check_c_compiler_find_first.rb" => "when arguments are given"}.each_pair do |rsconscript, desc|
@@ -1801,12 +1904,14 @@ EOF
{"check_d_compiler.rb" => "when no arguments are given",
"check_d_compiler_find_first.rb" => "when arguments are given"}.each_pair do |rsconscript, desc|
context desc do
it "finds the first listed D compiler" do
test_dir "configure"
result = run_rscons(rsconscript: rsconscript, op: "configure")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match /Checking for D compiler\.\.\. gdc/
unless RUBY_PLATFORM =~ /mingw|msys/
it "finds the first listed D compiler" do
test_dir "configure"
result = run_rscons(rsconscript: rsconscript, op: "configure")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match /Checking for D compiler\.\.\. gdc/
end
end
it "finds the second listed D compiler" do
@@ -1830,13 +1935,15 @@ EOF
end
end
it "successfully tests a compiler with an unknown name that uses gdc-compatible options" do
test_dir "configure"
create_exe "mycompiler", %[exec gdc "$@"]
result = run_rscons(rsconscript: "check_d_compiler_custom.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match /Checking for D compiler\.\.\. mycompiler/
unless RUBY_PLATFORM =~ /mingw|msys/
it "successfully tests a compiler with an unknown name that uses gdc-compatible options" do
test_dir "configure"
create_exe "mycompiler", %[exec gdc "$@"]
result = run_rscons(rsconscript: "check_d_compiler_custom.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match /Checking for D compiler\.\.\. mycompiler/
end
end
it "successfully tests a compiler with an unknown name that uses ldc2-compatible options" do
@@ -2226,6 +2333,16 @@ EOF
end
end
context "on_fail option" do
it "prints on_fail messages and calls on_fail procs on failure" do
test_dir "configure"
result = run_rscons(rsconscript: "on_fail.rb", op: %w[configure])
expect(result.status).to_not eq 0
expect(result.stdout).to match /Install the foo123 package/
expect(result.stdout).to match /Install the foo123cxx package/
end
end
it "does everything" do
test_dir "configure"
create_exe "pkg-config", "echo '-DMYPACKAGE'"
@@ -2233,7 +2350,6 @@ EOF
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match /Configuring configure test\.\.\./
expect(result.stdout).to match /Setting build directory\.\.\. bb/
expect(result.stdout).to match %r{Setting prefix\.\.\. /my/prefix}
expect(result.stdout).to match /Checking for C compiler\.\.\. gcc/
expect(result.stdout).to match /Checking for C\+\+ compiler\.\.\. g\+\+/
@@ -2244,6 +2360,8 @@ EOF
expect(result.stdout).to match /Checking for D import 'std.stdio'\.\.\. found/
expect(result.stdout).to match /Checking for library 'm'\.\.\. found/
expect(result.stdout).to match /Checking for program 'ls'\.\.\. .*ls/
expect(Dir.exist?("build")).to be_falsey
expect(Dir.exist?("bb/_configure")).to be_truthy
end
it "aggregates multiple set_define's" do
@@ -2306,7 +2424,6 @@ EOF
expect(result.status).to eq 0
expect(File.exists?("simple.o")).to be_falsey
expect(File.exists?("build")).to be_falsey
expect(File.exists?(Rscons::Cache::CACHE_FILE)).to be_falsey
end
end
@@ -2379,7 +2496,7 @@ EOF
expect(result.stderr).to eq ""
expect(result.stdout).to match %r{Compiling/Linking}
expect(File.exists?("test.exe")).to be_truthy
ld_library_path_prefix = (RUBY_PLATFORM =~ /mingw/ ? "" : "LD_LIBRARY_PATH=. ")
ld_library_path_prefix = (RUBY_PLATFORM =~ /mingw|msys/ ? "" : "LD_LIBRARY_PATH=. ")
expect(`#{ld_library_path_prefix}./test.exe`).to match /three/
result = run_rscons(rsconscript: "c_shared_library.rb")
@@ -2574,4 +2691,188 @@ EOF
end
end
context "with subsidiary scripts" do
context "with a script specified" do
it "executes the subsidiary script from configure block" do
test_dir "subsidiary"
result = run_rscons(op: %W[configure])
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{Entering directory '.*/sub'},
%r{sub Rsconscript configure},
%r{Leaving directory '.*/sub'},
%r{Entering directory '.*/sub'},
%r{sub Rsconscript build},
%r{Leaving directory '.*/sub'},
%r{Entering directory '.*/sub'},
%r{sub Rsconscript2 configure},
%r{Leaving directory '.*/sub'},
%r{top configure},
])
end
it "executes the subsidiary script from build block" do
test_dir "subsidiary"
result = run_rscons(op: %W[configure])
expect(result.stderr).to eq ""
result = run_rscons(op: %W[build])
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{sub Rsconscript2 build},
%r{top build},
])
end
end
context "with a directory specified" do
it "executes the subsidiary script from configure block" do
test_dir "subsidiary"
result = run_rscons(rsconscript: "Rsconscript_dir", op: %W[configure])
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{Entering directory '.*/sub'},
%r{sub Rsconscript configure},
%r{Leaving directory '.*/sub'},
%r{Entering directory '.*/sub'},
%r{sub Rsconscript build},
%r{Leaving directory '.*/sub'},
%r{Entering directory '.*/sub'},
%r{sub Rsconscript2 configure},
%r{Leaving directory '.*/sub'},
%r{top configure},
])
end
it "executes the subsidiary script from build block" do
test_dir "subsidiary"
result = run_rscons(rsconscript: "Rsconscript_dir", op: %W[configure])
expect(result.stderr).to eq ""
result = run_rscons(rsconscript: "Rsconscript_dir", op: %W[build])
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{sub Rsconscript2 build},
%r{top build},
])
end
end
context "with a rscons binary in the subsidiary script directory" do
it "executes rscons from the subsidiary script directory" do
test_dir "subsidiary"
File.binwrite("sub/rscons", <<EOF)
#!/usr/bin/env ruby
puts "sub rscons"
EOF
FileUtils.chmod(0755, "sub/rscons")
result = run_rscons(op: %W[configure])
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{Entering directory '.*/sub'},
%r{sub rscons},
%r{Leaving directory '.*/sub'},
%r{Entering directory '.*/sub'},
%r{sub rscons},
%r{Leaving directory '.*/sub'},
%r{Entering directory '.*/sub'},
%r{sub rscons},
%r{Leaving directory '.*/sub'},
%r{top configure},
])
end
end
it "does not print entering/leaving directory messages when the subsidiary script is in the same directory" do
test_dir "subsidiary"
result = run_rscons(rsconscript: "Rsconscript_samedir", op: %W[configure])
expect(result.stderr).to eq ""
result = run_rscons(rsconscript: "Rsconscript_samedir", op: %W[build])
expect(result.stderr).to eq ""
expect(result.stdout).to_not match(%{(Entering|Leaving) directory})
verify_lines(lines(result.stdout), [
%r{second build},
%r{top build},
])
end
it "terminates execution when a subsidiary script fails" do
test_dir "subsidiary"
result = run_rscons(rsconscript: "Rsconscript_fail", op: %W[configure])
expect(result.stderr).to_not eq ""
expect(result.status).to_not eq 0
expect(result.stdout).to_not match /top configure/
end
it "does not pass RSCONS_BUILD_DIR to subsidiary scripts" do
test_dir "subsidiary"
passenv["RSCONS_BUILD_DIR"] = "buildit"
result = run_rscons(op: %W[configure])
expect(result.stderr).to eq ""
expect(Dir.exist?("build")).to be_falsey
expect(Dir.exist?("buildit")).to be_truthy
expect(Dir.exist?("sub/build")).to be_truthy
expect(Dir.exist?("sub/buildit")).to be_falsey
end
end
context "sh method" do
it "executes the command given" do
test_dir "sh"
result = run_rscons(rsconscript: "sh.rb")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
verify_lines(lines(result.stdout), [
"hi there",
"1 2",
])
end
it "prints the command when executing verbosely" do
test_dir "sh"
result = run_rscons(rsconscript: "sh.rb", rscons_args: %w[-v])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
verify_lines(lines(result.stdout), [
%r{echo 'hi there'},
"hi there",
%r{echo 1 2},
"1 2",
])
end
it "terminates execution on failure" do
test_dir "sh"
result = run_rscons(rsconscript: "sh_fail.rb")
expect(result.stderr).to match /sh_fail\.rb:2:.*foobar42/
expect(result.status).to_not eq 0
expect(result.stdout).to_not match /continued/
end
it "continues execution on failure when :continue option is set" do
test_dir "sh"
result = run_rscons(rsconscript: "sh_fail_continue.rb")
expect(result.stderr).to match /sh_fail_continue\.rb:2:.*foobar42/
expect(result.status).to eq 0
expect(result.stdout).to match /continued/
end
end
context "FileUtils methods" do
it "defines FileUtils methods to be available in the build script" do
test_dir "typical"
result = run_rscons(rsconscript: "fileutils_methods.rb")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(Dir.exist?("foobar")).to be_truthy
expect(Dir.exist?("foo")).to be_falsey
expect(File.exist?("foobar/baz/b.txt")).to be_truthy
end
end
end
+1 -1
View File
@@ -68,7 +68,7 @@ EOF
expect(RbConfig::CONFIG).to receive(:[]).with("host_os").and_return("mingw")
end
it "returns the number of logical processors that wmic reports" do
expect(Util).to receive(:`).with("wmic cpu get NumberOfLogicalProcessors /value").and_return("NumberOfLogicalProcessors=7")
expect(Util).to receive(:`).with("wmic cpu get NumberOfLogicalProcessors -value").and_return("NumberOfLogicalProcessors=7")
expect(Util.determine_n_threads).to eq(7)
end
end
+20 -20
View File
@@ -1,25 +1,25 @@
require "simplecov"
SimpleCov.start do
add_filter "/spec/"
add_filter "/.bundle/"
if ENV["partial_specs"]
command_name "RSpec-partial"
else
command_name "RSpec"
end
if ENV["dist_specs"]
add_filter "/bin/"
add_filter "/lib/"
else
add_filter "test/rscons.rb"
end
project_name "Rscons"
merge_timeout 3600
end
if ENV["dist_specs"]
require_relative "../test/rscons"
else
require "simplecov"
SimpleCov.start do
add_filter "/spec/"
add_filter "/.bundle/"
if ENV["partial_specs"]
command_name "RSpec-partial"
else
command_name "RSpec"
end
if ENV["dist_specs"]
add_filter "/bin/"
add_filter "/lib/"
else
add_filter "test/rscons.rb"
end
project_name "Rscons"
merge_timeout 3600
end
require "rscons"
end