Compare commits

...

46 Commits

Author SHA1 Message Date
7055cad73c Add v2.3.0 release notes 2021-12-04 09:27:24 -05:00
4548f4e3c9 Document phony targets - close #141 2021-12-01 23:00:12 -05:00
19dbab3426 Run subsidiary rscons binary from rscons() method if found - close #129 2021-11-28 22:36:24 -05:00
69f5bea2b2 Fix specs for dependency file moving - #135 2021-11-28 15:10:25 -05:00
2ee9dda49d Write dependency file to build directory when user invokes Object builder directly - close #135 2021-11-18 20:18:19 -05:00
f011b23499 Add PATH manipulation methods - close #126 2021-11-17 21:39:58 -05:00
80d52f25b8 Document CMD_STDOUT variable for Command builder - close #134 2021-11-15 21:52:11 -05:00
0face546e3 Clarify failed command error message indicating to run -F - close #133 2021-11-15 21:45:18 -05:00
ca747232cd Support passing a directory to rscons() method - close #128 2021-11-13 11:36:25 -05:00
a7b46093e9 Document -f command line option - close #130 2021-11-13 11:07:15 -05:00
09892eed63 Add DEVELOPING.md 2021-11-07 21:09:42 -05:00
117df43f64 Document rscons() build script method for subsidiary build scripts 2021-10-27 23:02:04 -04:00
acc12822b6 Add description for user_guide task 2021-10-27 22:42:31 -04:00
8c7b43f60c Show a message when entering/leaving a subsidiary script directory 2021-10-22 16:48:44 -04:00
7c8becc3f9 env.depends() does not work with build-root-relative "^/" paths - close #121 2021-10-22 16:03:10 -04:00
d1a35501ef Support subsidiary Rsconscript files - close #125 2021-10-22 13:58:38 -04:00
28a245f0ab build_dist: Remove lines marked just for specs 2021-10-22 13:58:38 -04:00
87a6d9f04f Do not run simplecov on dspec specs 2021-10-22 13:58:38 -04:00
556c821cc6 Commonize error handling across all operations 2021-10-21 18:35:58 -04:00
441fc9bc65 bundle update 2021-09-02 15:37:33 -04:00
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
73af83803a add v2.2.0 release notes 2020-10-13 19:04:29 -04:00
353f4f45bc v2.2.0 2020-10-13 18:59:18 -04:00
5bac91f92f add failure messages for failed configuration checks - close #119 2020-10-12 21:01:04 -04:00
bbe9563ceb compiler checks should support cross-compilers and freestanding compilers - close #118 2020-10-08 19:23:17 -04:00
5ec74604c6 improve support for MSYS2 - close #120 2020-10-08 19:04:50 -04:00
8e06efe61e v2.1.0 2020-04-16 16:52:58 -04:00
0756874ddb update CHANGELOG for v2.1.0 2020-04-16 16:51:16 -04:00
dded5e2648 ruby 2.7 compatibility - #117 2020-04-16 15:48:45 -04:00
fbe60f6ba2 stick with simplecov 0.15.x for now 2020-04-16 15:02:01 -04:00
8dd9799666 Revert "update simplecov"
This reverts commit 01ca86de8b02ef7db222eb5db2598762ba9a516b.
2020-04-16 15:02:01 -04:00
f2c755c8bb update bundle 2020-04-16 13:07:20 -04:00
01ca86de8b update simplecov 2020-02-29 14:27:21 -05:00
943fabd0d1 update some gems 2020-02-29 10:47:46 -05:00
d08e2f6b5c v2.0.2 2019-12-16 21:36:38 -05:00
90365dd197 update CHANGELOG for v2.0.2 2019-12-16 21:36:22 -05:00
8d05516c40 distinguish object files built from multiple sources with the same base name but different extensions - close #113 2019-12-14 00:10:42 -05:00
db49d86866 show a configuration failed message on stderr on configuration failure 2019-10-14 22:57:49 -04:00
e667455c32 user guide: fix configure script example 2019-10-14 22:49:34 -04:00
e62332aba6 avoid backtrace when configuring fails during autoconf 2019-10-14 22:23:18 -04:00
397639849d call print_run_message() in json_to_yaml build test 2019-10-12 17:59:08 -04:00
96d5caf2e5 user guide: fix print_run_message() examples 2019-10-12 17:57:58 -04:00
f4b10a30af update CHANGELOG for v2.0.1 2019-10-02 21:10:29 -04:00
cf52c3fc6f v2.0.1 2019-10-02 20:59:58 -04:00
5378afb6f8 Install builder cannot replace a currently executing binary on Linux - close #112 2019-09-28 23:18:31 -04:00
48 changed files with 963 additions and 366 deletions

View File

@ -1,3 +1,45 @@
## 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
- #113 - distinguish object files built from multiple sources with the same base name but different extensions
## v2.0.1
### Fixes
- #112 - Install builder cannot replace a currently executing binary on Linux
## v2.0.0 ## v2.0.0
- convert rscons from a Ruby gem to a standalone script - convert rscons from a Ruby gem to a standalone script

14
DEVELOPING.md Normal file
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

View File

@ -1,10 +1,14 @@
source 'https://rubygems.org' source 'https://rubygems.org'
gem "json"
gem "rspec" gem "rspec"
gem "rake" gem "rake"
gem "simplecov" gem "simplecov", "~> 0.15.0"
if RbConfig::CONFIG["host"]["msys"]
gem "json", "2.1.0"
else
gem "json"
gem "yard" gem "yard"
gem "rdoc" gem "rdoc"
gem "redcarpet" gem "redcarpet"
gem "syntax" gem "syntax"
end

View File

@ -1,32 +1,32 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
diff-lcs (1.3) diff-lcs (1.4.4)
docile (1.3.2) docile (1.1.5)
json (2.2.0) json (2.5.1)
rake (12.3.2) rake (13.0.6)
rdoc (6.1.1) rdoc (6.3.2)
redcarpet (3.4.0) redcarpet (3.5.1)
rspec (3.8.0) rspec (3.10.0)
rspec-core (~> 3.8.0) rspec-core (~> 3.10.0)
rspec-expectations (~> 3.8.0) rspec-expectations (~> 3.10.0)
rspec-mocks (~> 3.8.0) rspec-mocks (~> 3.10.0)
rspec-core (3.8.2) rspec-core (3.10.1)
rspec-support (~> 3.8.0) rspec-support (~> 3.10.0)
rspec-expectations (3.8.4) rspec-expectations (3.10.1)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0) rspec-support (~> 3.10.0)
rspec-mocks (3.8.1) rspec-mocks (3.10.2)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0) rspec-support (~> 3.10.0)
rspec-support (3.8.2) rspec-support (3.10.2)
simplecov (0.15.0) simplecov (0.15.1)
docile (~> 1.1.0) docile (~> 1.1.0)
json (>= 1.8, < 3) json (>= 1.8, < 3)
simplecov-html (~> 0.10.0) simplecov-html (~> 0.10.0)
simplecov-html (0.10.2) simplecov-html (0.10.2)
syntax (1.2.2) syntax (1.2.2)
yard (0.9.20) yard (0.9.26)
PLATFORMS PLATFORMS
ruby ruby
@ -38,9 +38,9 @@ DEPENDENCIES
rdoc rdoc
redcarpet redcarpet
rspec rspec
simplecov simplecov (~> 0.15.0)
syntax syntax
yard yard
BUNDLED WITH BUNDLED WITH
1.17.3 2.1.4

View File

@ -6,7 +6,6 @@ rescue Bundler::BundlerError => e
end end
require "rspec/core/rake_task" require "rspec/core/rake_task"
require "yard"
require "rake/clean" require "rake/clean"
require "fileutils" require "fileutils"
@ -18,12 +17,16 @@ task :build_dist do
end end
RSpec::Core::RakeTask.new(:spec, :example_string) do |task, args| RSpec::Core::RakeTask.new(:spec, :example_string) do |task, args|
ENV["specs"] = "1"
if args.example_string if args.example_string
ENV["partial_specs"] = "1" ENV["partial_specs"] = "1"
task.rspec_opts = %W[-e "#{args.example_string}" -f documentation] task.rspec_opts = %W[-e "#{args.example_string}" -f documentation]
end end
end end
task :spec => :build_dist task :spec => :build_dist
task :spec do
ENV.delete("specs")
end
# dspec task is useful to test the distributable release script, but is not # dspec task is useful to test the distributable release script, but is not
# useful for coverage information. # useful for coverage information.
@ -32,16 +35,11 @@ task :dspec, [:example_string] => :build_dist do |task, args|
FileUtils.mkdir_p("test") FileUtils.mkdir_p("test")
FileUtils.cp("dist/rscons", "test/rscons.rb") FileUtils.cp("dist/rscons", "test/rscons.rb")
ENV["dist_specs"] = "1" ENV["dist_specs"] = "1"
Rake::Task["spec"].invoke(args.example_string) Rake::Task["spec"].execute(args.example_string)
ENV.delete("dist_specs") ENV.delete("dist_specs")
FileUtils.rm_f(Dir.glob(".rscons-*")) FileUtils.rm_f(Dir.glob(".rscons-*"))
end 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| task :gen_large_project, [:size] => :build_dist do |task, args|
size = (args.size || 10000).to_i size = (args.size || 10000).to_i
FileUtils.rm_rf("large_project") FileUtils.rm_rf("large_project")
@ -88,15 +86,25 @@ EOF
FileUtils.cp("dist/rscons", "large_project") FileUtils.cp("dist/rscons", "large_project")
end end
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 task :user_guide do
system("ruby", "-Ilib", "rb/gen_user_guide.rb") system("ruby", "-Ilib", "rb/gen_user_guide.rb")
end end
end
task :default => :spec task :default => :spec
task :all => [ task :all => [
:build_dist, :build_dist,
:spec, :spec,
:dspec,
:yard, :yard,
:user_guide, :user_guide,
] ]

View File

@ -1,4 +1,5 @@
configure do configure do
check_d_compiler
check_d_import "std.stdio", check_d_import_path: ["./usr1"] check_d_import "std.stdio", check_d_import_path: ["./usr1"]
check_d_import "frobulous", check_d_import_path: ["./usr2"] check_d_import "frobulous", check_d_import_path: ["./usr2"]
end end

View File

@ -1,3 +1,4 @@
configure do configure do
check_d_compiler
check_d_import "std.stdio" check_d_import "std.stdio"
end end

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

View File

@ -6,7 +6,8 @@ build do
"CMD_DESC" => "Generating") "CMD_DESC" => "Generating")
env["build_root"] = env.build_root env["build_root"] = env.build_root
env["inc_c"] = "inc.c" env["inc_c"] = "inc.c"
env.build_after("${build_root}/program.o", "${inc_c}") env.Object("program.o", "program.c")
env.Program("program.exe", ["program.c", "inc.c"]) env.build_after("program.o", "${inc_c}")
env.Program("program.exe", ["program.o", "inc.c"])
end end
end end

View File

@ -4,8 +4,9 @@ build do
env["inc_h"] = "inc.h" env["inc_h"] = "inc.h"
env.Copy("copy_inc.h", "${inc_h}") env.Copy("copy_inc.h", "${inc_h}")
env.depends("${build_root}/program.o", "${inc_h}") env.depends("program.o", "${inc_h}")
env.Program("program.exe", ["program.c", "inc.c"]) env.Object("program.o", "program.c")
env.Program("program.exe", ["program.o", "inc.c"])
inc_c = env.Command("inc.c", inc_c = env.Command("inc.c",
[], [],

View File

@ -4,8 +4,9 @@ build do
env["inc_h"] = "inc.h" env["inc_h"] = "inc.h"
env.Copy("copy_inc.h", "${inc_h}") env.Copy("copy_inc.h", "${inc_h}")
env.depends("${build_root}/program.o", "${inc_h}") env.depends("program.o", "${inc_h}")
env.Program("program.exe", ["program.c", "inc.c"]) env.Object("program.o", "program.c")
env.Program("program.exe", ["program.o", "inc.c"])
env.Command("inc.c", env.Command("inc.c",
[], [],

View File

@ -4,6 +4,7 @@ build do
require 'yaml' require 'yaml'
env.add_builder(:JsonToYaml) do |params| env.add_builder(:JsonToYaml) do |params|
unless @cache.up_to_date?(@target, :JsonToYaml, @sources, @env) unless @cache.up_to_date?(@target, :JsonToYaml, @sources, @env)
print_run_message("JsonToYaml #{@target}", nil)
@cache.mkdir_p(File.dirname(@target)) @cache.mkdir_p(File.dirname(@target))
File.open(@target, 'w') do |f| File.open(@target, 'w') do |f|
f.write(YAML.dump(JSON.load(IO.read(@sources.first)))) f.write(YAML.dump(JSON.load(IO.read(@sources.first))))

View File

@ -0,0 +1,5 @@
build do
Environment.new do |env|
env.Program("foo.exe", glob("*.cc", "*.c"))
end
end

View File

@ -0,0 +1,6 @@
#include <stdio.h>
void foo(void)
{
printf("foo\n");
}

View File

@ -0,0 +1,12 @@
#include <iostream>
extern "C" {
void foo(void);
}
int main(int argc, char * argv[])
{
foo();
std::cout << "main" << std::endl;
return 0;
}

View File

@ -0,0 +1,3 @@
#!/bin/sh
echo "foobar!"

View File

@ -0,0 +1,3 @@
#!/bin/sh
exit 42

View File

@ -0,0 +1,3 @@
#!/bin/sh
echo "flex!"

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

View File

@ -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

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

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

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

View File

@ -0,0 +1,4 @@
build do
rscons "second", "build"
puts "top build"
end

View File

@ -0,0 +1,3 @@
build do
puts "second build"
end

View File

@ -0,0 +1,7 @@
configure do
puts "sub Rsconscript configure"
end
build do
puts "sub Rsconscript build"
end

View File

@ -0,0 +1,7 @@
configure do
puts "sub Rsconscript2 configure"
end
build do
puts "sub Rsconscript2 build"
end

View File

@ -0,0 +1,8 @@
configure do
puts "sub Rsconscript configure"
check_program "foobarfailure"
end
build do
puts "sub Rsconscript build"
end

View File

@ -2,9 +2,9 @@ build do
Environment.new(echo: :command) do |env| Environment.new(echo: :command) do |env|
env.append('CPPPATH' => glob('src/**/*/')) env.append('CPPPATH' => glob('src/**/*/'))
env.add_build_hook do |builder| env.add_build_hook do |builder|
if File.basename(builder.target) == "one.o" if File.basename(builder.sources.first) == "one.c"
builder.vars["CFLAGS"] << "-O1" builder.vars["CFLAGS"] << "-O1"
elsif File.basename(builder.target) == "two.o" elsif File.basename(builder.sources.first) == "two.c"
builder.vars["CFLAGS"] << "-O2" builder.vars["CFLAGS"] << "-O2"
end end
end end

View File

@ -246,6 +246,15 @@ configure do
end 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 ###> Checking for a Header File
The following methods can be used to check for the presence of a header file: The following methods can be used to check for the presence of a header file:
@ -272,18 +281,6 @@ end
Optionally specifies an array of paths to look for the header file in. 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 ###> Checking for a D Import
The `check_d_import` method can be used to check for the presence of D import. The `check_d_import` method can be used to check for the presence of D import.
@ -327,18 +324,6 @@ end
Optionally specifies an array of paths to look for the library in. 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` ##### `:use`
If not set, the library will be used by default in all `Environment` objects. If not set, the library will be used by default in all `Environment` objects.
@ -387,18 +372,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 If the `:program` option is given, the program specified will be used to look
for configuration flags. 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` ##### `:use`
If not set, the library will be used by default in all `Environment` objects. If not set, the library will be used by default in all `Environment` objects.
@ -457,6 +430,46 @@ configure do
end 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 ##> Build Operations
The `build` block is used to create Environments and register build targets. The `build` block is used to create Environments and register build targets.
@ -599,14 +612,26 @@ There are several default builders that are built-in to Rscons:
```ruby ```ruby
env.Command(target, sources, "CMD" => command) env.Command(target, sources, "CMD" => command)
# Example # Example
env.Command("docs.html", "docs.md", env.Command("user_guide.html", "user_guide.md",
"CMD" => ["pandoc", "-fmarkdown", "-thtml", "-o${_TARGET}", "${_SOURCES}"], "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 The `Command` builder executes a user-defined command in order to produce the
desired target file based on the provided source files. 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 ####> The CFile Builder
```ruby ```ruby
@ -796,6 +821,22 @@ 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 Although it can be called explicitly, it is more commonly implicitly called by
the `SharedLibrary` builder. the `SharedLibrary` builder.
###> 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 ###> Explicit Dependencies
A target can be marked as depending on another file that Rscons would not A target can be marked as depending on another file that Rscons would not
@ -859,6 +900,70 @@ In other words, build targets are not parallelized across a barrier.
env.barrier env.barrier
``` ```
##> Global Build Script Functionality
###> Using Subsidiary Build Scripts
The `rscons` 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.
###> PATH Management
`rscons` provides methods for management of the `PATH` environment variable.
The `path_append` and `path_prepend` 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` method sets the `PATH` environment variable to the given
Array or String.
The `path_components` method returns an Array of the components in the `PATH`
environment variable.
##> Extending Rscons ##> Extending Rscons
### Adding New Languages ### Adding New Languages
@ -1025,7 +1130,7 @@ Here is our Custom builder example extended to print its status:
```ruby ```ruby
class Rscons::Builders::Custom < Rscons::Builder class Rscons::Builders::Custom < Rscons::Builder
def run(options) def run(options)
print_run_message("Creating <target>#{@target}<reset> from Custom builder") print_run_message("Creating <target>#{@target}<reset> from Custom builder", nil)
File.open(@target, "w") do |fh| File.open(@target, "w") do |fh|
fh.write("Target file created.") fh.write("Target file created.")
end end
@ -1050,7 +1155,7 @@ Here is a Custom builder which combines its source files similar to what the
class Rscons::Builders::Custom < Rscons::Builder class Rscons::Builders::Custom < Rscons::Builder
def run(options) def run(options)
unless @cache.up_to_date?(@target, nil, @sources, @env) unless @cache.up_to_date?(@target, nil, @sources, @env)
print_run_message("Combining <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>") print_run_message("Combining <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", nil)
File.open(@target, "wb") do |fh| File.open(@target, "wb") do |fh|
@sources.each do |source| @sources.each do |source|
fh.write(File.read(source, mode: "rb")) fh.write(File.read(source, mode: "rb"))
@ -1277,7 +1382,7 @@ To do this, create a `configure` script with contents similar to the following:
``` ```
#!/bin/sh #!/bin/sh
exec "$(dirname "$0")"/rscons "$@" exec "$(dirname "$0")"/rscons configure "$@"
``` ```
and make it executable with `chmod +x configure`. and make it executable with `chmod +x configure`.

View File

@ -35,8 +35,8 @@ module Rscons
:SharedObject, :SharedObject,
] ]
# Class to represent a fatal error while building a target. # Class to represent a fatal error during an Rscons operation.
class BuildError < RuntimeError; end class RsconsError < RuntimeError; end
class << self class << self
@ -64,16 +64,6 @@ module Rscons
target.is_a?(Symbol) target.is_a?(Symbol)
end end
# Return a new path by changing the suffix in path to suffix.
#
# @param path [String] The path to alter.
# @param suffix [String] The new filename suffix, e.g. ".exe".
#
# @return [String] New path.
def set_suffix(path, suffix)
path.sub(/\.[^.]*$/, "") + suffix
end
# Return the system shell and arguments for executing a shell command. # Return the system shell and arguments for executing a shell command.
# #
# @return [Array<String>] The shell and flag. # @return [Array<String>] The shell and flag.
@ -91,7 +81,7 @@ module Rscons
end end
if ENV["SHELL"] and ENV["SHELL"] != "" and test_shell[ENV["SHELL"], "-c"] if ENV["SHELL"] and ENV["SHELL"] != "" and test_shell[ENV["SHELL"], "-c"]
[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"] if test_shell["sh", "-c"]
# Using Rscons from MSYS should use MSYS's shell. # Using Rscons from MSYS should use MSYS's shell.
["sh", "-c"] ["sh", "-c"]
@ -112,7 +102,7 @@ module Rscons
# @return [Array<String>] Command used to execute commands. # @return [Array<String>] Command used to execute commands.
def command_executer def command_executer
@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/} if ENV.keys.find {|key| key =~ /MSYS/}
begin begin
if IO.popen(["env", "echo", "success"]) {|io| io.read.strip} == "success" if IO.popen(["env", "echo", "success"]) {|io| io.read.strip} == "success"

View File

@ -84,7 +84,7 @@ module Rscons
# @return [Boolean] # @return [Boolean]
# Whether to output ANSI color escape codes. # Whether to output ANSI color escape codes.
def do_ansi?(io) def do_ansi?(io)
if RUBY_PLATFORM =~ /mingw/ if RUBY_PLATFORM =~ /mingw|msys/
(ENV["TERM"] == "xterm") && %w[fifo characterSpecial].include?(io.stat.ftype) (ENV["TERM"] == "xterm") && %w[fifo characterSpecial].include?(io.stat.ftype)
else else
io.tty? io.tty?

View File

@ -75,6 +75,8 @@ module Rscons
end end
if rv == 0 if rv == 0
build(operation_options) build(operation_options)
else
rv
end end
when "clean" when "clean"
clean clean
@ -134,8 +136,8 @@ module Rscons
env.process env.process
end end
0 0
rescue BuildError => be rescue RsconsError => e
Ansi.write($stderr, :red, be.message, :reset, "\n") Ansi.write($stderr, :red, e.message, :reset, "\n")
1 1
end end
end end
@ -191,7 +193,11 @@ module Rscons
co = ConfigureOp.new(options) co = ConfigureOp.new(options)
begin begin
@script.configure(co) @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 rv = 1
end end
co.close(rv == 0) co.close(rv == 0)

View File

@ -57,6 +57,7 @@ module Rscons
printed_message = true printed_message = true
end end
@cache.mkdir_p(File.dirname(dest), install: @install_builder) @cache.mkdir_p(File.dirname(dest), install: @install_builder)
FileUtils.rm_f(dest)
FileUtils.cp(src, dest, :preserve => true) FileUtils.cp(src, dest, :preserve => true)
end end
@cache.register_build(dest, :Copy, [src], @env, install: @install_builder) @cache.register_build(dest, :Copy, [src], @env, install: @install_builder)

View File

@ -75,10 +75,10 @@ module Rscons
@vars["_SOURCES"] = @sources @vars["_SOURCES"] = @sources
depfilesuffix = @env.expand_varref("${DEPFILESUFFIX}", vars) depfilesuffix = @env.expand_varref("${DEPFILESUFFIX}", vars)
@vars["_DEPFILE"] = @vars["_DEPFILE"] =
if @vars[:direct] if @vars[:direct] || !@target.start_with?("#{@env.build_root}/")
@env.get_build_fname(target, depfilesuffix, self.class) @env.get_build_fname(@target, depfilesuffix, self.class)
else else
Rscons.set_suffix(target, depfilesuffix) "#{@target}#{depfilesuffix}"
end end
@cache.mkdir_p(File.dirname(@vars["_DEPFILE"])) @cache.mkdir_p(File.dirname(@vars["_DEPFILE"]))
command = @env.build_command(@command_template, @vars) command = @env.build_command(@command_template, @vars)

View File

@ -23,7 +23,7 @@ module Rscons
@vars["_PREPROCESS_DEPGEN"] = depgen @vars["_PREPROCESS_DEPGEN"] = depgen
@vars["_TARGET"] = @target @vars["_TARGET"] = @target
@vars["_SOURCES"] = @sources @vars["_SOURCES"] = @sources
@vars["_DEPFILE"] = Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars)) @vars["_DEPFILE"] = "#{target}#{env.expand_varref("${DEPFILESUFFIX}", vars)}"
command = @env.build_command("${CPP_CMD}", @vars) command = @env.build_command("${CPP_CMD}", @vars)
self.produces(@vars["_DEPFILE"]) self.produces(@vars["_DEPFILE"])
standard_command("Preprocessing <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command) standard_command("Preprocessing <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command)

View File

@ -6,11 +6,13 @@ USAGE = <<EOF
Usage: #{$0} [global options] [operation] [operation options] Usage: #{$0} [global options] [operation] [operation options]
Global options: Global options:
-f FILE Use FILE as Rsconscript
-F, --show-failure Show failed command log from previous build and exit -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 -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) -r COLOR, --color=COLOR Set color mode (off, auto, force)
-v, --verbose Run verbosely -v, --verbose Run verbosely
--version Show rscons version and exit
Operations: Operations:
configure Configure the project configure Configure the project
@ -23,10 +25,6 @@ Operations:
Configure options: Configure options:
-b BUILD, --build=BUILD Set build directory (default: build) -b BUILD, --build=BUILD Set build directory (default: build)
--prefix=PREFIX Set installation prefix (default: /usr/local) --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 EOF
module Rscons module Rscons

View File

@ -5,9 +5,6 @@ module Rscons
# Class to manage a configure operation. # Class to manage a configure operation.
class ConfigureOp class ConfigureOp
# Exception raised when a configuration error occurs.
class ConfigureFailure < Exception; end
# Create a ConfigureOp. # Create a ConfigureOp.
# #
# @param options [Hash] # @param options [Hash]
@ -62,6 +59,10 @@ module Rscons
# @return [void] # @return [void]
def check_c_compiler(*ccc) def check_c_compiler(*ccc)
$stdout.write("Checking for C compiler... ") $stdout.write("Checking for C compiler... ")
options = {}
if ccc.last.is_a?(Hash)
options = ccc.slice!(-1)
end
if ccc.empty? if ccc.empty?
# Default C compiler search array. # Default C compiler search array.
ccc = %w[gcc clang] ccc = %w[gcc clang]
@ -69,7 +70,7 @@ module Rscons
cc = ccc.find do |cc| cc = ccc.find do |cc|
test_c_compiler(cc) test_c_compiler(cc)
end end
complete(cc ? 0 : 1, success_message: cc) complete(cc ? 0 : 1, options.merge(success_message: cc))
end end
# Check for a working C++ compiler. # Check for a working C++ compiler.
@ -80,6 +81,10 @@ module Rscons
# @return [void] # @return [void]
def check_cxx_compiler(*ccc) def check_cxx_compiler(*ccc)
$stdout.write("Checking for C++ compiler... ") $stdout.write("Checking for C++ compiler... ")
options = {}
if ccc.last.is_a?(Hash)
options = ccc.slice!(-1)
end
if ccc.empty? if ccc.empty?
# Default C++ compiler search array. # Default C++ compiler search array.
ccc = %w[g++ clang++] ccc = %w[g++ clang++]
@ -87,7 +92,7 @@ module Rscons
cc = ccc.find do |cc| cc = ccc.find do |cc|
test_cxx_compiler(cc) test_cxx_compiler(cc)
end end
complete(cc ? 0 : 1, success_message: cc) complete(cc ? 0 : 1, options.merge(success_message: cc))
end end
# Check for a working D compiler. # Check for a working D compiler.
@ -98,6 +103,10 @@ module Rscons
# @return [void] # @return [void]
def check_d_compiler(*cdc) def check_d_compiler(*cdc)
$stdout.write("Checking for D compiler... ") $stdout.write("Checking for D compiler... ")
options = {}
if cdc.last.is_a?(Hash)
options = cdc.slice!(-1)
end
if cdc.empty? if cdc.empty?
# Default D compiler search array. # Default D compiler search array.
cdc = %w[gdc ldc2] cdc = %w[gdc ldc2]
@ -105,7 +114,7 @@ module Rscons
dc = cdc.find do |dc| dc = cdc.find do |dc|
test_d_compiler(dc) test_d_compiler(dc)
end end
complete(dc ? 0 : 1, success_message: dc) complete(dc ? 0 : 1, options.merge(success_message: dc))
end end
# Check for a package or configure program output. # Check for a package or configure program output.
@ -387,11 +396,15 @@ module Rscons
else else
!options[:set_define] !options[:set_define]
end 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 if should_fail
Ansi.write($stdout, :red, "#{fail_message}\n") raise RsconsError.new
raise ConfigureFailure.new
else
Ansi.write($stdout, :yellow, "#{fail_message}\n")
end end
end end
end end
@ -408,24 +421,19 @@ module Rscons
def test_c_compiler(cc) def test_c_compiler(cc)
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh| File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
fh.puts <<-EOF fh.puts <<-EOF
#include <stdio.h> int fun(int val) {
int main(int argc, char * argv[]) { return val * 2;
printf("Success.\\n");
return 0;
} }
EOF EOF
end 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} merge = {"CC" => cc}
_, _, status = log_and_test_command(command) _, _, status = log_and_test_command(command)
if status == 0 if status == 0
stdout, _, status = log_and_test_command(["#{@work_dir}/cfgtest.exe"])
if status == 0 and stdout =~ /Success/
store_merge(merge) store_merge(merge)
true true
end end
end end
end
# Test a C++ compiler. # Test a C++ compiler.
# #
@ -437,25 +445,20 @@ module Rscons
def test_cxx_compiler(cc) def test_cxx_compiler(cc)
File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh| File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh|
fh.puts <<-EOF fh.puts <<-EOF
#include <iostream> template<typename T>
using namespace std; T fun(T val) {
int main(int argc, char * argv[]) { return val * 2;
cout << "Success." << endl;
return 0;
} }
EOF EOF
end 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} merge = {"CXX" => cc}
_, _, status = log_and_test_command(command) _, _, status = log_and_test_command(command)
if status == 0 if status == 0
stdout, _, status = log_and_test_command(["#{@work_dir}/cfgtest.exe"])
if status == 0 and stdout =~ /Success/
store_merge(merge) store_merge(merge)
true true
end end
end end
end
# Test a D compiler. # Test a D compiler.
# #
@ -467,9 +470,8 @@ module Rscons
def test_d_compiler(dc) def test_d_compiler(dc)
File.open("#{@work_dir}/cfgtest.d", "wb") do |fh| File.open("#{@work_dir}/cfgtest.d", "wb") do |fh|
fh.puts <<-EOF fh.puts <<-EOF
import std.stdio; import core.math;
int main() { int fun() {
writeln("Success.");
return 0; return 0;
} }
EOF EOF
@ -477,32 +479,28 @@ module Rscons
[:gdc, :ldc2].find do |dc_test| [:gdc, :ldc2].find do |dc_test|
case dc_test case dc_test
when :gdc 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} merge = {"DC" => dc}
when :ldc2 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 env = BasicEnvironment.new
merge = { merge = {
"DC" => dc, "DC" => dc,
"DCCMD" => env["DCCMD"].map {|e| if e == "-o"; "-of"; else; e; end}, "DCCMD" => env["DCCMD"].map {|e| e.sub(/^-o$/, "-of")},
"LDCMD" => env["LDCMD"].map {|e| if e == "-o"; "-of"; else; e; end}, "LDCMD" => env["LDCMD"].map {|e| e.sub(/^-o$/, "-of")},
"DDEPGEN" => ["-deps=${_DEPFILE}"], "DDEPGEN" => ["-deps=${_DEPFILE}"],
} }
if RUBY_PLATFORM =~ /mingw/ merge["OBJSUFFIX"] = [ldc_objsuffix]
# ldc2 on Windows expect an object file suffix of .obj.
merge["OBJSUFFIX"] = %w[.obj]
end
end end
_, _, status = log_and_test_command(command) _, _, status = log_and_test_command(command)
if status == 0 if status == 0
stdout, _, status = log_and_test_command(["#{@work_dir}/cfgtest.exe"])
if status == 0 and stdout =~ /Success/
store_merge(merge) store_merge(merge)
true true
end end
end end
end end
end
# Common functionality for all store methods. # Common functionality for all store methods.
# #

View File

@ -1,6 +1,6 @@
module Rscons module Rscons
on_windows = RUBY_PLATFORM =~ /mingw|cygwin/ on_windows = RUBY_PLATFORM =~ /mingw|msys|cygwin/
pic_flags = on_windows ? [] : %w[-fPIC] pic_flags = on_windows ? [] : %w[-fPIC]
# Default Rscons construction variables. # Default Rscons construction variables.

View File

@ -1,7 +1,6 @@
require "fileutils" require "fileutils"
require "set" require "set"
require "shellwords" require "shellwords"
require "thwait"
module Rscons module Rscons
# The Environment class is the main programmatic interface to Rscons. It # The Environment class is the main programmatic interface to Rscons. It
@ -251,7 +250,7 @@ module Rscons
if extra_path = builder_class.extra_path if extra_path = builder_class.extra_path
extra_path = "/#{extra_path}" extra_path = "/#{extra_path}"
end end
"#{@build_root}#{extra_path}/#{Util.make_relative_path(Rscons.set_suffix(source_fname, suffix))}".gsub("\\", "/") "#{@build_root}#{extra_path}/#{Util.make_relative_path("#{source_fname}#{suffix}")}".gsub("\\", "/")
end end
# Build all build targets specified in the Environment. # Build all build targets specified in the Environment.
@ -287,9 +286,9 @@ module Rscons
unless @process_failures.empty? unless @process_failures.empty?
msg = @process_failures.join("\n") msg = @process_failures.join("\n")
if Cache.instance["failed_commands"].size > 0 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 end
raise BuildError.new(msg) raise RsconsError.new(msg)
end end
end end
@ -347,12 +346,12 @@ module Rscons
if target.is_a?(Builder) if target.is_a?(Builder)
target = target.target target = target.target
end end
target = expand_varref(target.to_s) target = expand_path(expand_varref(target.to_s))
user_deps = user_deps.map do |ud| user_deps = user_deps.map do |ud|
if ud.is_a?(Builder) if ud.is_a?(Builder)
ud = ud.target ud = ud.target
end end
expand_varref(ud) expand_path(expand_varref(ud))
end end
@user_deps[target] ||= [] @user_deps[target] ||= []
@user_deps[target] = (@user_deps[target] + user_deps).uniq @user_deps[target] = (@user_deps[target] + user_deps).uniq
@ -702,10 +701,7 @@ module Rscons
!thread.alive? !thread.alive?
end end
else else
if @threads.empty? Util.wait_for_thread(*@threads.keys)
raise "No threads to wait for"
end
ThreadsWait.new(*@threads.keys).next_wait
end end
end end

View File

@ -3,8 +3,87 @@ module Rscons
# The Script class encapsulates the state of a build script. # The Script class encapsulates the state of a build script.
class Script class Script
# DSL available to the Rsconscript. # Global DSL methods.
class Dsl class GlobalDsl
# 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
end
# Top-level DSL available to the Rsconscript.
class Dsl < GlobalDsl
# Create a Dsl. # Create a Dsl.
def initialize(script) def initialize(script)
@script = script @script = script
@ -59,7 +138,7 @@ module Rscons
end end
# DSL available to the 'configure' block. # DSL available to the 'configure' block.
class ConfigureDsl class ConfigureDsl < GlobalDsl
# Create a ConfigureDsl. # Create a ConfigureDsl.
# #
# @param configure_op [ConfigureOp] # @param configure_op [ConfigureOp]

View File

@ -9,7 +9,7 @@ module Rscons
# #
# @return [Boolean] Whether the given path is an absolute filesystem path. # @return [Boolean] Whether the given path is an absolute filesystem path.
def absolute_path?(path) def absolute_path?(path)
if RUBY_PLATFORM =~ /mingw/ if RUBY_PLATFORM =~ /mingw|msys/
path =~ %r{^(?:\w:)?[\\/]} path =~ %r{^(?:\w:)?[\\/]}
else else
path.start_with?("/") path.start_with?("/")
@ -68,8 +68,8 @@ module Rscons
case RbConfig::CONFIG["host_os"] case RbConfig::CONFIG["host_os"]
when /linux/ when /linux/
return File.read("/proc/cpuinfo").scan(/^processor\s*:/).size return File.read("/proc/cpuinfo").scan(/^processor\s*:/).size
when /mswin|mingw/ when /mswin|mingw|msys/
if `wmic cpu get NumberOfLogicalProcessors /value` =~ /NumberOfLogicalProcessors=(\d+)/ if `wmic cpu get NumberOfLogicalProcessors -value` =~ /NumberOfLogicalProcessors=(\d+)/
return $1.to_i return $1.to_i
end end
when /darwin/ when /darwin/
@ -191,6 +191,40 @@ module Rscons
deps deps
end 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 private
# Check if a directory contains a certain executable. # Check if a directory contains a certain executable.
@ -199,11 +233,14 @@ module Rscons
# Directory to look in. # Directory to look in.
# @param executable [String] # @param executable [String]
# Executable to look for. # Executable to look for.
#
# @return [String, nil]
# Executable found.
def test_path_for_executable(path_entry, executable) def test_path_for_executable(path_entry, executable)
is_executable = lambda do |path| is_executable = lambda do |path|
File.file?(path) and File.executable?(path) File.file?(path) and File.executable?(path)
end 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) if File.directory?(path_entry)
executable = executable.downcase executable = executable.downcase
dir_entries = Dir.entries(path_entry) dir_entries = Dir.entries(path_entry)

View File

@ -1,4 +1,4 @@
module Rscons module Rscons
# Project version. # Project version.
VERSION = "2.0.0" VERSION = "2.2.0"
end end

View File

@ -44,7 +44,7 @@ combine_files[START_FILE]
# Strip Ruby comment lines and empty lines to save some space, but do not # 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 # 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. # used in the wild, but works for the heredoc instances for this project.
stripped_comments = [] stripped = []
heredoc_end = nil heredoc_end = nil
combined_file.each do |line| combined_file.each do |line|
if line =~ /<<-?([A-Z]+)/ if line =~ /<<-?([A-Z]+)/
@ -53,14 +53,16 @@ combined_file.each do |line|
if heredoc_end and line =~ /^\s*#{heredoc_end}/ if heredoc_end and line =~ /^\s*#{heredoc_end}/
heredoc_end = nil heredoc_end = nil
end end
if heredoc_end or not (line =~ /^\s*(#[^!].*)?$/) if line !~ /#\sspecs/
stripped_comments << line if heredoc_end or line !~ /^\s*(#[^!].*)?$/
stripped << line
end
end end
end end
require "zlib" require "zlib"
require "base64" 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", "") encoded_compressed_script = Base64.encode64(compressed_script).gsub("\n", "")
hash = Digest::MD5.hexdigest(encoded_compressed_script) hash = Digest::MD5.hexdigest(encoded_compressed_script)

View File

@ -55,6 +55,8 @@ describe Rscons do
end end
end end
let(:passenv) {{}}
def test_dir(build_test_directory) def test_dir(build_test_directory)
Dir.chdir(@owd) Dir.chdir(@owd)
rm_rf(@build_test_run_dir) rm_rf(@build_test_run_dir)
@ -118,6 +120,7 @@ describe Rscons do
command_name = "#{command_prefix}#{@statics[:build_test_id]}" command_name = "#{command_prefix}#{@statics[:build_test_id]}"
File.open("_simplecov_setup.rb", "w") do |fh| File.open("_simplecov_setup.rb", "w") do |fh|
fh.puts <<EOF fh.puts <<EOF
unless ENV["dist_specs"]
require "bundler" require "bundler"
Bundler.setup Bundler.setup
require "simplecov" require "simplecov"
@ -134,6 +137,7 @@ SimpleCov.start do
end end
formatter(MyFormatter) formatter(MyFormatter)
end end
end
# force color off # force color off
ENV["TERM"] = nil ENV["TERM"] = nil
#{options[:ruby_setup_code]} #{options[:ruby_setup_code]}
@ -143,8 +147,9 @@ EOF
end end
end end
stdout, stderr, status = nil, nil, nil stdout, stderr, status = nil, nil, nil
Bundler.with_clean_env do Bundler.with_unbundled_env do
env = ENV.to_h env = ENV.to_h
env.merge!(passenv)
path = ["#{@build_test_run_dir}/_bin", "#{env["PATH"]}"] path = ["#{@build_test_run_dir}/_bin", "#{env["PATH"]}"]
if options[:path] if options[:path]
path = Array(options[:path]) + path path = Array(options[:path]) + path
@ -187,6 +192,10 @@ EOF
end end
end end
def nr(str)
str.gsub("\r", "")
end
########################################################################### ###########################################################################
# Tests # Tests
########################################################################### ###########################################################################
@ -195,8 +204,8 @@ EOF
test_dir('simple') test_dir('simple')
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?('build/e.1/simple.o')).to be_truthy 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 end
it "allows specifying a Builder object as the source to another build target" do it "allows specifying a Builder object as the source to another build target" do
@ -204,7 +213,7 @@ EOF
result = run_rscons(rsconscript: "builder_as_source.rb") result = run_rscons(rsconscript: "builder_as_source.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("simple.o")).to be_truthy 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 end
it 'prints commands as they are executed' do it 'prints commands as they are executed' do
@ -212,8 +221,8 @@ EOF
result = run_rscons(rsconscript: "command.rb") result = run_rscons(rsconscript: "command.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{gcc -c -o build/e.1/simple.o -MMD -MF build/e.1/simple.mf simple.c}, %r{gcc -c -o build/e.1/simple.c.o -MMD -MF build/e.1/simple.c.o.mf simple.c},
%r{gcc -o simple.exe build/e.1/simple.o}, %r{gcc -o simple.exe build/e.1/simple.c.o},
]) ])
end end
@ -231,19 +240,19 @@ EOF
test_dir('header') test_dir('header')
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?('build/e.1/header.o')).to be_truthy 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 end
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')
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" 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')} file_sub('header.h') {|line| line.sub(/2/, '5')}
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" 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 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
@ -254,7 +263,7 @@ EOF
%r{Compiling header.c}, %r{Compiling header.c},
%r{Linking header.exe}, %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 result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(result.stdout).to eq "" expect(result.stdout).to eq ""
@ -268,7 +277,7 @@ EOF
%r{Compiling header.c}, %r{Compiling header.c},
%r{Linking header.exe}, %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 sleep 0.05
file_sub('header.c') {|line| line} file_sub('header.c') {|line| line}
result = run_rscons result = run_rscons
@ -281,13 +290,13 @@ EOF
result = run_rscons(rsconscript: "command.rb") result = run_rscons(rsconscript: "command.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{gcc -c -o build/e.1/simple.o -MMD -MF build/e.1/simple.mf simple.c}, %r{gcc -c -o build/e.1/simple.c.o -MMD -MF build/e.1/simple.c.o.mf simple.c},
%r{gcc -o simple.exe build/e.1/simple.o}, %r{gcc -o simple.exe build/e.1/simple.c.o},
]) ])
result = run_rscons(rsconscript: "link_flag_change.rb") result = run_rscons(rsconscript: "link_flag_change.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{gcc -o simple.exe build/e.1/simple.o -Llibdir}, %r{gcc -o simple.exe build/e.1/simple.c.o -Llibdir},
]) ])
end end
@ -317,9 +326,9 @@ EOF
result = run_rscons(rsconscript: "carat.rb") result = run_rscons(rsconscript: "carat.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{gcc -c -o build/e.1/one.o -MMD -MF build/e.1/one.mf -Isrc -Isrc/one -Isrc/two build/e.1/one.c}, %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.o -MMD -MF build/e.1/src/two/two.mf -Isrc -Isrc/one -Isrc/two src/two/two.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.o build/e.1/one.o}, %r{gcc -o program.exe build/e.1/src/two/two.c.o build/e.1/one.o},
]) ])
end end
@ -328,7 +337,7 @@ EOF
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?('foo.yml')).to be_truthy 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 end
it "raises an error when a side-effect file is registered for a build target that is not registered" do it "raises an error when a side-effect file is registered for a build target that is not registered" do
@ -343,9 +352,9 @@ EOF
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(`./simple.exe`).to match /This is a simple C program/ expect(`./simple.exe`).to match /This is a simple C program/
expect(File.exists?('build/e.1/simple.o')).to be_truthy expect(File.exists?('build/e.1/simple.c.o')).to be_truthy
result = run_rscons(op: %w[clean]) result = run_rscons(op: %w[clean])
expect(File.exists?('build/e.1/simple.o')).to be_falsey expect(File.exists?('build/e.1/simple.c.o')).to be_falsey
expect(File.exists?('build/e.1')).to be_falsey expect(File.exists?('build/e.1')).to be_falsey
expect(File.exists?('simple.exe')).to be_falsey expect(File.exists?('simple.exe')).to be_falsey
expect(File.exists?('simple.c')).to be_truthy expect(File.exists?('simple.c')).to be_truthy
@ -356,7 +365,7 @@ EOF
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(`./simple.exe`).to match /This is a simple C program/ expect(`./simple.exe`).to match /This is a simple C program/
expect(File.exists?('build/e.1/simple.o')).to be_truthy expect(File.exists?('build/e.1/simple.c.o')).to be_truthy
File.open('build/e.1/dum', 'w') { |fh| fh.puts "dum" } File.open('build/e.1/dum', 'w') { |fh| fh.puts "dum" }
result = run_rscons(op: %w[clean]) result = run_rscons(op: %w[clean])
expect(File.exists?('build/e.1')).to be_truthy expect(File.exists?('build/e.1')).to be_truthy
@ -373,12 +382,12 @@ EOF
result = run_rscons(rsconscript: "install.rb", op: %W[install]) result = run_rscons(rsconscript: "install.rb", op: %W[install])
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("#{prefix}/bin/program.exe")).to be_truthy expect(File.exists?("#{prefix}/bin/program.exe")).to be_truthy
expect(File.exists?("build/e.1/src/one/one.o")).to be_truthy expect(File.exists?("build/e.1/src/one/one.c.o")).to be_truthy
result = run_rscons(rsconscript: "install.rb", op: %W[clean]) result = run_rscons(rsconscript: "install.rb", op: %W[clean])
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("#{prefix}/bin/program.exe")).to be_truthy expect(File.exists?("#{prefix}/bin/program.exe")).to be_truthy
expect(File.exists?("build/e.1/src/one/one.o")).to be_falsey expect(File.exists?("build/e.1/src/one/one.c.o")).to be_falsey
end end
end end
@ -395,7 +404,7 @@ EOF
result = run_rscons(rsconscript: "install.rb", op: %W[clean]) result = run_rscons(rsconscript: "install.rb", op: %W[clean])
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("#{prefix}/bin/program.exe")).to be_truthy expect(File.exists?("#{prefix}/bin/program.exe")).to be_truthy
expect(File.exists?("build/e.1/src/one/one.o")).to be_falsey expect(File.exists?("build/e.1/src/one/one.c.o")).to be_falsey
result = run_rscons(rsconscript: "install.rb", op: %W[uninstall -v]) result = run_rscons(rsconscript: "install.rb", op: %W[uninstall -v])
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
@ -414,7 +423,7 @@ EOF
%r{Linking program.exe}, %r{Linking program.exe},
]) ])
expect(File.exists?('inc.h')).to be_truthy 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 end
it 'supports custom builders with multiple targets' do it 'supports custom builders with multiple targets' do
@ -430,13 +439,13 @@ EOF
]) ])
expect(File.exists?("inc.c")).to be_truthy expect(File.exists?("inc.c")).to be_truthy
expect(File.exists?("inc.h")).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;"} File.open("inc.c", "w") {|fh| fh.puts "int THE_VALUE = 33;"}
result = run_rscons(rsconscript: "multiple_targets.rb") result = run_rscons(rsconscript: "multiple_targets.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [%r{CHGen inc.c}]) 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 end
it 'raises an error when a custom builder returns an invalid value from #run' do it 'raises an error when a custom builder returns an invalid value from #run' do
@ -477,10 +486,10 @@ EOF
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{gcc -c -o build/e.1/src/program.o -MMD -MF build/e.1/src/program.mf '-DSTRING="Debug Version"' -O2 src/program.c}, %r{gcc -c -o build/e.1/src/program.c.o -MMD -MF build/e.1/src/program.c.o.mf '-DSTRING="Debug Version"' -O2 src/program.c},
%r{gcc -o program-debug.exe build/e.1/src/program.o}, %r{gcc -o program-debug.exe build/e.1/src/program.c.o},
%r{gcc -c -o build/e.2/src/program.o -MMD -MF build/e.2/src/program.mf '-DSTRING="Release Version"' -O2 src/program.c}, %r{gcc -c -o build/e.2/src/program.c.o -MMD -MF build/e.2/src/program.c.o.mf '-DSTRING="Release Version"' -O2 src/program.c},
%r{gcc -o program-release.exe build/e.2/src/program.o}, %r{gcc -o program-release.exe build/e.2/src/program.c.o},
]) ])
end end
@ -489,12 +498,12 @@ EOF
result = run_rscons(rsconscript: "clone_all.rb") result = run_rscons(rsconscript: "clone_all.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{gcc -c -o build/e.1/src/program.o -MMD -MF build/e.1/src/program.mf -DSTRING="Hello" -O2 src/program.c}, %r{gcc -c -o build/e.1/src/program.c.o -MMD -MF build/e.1/src/program.c.o.mf -DSTRING="Hello" -O2 src/program.c},
%r{post build/e.1/src/program.o}, %r{post build/e.1/src/program.c.o},
%r{gcc -o program.exe build/e.1/src/program.o}, %r{gcc -o program.exe build/e.1/src/program.c.o},
%r{post program.exe}, %r{post program.exe},
%r{post build/e.2/src/program.o}, %r{post build/e.2/src/program.c.o},
%r{gcc -o program2.exe build/e.2/src/program.o}, %r{gcc -o program2.exe build/e.2/src/program.c.o},
%r{post program2.exe}, %r{post program2.exe},
]) ])
end end
@ -503,8 +512,8 @@ EOF
test_dir('simple_cc') test_dir('simple_cc')
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?('build/e.1/simple.o')).to be_truthy 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 end
it "links with the C++ linker when object files were built from C++ sources" do it "links with the C++ linker when object files were built from C++ sources" do
@ -512,7 +521,7 @@ EOF
result = run_rscons(rsconscript: "link_objects.rb") result = run_rscons(rsconscript: "link_objects.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("simple.o")).to be_truthy 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 end
it 'allows overriding construction variables for individual builder calls' do it 'allows overriding construction variables for individual builder calls' do
@ -520,12 +529,12 @@ EOF
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{gcc -c -o one.o -MMD -MF one.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.o -MMD -MF build/e.1/two.mf two.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.o}, %r{gcc -o two_sources.exe one.o build/e.1/two.c.o},
]) ])
expect(File.exists?("two_sources.exe")).to be_truthy 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 end
it 'builds a static library archive' do it 'builds a static library archive' do
@ -533,14 +542,14 @@ EOF
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{gcc -c -o build/e.1/one.o -MMD -MF build/e.1/one.mf -Dmake_lib one.c}, %r{gcc -c -o build/e.1/one.c.o -MMD -MF build/e.1/one.c.o.mf -Dmake_lib one.c},
%r{gcc -c -o build/e.1/two.o -MMD -MF build/e.1/two.mf -Dmake_lib two.c}, %r{gcc -c -o build/e.1/two.c.o -MMD -MF build/e.1/two.c.o.mf -Dmake_lib two.c},
%r{ar rcs lib.a build/e.1/one.o build/e.1/two.o}, %r{ar rcs lib.a build/e.1/one.c.o build/e.1/two.c.o},
%r{gcc -c -o build/e.1/three.o -MMD -MF build/e.1/three.mf three.c}, %r{gcc -c -o build/e.1/three.c.o -MMD -MF build/e.1/three.c.o.mf three.c},
%r{gcc -o library.exe lib.a build/e.1/three.o}, %r{gcc -o library.exe lib.a build/e.1/three.c.o},
]) ])
expect(File.exists?("library.exe")).to be_truthy expect(File.exists?("library.exe")).to be_truthy
expect(`ar t lib.a`).to eq "one.o\ntwo.o\n" expect(nr(`ar t lib.a`)).to eq "one.c.o\ntwo.c.o\n"
end end
it 'supports build hooks to override construction variables' do it 'supports build hooks to override construction variables' do
@ -548,11 +557,11 @@ EOF
result = run_rscons(rsconscript: "build_hooks.rb") result = run_rscons(rsconscript: "build_hooks.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{gcc -c -o build/e.1/src/one/one.o -MMD -MF build/e.1/src/one/one.mf -Isrc/one -Isrc/two -O1 src/one/one.c}, %r{gcc -c -o build/e.1/src/one/one.c.o -MMD -MF build/e.1/src/one/one.c.o.mf -Isrc/one -Isrc/two -O1 src/one/one.c},
%r{gcc -c -o build/e.1/src/two/two.o -MMD -MF build/e.1/src/two/two.mf -Isrc/one -Isrc/two -O2 src/two/two.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/one -Isrc/two -O2 src/two/two.c},
%r{gcc -o build_hook.exe build/e.1/src/one/one.o build/e.1/src/two/two.o}, %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 end
it 'supports build hooks to override the entire vars hash' do it 'supports build hooks to override the entire vars hash' do
@ -560,8 +569,8 @@ EOF
result = run_rscons(rsconscript: "build_hooks_override_vars.rb") result = run_rscons(rsconscript: "build_hooks_override_vars.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{gcc -c -o one.o -MMD -MF one.mf -Isrc -Isrc/one -Isrc/two -O1 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 two.mf -Isrc -Isrc/one -Isrc/two -O2 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?('one.o')).to be_truthy
expect(File.exists?('two.o')).to be_truthy expect(File.exists?('two.o')).to be_truthy
@ -577,8 +586,8 @@ EOF
%r{Compiling simple.c}, %r{Compiling simple.c},
%r{Linking simple.exe}, %r{Linking simple.exe},
]) ])
expect(File.exists?('build/e.1/simple.o')).to be_truthy 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")} File.open("program.ld", "w") {|fh| fh.puts("2")}
result = run_rscons(rsconscript: "user_dependencies.rb") result = run_rscons(rsconscript: "user_dependencies.rb")
@ -595,25 +604,48 @@ EOF
expect(result.stdout).to eq "" expect(result.stdout).to eq ""
end end
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 ""
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 it "supports building D sources with gdc" do
test_dir("d") test_dir("d")
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
slines = lines(result.stdout) slines = lines(result.stdout)
verify_lines(slines, [%r{gdc -c -o build/e.1/main.o -MMD -MF build/e.1/main.mf main.d}]) 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.o -MMD -MF build/e.1/mod.mf mod.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.o build/e.1/mod.o}]) 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!" expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
end end
end
it "supports building D sources with ldc2" do it "supports building D sources with ldc2" do
test_dir("d") test_dir("d")
result = run_rscons(rsconscript: "build-ldc2.rb") result = run_rscons(rsconscript: "build-ldc2.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
slines = lines(result.stdout) slines = lines(result.stdout)
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.o(bj)? -deps=build/e.1/main.mf main.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.o(bj)? -deps=build/e.1/mod.mf mod.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.o(bj)? build/e.1/mod.o(bj)?}]) 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!" expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
end end
@ -622,9 +654,9 @@ EOF
result = run_rscons(rsconscript: "build-ldc2.rb") result = run_rscons(rsconscript: "build-ldc2.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
slines = lines(result.stdout) slines = lines(result.stdout)
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.o(bj)? -deps=build/e.1/main.mf main.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.o(bj)? -deps=build/e.1/mod.mf mod.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.o(bj)? build/e.1/mod.o(bj)?}]) 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!" expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
contents = File.read("mod.d", mode: "rb").sub("42", "33") contents = File.read("mod.d", mode: "rb").sub("42", "33")
File.open("mod.d", "wb") do |fh| File.open("mod.d", "wb") do |fh|
@ -633,13 +665,13 @@ EOF
result = run_rscons(rsconscript: "build-ldc2.rb") result = run_rscons(rsconscript: "build-ldc2.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
slines = lines(result.stdout) slines = lines(result.stdout)
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.o(bj)? -deps=build/e.1/main.mf main.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.o(bj)? -deps=build/e.1/mod.mf mod.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.o(bj)? build/e.1/mod.o(bj)?}]) 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!" expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 33!"
end 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 it "links with the D linker when object files were built from D sources" do
test_dir("d") test_dir("d")
result = run_rscons(rsconscript: "link_objects.rb") result = run_rscons(rsconscript: "link_objects.rb")
@ -654,18 +686,18 @@ EOF
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
slines = lines(result.stdout) slines = lines(result.stdout)
verify_lines(slines, [%r{gdc -c -o build/e.1/main.o -MMD -MF build/e.1/main.mf main.d}]) 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.o -MMD -MF build/e.1/mod.mf mod.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.o build/e.1/mod.o}]) 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!" expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
fcontents = File.read("mod.d", mode: "rb").sub("42", "33") fcontents = File.read("mod.d", mode: "rb").sub("42", "33")
File.open("mod.d", "wb") {|fh| fh.write(fcontents)} File.open("mod.d", "wb") {|fh| fh.write(fcontents)}
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
slines = lines(result.stdout) slines = lines(result.stdout)
verify_lines(slines, [%r{gdc -c -o build/e.1/main.o -MMD -MF build/e.1/main.mf main.d}]) 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.o -MMD -MF build/e.1/mod.mf mod.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.o build/e.1/mod.o}]) 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 33!" expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 33!"
end end
@ -675,7 +707,7 @@ EOF
result = run_rscons(rsconscript: "shared_library_d.rb") result = run_rscons(rsconscript: "shared_library_d.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
slines = lines(result.stdout) slines = lines(result.stdout)
if RUBY_PLATFORM =~ /mingw/ if RUBY_PLATFORM =~ /mingw|msys/
verify_lines(slines, [%r{Linking mine.dll}]) verify_lines(slines, [%r{Linking mine.dll}])
else else
verify_lines(slines, [%r{Linking libmine.so}]) verify_lines(slines, [%r{Linking libmine.so}])
@ -701,7 +733,7 @@ EOF
result = run_rscons(rsconscript: "preprocess.rb") result = run_rscons(rsconscript: "preprocess.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.read("simplepp.c")).to match /# \d+ "simple.c"/ 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 end
it "supports preprocessing C++ sources" do it "supports preprocessing C++ sources" do
@ -709,7 +741,7 @@ EOF
result = run_rscons(rsconscript: "preprocess.rb") result = run_rscons(rsconscript: "preprocess.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.read("simplepp.cc")).to match /# \d+ "simple.cc"/ 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 end
it "supports invoking builders with no sources" do it "supports invoking builders with no sources" do
@ -727,23 +759,23 @@ EOF
%r{Linking program.exe}, %r{Linking program.exe},
]) ])
expect(File.exists?('inc.h')).to be_truthy 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 end
it "supports lambdas as construction variable values" do it "supports lambdas as construction variable values" do
test_dir "custom_builder" test_dir "custom_builder"
result = run_rscons(rsconscript: "cvar_lambda.rb") result = run_rscons(rsconscript: "cvar_lambda.rb")
expect(result.stderr).to eq "" 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 end
it "supports registering build targets from within a build hook" do it "supports registering build targets from within a build hook" do
test_dir("simple") test_dir("simple")
result = run_rscons(rsconscript: "register_target_in_build_hook.rb") result = run_rscons(rsconscript: "register_target_in_build_hook.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("build/e.1/simple.o")).to be_truthy expect(File.exists?("build/e.1/simple.c.o")).to be_truthy
expect(File.exists?("build/e.1/simple.o.txt")).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 end
it "supports multiple values for CXXSUFFIX" do it "supports multiple values for CXXSUFFIX" do
@ -751,9 +783,9 @@ EOF
File.open("other.cccc", "w") {|fh| fh.puts} File.open("other.cccc", "w") {|fh| fh.puts}
result = run_rscons(rsconscript: "cxxsuffix.rb") result = run_rscons(rsconscript: "cxxsuffix.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("build/e.1/simple.o")).to be_truthy expect(File.exists?("build/e.1/simple.cc.o")).to be_truthy
expect(File.exists?("build/e.1/other.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 end
it "supports multiple values for CSUFFIX" do it "supports multiple values for CSUFFIX" do
@ -761,9 +793,9 @@ EOF
FileUtils.mv("src/one/one.c", "src/one/one.yargh") FileUtils.mv("src/one/one.c", "src/one/one.yargh")
result = run_rscons(rsconscript: "csuffix.rb") result = run_rscons(rsconscript: "csuffix.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("build/e.1/src/one/one.o")).to be_truthy expect(File.exists?("build/e.1/src/one/one.yargh.o")).to be_truthy
expect(File.exists?("build/e.1/src/two/two.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 end
it "supports multiple values for OBJSUFFIX" do it "supports multiple values for OBJSUFFIX" do
@ -773,7 +805,7 @@ EOF
expect(File.exists?("two_sources.exe")).to be_truthy expect(File.exists?("two_sources.exe")).to be_truthy
expect(File.exists?("one.oooo")).to be_truthy expect(File.exists?("one.oooo")).to be_truthy
expect(File.exists?("two.ooo")).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 end
it "supports multiple values for LIBSUFFIX" do it "supports multiple values for LIBSUFFIX" do
@ -781,7 +813,7 @@ EOF
result = run_rscons(rsconscript: "libsuffix.rb") result = run_rscons(rsconscript: "libsuffix.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("two_sources.exe")).to be_truthy 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 end
it "supports multiple values for ASSUFFIX" do it "supports multiple values for ASSUFFIX" do
@ -796,7 +828,7 @@ EOF
%r{Linking two_sources.exe}, %r{Linking two_sources.exe},
]) ])
expect(File.exists?("two_sources.exe")).to be_truthy 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 end
it "supports dumping an Environment's construction variables" do it "supports dumping an Environment's construction variables" do
@ -834,8 +866,8 @@ EOF
test_dir("simple") test_dir("simple")
result = run_rscons(rsconscript: "cvar_array.rb") result = run_rscons(rsconscript: "cvar_array.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("build/e.1/simple.o")).to be_truthy 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 end
it "supports registering multiple build targets with the same target path" do it "supports registering multiple build targets with the same target path" do
@ -916,7 +948,7 @@ EOF
result = run_rscons(rsconscript: "absolute_source_path.rb") result = run_rscons(rsconscript: "absolute_source_path.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
slines = lines(result.stdout) slines = lines(result.stdout)
verify_lines(slines, [%r{build/e.1/.*/abs\.o$}]) verify_lines(slines, [%r{build/e.1/.*/abs\.c.o$}])
verify_lines(slines, [%r{\babs.exe\b}]) verify_lines(slines, [%r{\babs.exe\b}])
end end
@ -926,7 +958,7 @@ EOF
result = run_rscons result = run_rscons
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
slines = lines(result.stdout) slines = lines(result.stdout)
if RUBY_PLATFORM =~ /mingw/ if RUBY_PLATFORM =~ /mingw|msys/
verify_lines(slines, [%r{Linking mine.dll}]) verify_lines(slines, [%r{Linking mine.dll}])
expect(File.exists?("mine.dll")).to be_truthy expect(File.exists?("mine.dll")).to be_truthy
else else
@ -938,7 +970,7 @@ EOF
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(result.stdout).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(`#{ld_library_path_prefix}./test-shared.exe`).to match /Hi from one/
expect(`./test-static.exe`).to match /Hi from one/ expect(`./test-static.exe`).to match /Hi from one/
end end
@ -957,7 +989,7 @@ EOF
result = run_rscons(rsconscript: "shared_library_cxx.rb") result = run_rscons(rsconscript: "shared_library_cxx.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
slines = lines(result.stdout) slines = lines(result.stdout)
if RUBY_PLATFORM =~ /mingw/ if RUBY_PLATFORM =~ /mingw|msys/
verify_lines(slines, [%r{Linking mine.dll}]) verify_lines(slines, [%r{Linking mine.dll}])
else else
verify_lines(slines, [%r{Linking libmine.so}]) verify_lines(slines, [%r{Linking libmine.so}])
@ -967,7 +999,7 @@ EOF
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(result.stdout).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(`#{ld_library_path_prefix}./test-shared.exe`).to match /Hi from one/
expect(`./test-static.exe`).to match /Hi from one/ expect(`./test-static.exe`).to match /Hi from one/
end end
@ -1034,7 +1066,7 @@ EOF
result = run_rscons result = run_rscons
expect(result.stderr).to match /Failed to build/ 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 expect(result.status).to_not eq 0
result = run_rscons(rscons_args: %w[-F]) result = run_rscons(rscons_args: %w[-F])
@ -1098,7 +1130,7 @@ EOF
result = run_rscons(rsconscript: "command_builder.rb") result = run_rscons(rsconscript: "command_builder.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [%r{BuildIt simple.exe}]) 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") result = run_rscons(rsconscript: "command_builder.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
@ -1431,6 +1463,37 @@ EOF
expect(result.stdout).to eq "" expect(result.stdout).to eq ""
end end
it "supports building multiple object files from sources with the same pathname and basename" do
test_dir "multiple_basename"
result = run_rscons
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(File.exist?("foo.exe")).to be_truthy
result = run_rscons
expect(result.stderr).to eq ""
expect(result.stdout).to eq ""
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 context "debugging" do
it "prints a message when the target does not exist" do it "prints a message when the target does not exist" do
test_dir("simple") test_dir("simple")
@ -1524,7 +1587,7 @@ EOF
result = run_rscons(rsconscript: "override_depfilesuffix.rb") result = run_rscons(rsconscript: "override_depfilesuffix.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [ verify_lines(lines(result.stdout), [
%r{gcc -c -o simple.o -MMD -MF simple.deppy simple.c}, %r{gcc -c -o simple.o -MMD -MF build/e.1/simple.o.deppy simple.c},
]) ])
end end
@ -1548,7 +1611,7 @@ EOF
test_dir("library") test_dir("library")
result = run_rscons(rsconscript: "override_arcmd.rb") result = run_rscons(rsconscript: "override_arcmd.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [%r{ar rcf lib.a build/e.1/one.o build/e.1/three.o build/e.1/two.o}]) verify_lines(lines(result.stdout), [%r{ar rcf lib.a build/e.1/one.c.o build/e.1/three.c.o build/e.1/two.c.o}])
end end
it "allows passing object files as sources" do it "allows passing object files as sources" do
@ -1567,7 +1630,7 @@ EOF
result = run_rscons(rsconscript: "shared_library_set_shld.rb") result = run_rscons(rsconscript: "shared_library_set_shld.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
slines = lines(result.stdout) slines = lines(result.stdout)
if RUBY_PLATFORM =~ /mingw/ if RUBY_PLATFORM =~ /mingw|msys/
verify_lines(slines, [%r{Linking mine.dll}]) verify_lines(slines, [%r{Linking mine.dll}])
else else
verify_lines(slines, [%r{Linking libmine.so}]) verify_lines(slines, [%r{Linking libmine.so}])
@ -1578,7 +1641,7 @@ EOF
test_dir "shared_library" test_dir "shared_library"
result = run_rscons(rsconscript: "shared_library_from_object.rb") result = run_rscons(rsconscript: "shared_library_from_object.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("one.o")) expect(File.exists?("one.c.o"))
end end
end end
@ -1711,7 +1774,7 @@ EOF
create_exe "gcc", "exit 1" create_exe "gcc", "exit 1"
create_exe "clang", "exit 1" create_exe "clang", "exit 1"
result = run_rscons(rsconscript: rsconscript, op: "configure") result = run_rscons(rsconscript: rsconscript, op: "configure")
expect(result.stderr).to eq "" expect(result.stderr).to match /Configuration failed/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
expect(result.stdout).to match /Checking for C compiler\.\.\. not found/ expect(result.stdout).to match /Checking for C compiler\.\.\. not found/
end end
@ -1754,7 +1817,7 @@ EOF
create_exe "g++", "exit 1" create_exe "g++", "exit 1"
create_exe "clang++", "exit 1" create_exe "clang++", "exit 1"
result = run_rscons(rsconscript: rsconscript, op: "configure") result = run_rscons(rsconscript: rsconscript, op: "configure")
expect(result.stderr).to eq "" expect(result.stderr).to match /Configuration failed/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
expect(result.stdout).to match /Checking for C\+\+ compiler\.\.\. not found/ expect(result.stdout).to match /Checking for C\+\+ compiler\.\.\. not found/
end end
@ -1775,6 +1838,7 @@ EOF
{"check_d_compiler.rb" => "when no arguments are given", {"check_d_compiler.rb" => "when no arguments are given",
"check_d_compiler_find_first.rb" => "when arguments are given"}.each_pair do |rsconscript, desc| "check_d_compiler_find_first.rb" => "when arguments are given"}.each_pair do |rsconscript, desc|
context desc do context desc do
unless RUBY_PLATFORM =~ /mingw|msys/
it "finds the first listed D compiler" do it "finds the first listed D compiler" do
test_dir "configure" test_dir "configure"
result = run_rscons(rsconscript: rsconscript, op: "configure") result = run_rscons(rsconscript: rsconscript, op: "configure")
@ -1782,6 +1846,7 @@ EOF
expect(result.status).to eq 0 expect(result.status).to eq 0
expect(result.stdout).to match /Checking for D compiler\.\.\. gdc/ expect(result.stdout).to match /Checking for D compiler\.\.\. gdc/
end end
end
it "finds the second listed D compiler" do it "finds the second listed D compiler" do
test_dir "configure" test_dir "configure"
@ -1797,13 +1862,14 @@ EOF
create_exe "gdc", "exit 1" create_exe "gdc", "exit 1"
create_exe "ldc2", "exit 1" create_exe "ldc2", "exit 1"
result = run_rscons(rsconscript: rsconscript, op: "configure") result = run_rscons(rsconscript: rsconscript, op: "configure")
expect(result.stderr).to eq "" expect(result.stderr).to match /Configuration failed/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
expect(result.stdout).to match /Checking for D compiler\.\.\. not found/ expect(result.stdout).to match /Checking for D compiler\.\.\. not found/
end end
end end
end end
unless RUBY_PLATFORM =~ /mingw|msys/
it "successfully tests a compiler with an unknown name that uses gdc-compatible options" do it "successfully tests a compiler with an unknown name that uses gdc-compatible options" do
test_dir "configure" test_dir "configure"
create_exe "mycompiler", %[exec gdc "$@"] create_exe "mycompiler", %[exec gdc "$@"]
@ -1812,6 +1878,7 @@ EOF
expect(result.status).to eq 0 expect(result.status).to eq 0
expect(result.stdout).to match /Checking for D compiler\.\.\. mycompiler/ expect(result.stdout).to match /Checking for D compiler\.\.\. mycompiler/
end end
end
it "successfully tests a compiler with an unknown name that uses ldc2-compatible options" do it "successfully tests a compiler with an unknown name that uses ldc2-compatible options" do
test_dir "configure" test_dir "configure"
@ -1835,7 +1902,7 @@ EOF
it "fails when the requested header is not found" do it "fails when the requested header is not found" do
test_dir "configure" test_dir "configure"
result = run_rscons(rsconscript: "check_c_header_failure.rb", op: "configure") result = run_rscons(rsconscript: "check_c_header_failure.rb", op: "configure")
expect(result.stderr).to eq "" expect(result.stderr).to match /Configuration failed/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
expect(result.stdout).to match /Checking for C header 'not___found\.h'... not found/ expect(result.stdout).to match /Checking for C header 'not___found\.h'... not found/
end end
@ -1900,7 +1967,7 @@ EOF
it "fails when the requested header is not found" do it "fails when the requested header is not found" do
test_dir "configure" test_dir "configure"
result = run_rscons(rsconscript: "check_cxx_header_failure.rb", op: "configure") result = run_rscons(rsconscript: "check_cxx_header_failure.rb", op: "configure")
expect(result.stderr).to eq "" expect(result.stderr).to match /Configuration failed/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
expect(result.stdout).to match /Checking for C\+\+ header 'not___found\.h'... not found/ expect(result.stdout).to match /Checking for C\+\+ header 'not___found\.h'... not found/
end end
@ -1941,7 +2008,7 @@ EOF
it "fails when the requested import is not found" do it "fails when the requested import is not found" do
test_dir "configure" test_dir "configure"
result = run_rscons(rsconscript: "check_d_import_failure.rb", op: "configure") result = run_rscons(rsconscript: "check_d_import_failure.rb", op: "configure")
expect(result.stderr).to eq "" expect(result.stderr).to match /Configuration failed/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
expect(result.stdout).to match /Checking for D import 'not\.found'... not found/ expect(result.stdout).to match /Checking for D import 'not\.found'... not found/
end end
@ -1982,7 +2049,7 @@ EOF
it "fails when the requested library is not found" do it "fails when the requested library is not found" do
test_dir "configure" test_dir "configure"
result = run_rscons(rsconscript: "check_lib_failure.rb", op: "configure") result = run_rscons(rsconscript: "check_lib_failure.rb", op: "configure")
expect(result.stderr).to eq "" expect(result.stderr).to match /Configuration failed/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
expect(result.stdout).to match /Checking for library 'mfoofoo'... not found/ expect(result.stdout).to match /Checking for library 'mfoofoo'... not found/
end end
@ -2063,7 +2130,7 @@ EOF
it "fails when the requested program is not found" do it "fails when the requested program is not found" do
test_dir "configure" test_dir "configure"
result = run_rscons(rsconscript: "check_program_failure.rb", op: "configure") result = run_rscons(rsconscript: "check_program_failure.rb", op: "configure")
expect(result.stderr).to eq "" expect(result.stderr).to match /Configuration failed/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
expect(result.stdout).to match /Checking for program 'program-that-is-not-found'... not found/ expect(result.stdout).to match /Checking for program 'program-that-is-not-found'... not found/
end end
@ -2095,7 +2162,7 @@ EOF
it "fails when the configure program given does not exist" do it "fails when the configure program given does not exist" do
test_dir "configure" test_dir "configure"
result = run_rscons(rsconscript: "check_cfg.rb", op: "configure") result = run_rscons(rsconscript: "check_cfg.rb", op: "configure")
expect(result.stderr).to eq "" expect(result.stderr).to match /Configuration failed/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
expect(result.stdout).to match /Checking 'my-config'\.\.\. not found/ expect(result.stdout).to match /Checking 'my-config'\.\.\. not found/
end end
@ -2140,7 +2207,7 @@ EOF
test_dir "configure" test_dir "configure"
create_exe "grep", "exit 4" create_exe "grep", "exit 4"
result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure") result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure")
expect(result.stderr).to eq "" expect(result.stderr).to match /Configuration failed/
expect(result.stdout).to match /Checking 'grep' version\.\.\. error executing grep/ expect(result.stdout).to match /Checking 'grep' version\.\.\. error executing grep/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
end end
@ -2151,7 +2218,7 @@ EOF
test_dir "configure" test_dir "configure"
create_exe "grep", "echo 'grep (GNU grep) 1.1'" create_exe "grep", "echo 'grep (GNU grep) 1.1'"
result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure") result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure")
expect(result.stderr).to eq "" expect(result.stderr).to match /Configuration failed/
expect(result.stdout).to match /Checking 'grep' version\.\.\. too old!/ expect(result.stdout).to match /Checking 'grep' version\.\.\. too old!/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
end end
@ -2200,6 +2267,16 @@ EOF
end end
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 it "does everything" do
test_dir "configure" test_dir "configure"
create_exe "pkg-config", "echo '-DMYPACKAGE'" create_exe "pkg-config", "echo '-DMYPACKAGE'"
@ -2238,11 +2315,13 @@ EOF
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
end end
it "exits with an error if configuration fails during autoconf" do it "exits with an error code and message if configuration fails during autoconf" do
test_dir "configure" test_dir "configure"
result = run_rscons(rsconscript: "autoconf_fail.rb") result = run_rscons(rsconscript: "autoconf_fail.rb")
expect(result.stdout).to match /Checking for C compiler\.\.\. not found/ expect(result.stdout).to match /Checking for C compiler\.\.\. not found/
expect(result.status).to_not eq 0 expect(result.status).to_not eq 0
expect(result.stderr).to_not match /from\s/
expect(lines(result.stderr).last).to eq "Configuration failed"
end end
it "exits with an error if configuration has not been performed before attempting to create an environment" do it "exits with an error if configuration has not been performed before attempting to create an environment" do
@ -2351,7 +2430,7 @@ EOF
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(result.stdout).to match %r{Compiling/Linking} expect(result.stdout).to match %r{Compiling/Linking}
expect(File.exists?("test.exe")).to be_truthy 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/ expect(`#{ld_library_path_prefix}./test.exe`).to match /three/
result = run_rscons(rsconscript: "c_shared_library.rb") result = run_rscons(rsconscript: "c_shared_library.rb")
@ -2446,13 +2525,13 @@ EOF
result = run_rscons(rsconscript: "install.rb", op: %W[install]) result = run_rscons(rsconscript: "install.rb", op: %W[install])
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(File.exists?("#{prefix}/bin/program.exe")).to be_truthy expect(File.exists?("#{prefix}/bin/program.exe")).to be_truthy
expect(File.exists?("build/e.1/src/one/one.o")).to be_truthy expect(File.exists?("build/e.1/src/one/one.c.o")).to be_truthy
result = run_rscons(rsconscript: "install.rb", op: %W[uninstall]) result = run_rscons(rsconscript: "install.rb", op: %W[uninstall])
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(result.stdout).to_not match /Removing/ expect(result.stdout).to_not match /Removing/
expect(File.exists?("#{prefix}/bin/program.exe")).to be_falsey expect(File.exists?("#{prefix}/bin/program.exe")).to be_falsey
expect(File.exists?("build/e.1/src/one/one.o")).to be_truthy expect(File.exists?("build/e.1/src/one/one.c.o")).to be_truthy
expect(Dir.entries(prefix)).to match_array %w[. ..] expect(Dir.entries(prefix)).to match_array %w[. ..]
end end
end end
@ -2546,4 +2625,123 @@ EOF
end end
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
end
end end

View File

@ -68,7 +68,7 @@ EOF
expect(RbConfig::CONFIG).to receive(:[]).with("host_os").and_return("mingw") expect(RbConfig::CONFIG).to receive(:[]).with("host_os").and_return("mingw")
end end
it "returns the number of logical processors that wmic reports" do 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) expect(Util.determine_n_threads).to eq(7)
end end
end end

View File

@ -1,15 +1,5 @@
describe Rscons do describe Rscons do
describe ".set_suffix" do
it "changes the suffix to the new one" do
expect(Rscons.set_suffix("foo.c", ".h")).to eq("foo.h")
end
it "appends a suffix if the given file name does not have one" do
expect(Rscons.set_suffix("bazz", ".d")).to eq("bazz.d")
end
end
describe ".get_system_shell" do describe ".get_system_shell" do
before(:each) do before(:each) do
Rscons.instance_variable_set(:@shell, nil) Rscons.instance_variable_set(:@shell, nil)

View File

@ -1,3 +1,6 @@
if ENV["dist_specs"]
require_relative "../test/rscons"
else
require "simplecov" require "simplecov"
SimpleCov.start do SimpleCov.start do
@ -18,8 +21,5 @@ SimpleCov.start do
merge_timeout 3600 merge_timeout 3600
end end
if ENV["dist_specs"]
require_relative "../test/rscons"
else
require "rscons" require "rscons"
end end