Compare commits

...
62 Commits
Author SHA1 Message Date
josh 353f4f45bc v2.2.0 2020-10-13 18:59:18 -04:00
josh 5bac91f92f add failure messages for failed configuration checks - close #119 2020-10-12 21:01:04 -04:00
josh bbe9563ceb compiler checks should support cross-compilers and freestanding compilers - close #118 2020-10-08 19:23:17 -04:00
josh 5ec74604c6 improve support for MSYS2 - close #120 2020-10-08 19:04:50 -04:00
josh 8e06efe61e v2.1.0 2020-04-16 16:52:58 -04:00
josh 0756874ddb update CHANGELOG for v2.1.0 2020-04-16 16:51:16 -04:00
josh dded5e2648 ruby 2.7 compatibility - #117 2020-04-16 15:48:45 -04:00
josh fbe60f6ba2 stick with simplecov 0.15.x for now 2020-04-16 15:02:01 -04:00
josh 8dd9799666 Revert "update simplecov"
This reverts commit 01ca86de8b.
2020-04-16 15:02:01 -04:00
josh f2c755c8bb update bundle 2020-04-16 13:07:20 -04:00
josh 01ca86de8b update simplecov 2020-02-29 14:27:21 -05:00
josh 943fabd0d1 update some gems 2020-02-29 10:47:46 -05:00
josh d08e2f6b5c v2.0.2 2019-12-16 21:36:38 -05:00
josh 90365dd197 update CHANGELOG for v2.0.2 2019-12-16 21:36:22 -05:00
josh 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
josh db49d86866 show a configuration failed message on stderr on configuration failure 2019-10-14 22:57:49 -04:00
josh e667455c32 user guide: fix configure script example 2019-10-14 22:49:34 -04:00
josh e62332aba6 avoid backtrace when configuring fails during autoconf 2019-10-14 22:23:18 -04:00
josh 397639849d call print_run_message() in json_to_yaml build test 2019-10-12 17:59:08 -04:00
josh 96d5caf2e5 user guide: fix print_run_message() examples 2019-10-12 17:57:58 -04:00
josh f4b10a30af update CHANGELOG for v2.0.1 2019-10-02 21:10:29 -04:00
josh cf52c3fc6f v2.0.1 2019-10-02 20:59:58 -04:00
josh 5378afb6f8 Install builder cannot replace a currently executing binary on Linux - close #112 2019-09-28 23:18:31 -04:00
josh 48d9273cc6 v2.0.0 2019-09-11 23:32:27 -04:00
josh efea4f1181 user guide: Installation: mention marking rscons executable 2019-09-11 23:29:25 -04:00
josh b4fa6d965c CHANGELOG - add logo 2019-09-11 23:25:19 -04:00
josh ac56ad34f6 add rscons logo to README 2019-09-11 23:24:20 -04:00
josh 2fd533a32a clobber large_project directory 2019-09-11 23:20:22 -04:00
josh c938ed71d1 add "all" rake task 2019-09-11 23:19:46 -04:00
josh ec60201a62 user guide: add rscons logo 2019-09-11 23:18:38 -04:00
josh 0cafb8c5b3 user guide: stylize as "Rscons" 2019-09-11 23:15:16 -04:00
josh 004810386b clobber should remove "gen" directory 2019-09-11 23:15:02 -04:00
josh a15ee9daaf add logo images 2019-09-11 23:02:17 -04:00
josh cd33405697 user guide: add FreeBSD compatibility 2019-09-01 21:52:32 -04:00
josh efa77066bf allow ConfigureOp#log_and_test_command to be passed standard input data - close #111 2019-09-01 21:50:57 -04:00
josh a1bb9d81fd check_program: do not fail on Windows for non-existent PATH entries 2019-08-27 19:47:21 -04:00
josh 0344f02cb1 build tests: use -m instead of -lc for mingw 2019-08-27 19:32:36 -04:00
josh d52bdaf046 build tests: do ldc2 tests even if omit_gdc_tests set 2019-08-27 18:51:56 -04:00
josh ad7ccb771d update build test for gdc fix 2019-08-27 18:50:01 -04:00
josh 044ee343ca Use OBJSUFFIX of .obj for ldc2 on Windows. 2019-08-26 19:10:19 -04:00
josh eb54a2b64a fix specs create_exe() on Windows 2019-08-26 18:51:03 -04:00
josh 2dd1ce9f1d Use base64 encoding for compressed script to avoid encoding issues. 2019-08-26 18:33:42 -04:00
josh 1e157c79ec fix dependency parsing with ldc2 - close #110 2019-08-19 22:56:54 -04:00
josh 2385874f4a v2.0.0beta2 2019-08-17 17:07:59 -04:00
josh c5c42e6f4a user guide: document a couple missing configure flags and link to YARD some places 2019-08-17 17:00:36 -04:00
josh 9a627b2150 add YARD comments to get to 100% YARD coverage 2019-08-17 16:42:14 -04:00
josh 937c964c3e document :use better 2019-08-17 16:35:06 -04:00
josh a2a1d08014 user guide: document Custom Configuration Checks 2019-08-17 16:35:06 -04:00
josh 0ccf43cc96 custom config checks - close #108 2019-08-17 16:06:37 -04:00
josh d202e15a57 user guide: document direct mode for Program/SharedLibrary builders 2019-07-29 21:57:38 -04:00
josh 94f808b1fc user guide: add Barriers section 2019-07-29 21:32:04 -04:00
josh 51a9dd365f add :check_libpath option to check_lib configuration method - #107 2019-07-29 21:00:52 -04:00
josh 6e590b62a6 add :check_cpppath option to check_c_header and check_cxx_header
configuration methods
add :check_d_import_path option to check_d_import configuration method - #107
2019-07-09 23:53:17 -04:00
josh 3aac190ad6 configure checks: compile instead of link when looking for C/C++ header files and D imports 2019-07-09 23:41:51 -04:00
josh d6b9487e65 user guide: document -F command-line flag 2019-07-09 23:06:45 -04:00
josh 66d2700faa clarify -F command-line option usage more 2019-07-09 23:06:45 -04:00
josh ba98af1424 default configure checks :fail option to false if :set_define is specified 2019-07-09 22:00:50 -04:00
josh cf4f80c9ae user guide: document first compiler found is used 2019-07-09 21:47:45 -04:00
josh d74785d579 Add v2.0.0 changes to CHANGELOG.md 2019-07-07 22:15:44 -04:00
josh 4fd822fb95 user guide: add warning when viewing markdown source file directly 2019-07-07 21:38:17 -04:00
josh b39fd80d8c user guide: add ./configure && make Appendix section 2019-07-07 21:29:30 -04:00
josh ef00c9f377 add link to github.io site from README.md 2019-07-02 22:39:33 -04:00
54 changed files with 1184 additions and 396 deletions

No files matched your search

+54
View File
@@ -1,3 +1,57 @@
## 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
- convert rscons from a Ruby gem to a standalone script
- compress rscons distributable script
- add configure operation to detect compilers, check for headers/libraries, etc... (invoked automatically if needed)
- Environments store builder classes instead of instances of builder classes
- use a separate Builder instance for each build operation
- load Rsconscript from Rsconscript/Rsconscript.rb instead of Rsconsfile
- drop support for builder run methods using the old 5 parameter signature
- remove Environment#build_dir
- set Environment build root in configure step
- remove Builder#finalize (now #run called repeatedly until builder completes)
- remove Builder#setup
- remove Builder#features and Builder#produces?
- add functionality to allow builders to wait on Ruby threads or other builders
- add install/uninstall/distclean command-line operations
- preserve makefile dependency files under build directory
- remove a few deprecated methods
- pass a Builder instance to build hooks instead of a build operation Hash
- support a basic markup syntax in builder run messages to colorize target/source files
- hide (but store) failed compilation command by default so the user doesn't have to scroll back as much to see compiler output
- refactor to remove some redundancy among built-in builders
- track object file source language (correctly determine linker when only passed object files previously built by particular toolchains)
- add barriers
- add InstallDirectory builder
- change Install builder to copy files on 'install' operation
- add "prefix" construction variable based on configured installation prefix
- allow passing builder objects as sources to build targets
- differentiate 'build' targets from 'install' targets in cache contents
- add verbose mode
- show build progress as a percentage in builder output messages
- various performance improvements
- wrote a new user guide
- added new website ([https://holtrop.github.io/rscons/](https://holtrop.github.io/rscons/))
- added new logo
## v1.17.0 ## v1.17.0
### New Features ### New Features
+10 -6
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"
gem "yard" if RbConfig::CONFIG["host"]["msys"]
gem "rdoc" gem "json", "2.1.0"
gem "redcarpet" else
gem "syntax" gem "json"
gem "yard"
gem "rdoc"
gem "redcarpet"
gem "syntax"
end
+20 -20
View File
@@ -2,31 +2,31 @@ GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
diff-lcs (1.3) diff-lcs (1.3)
docile (1.3.2) docile (1.1.5)
json (2.2.0) json (2.3.0)
rake (12.3.2) rake (13.0.1)
rdoc (6.1.1) rdoc (6.2.1)
redcarpet (3.4.0) redcarpet (3.5.0)
rspec (3.8.0) rspec (3.9.0)
rspec-core (~> 3.8.0) rspec-core (~> 3.9.0)
rspec-expectations (~> 3.8.0) rspec-expectations (~> 3.9.0)
rspec-mocks (~> 3.8.0) rspec-mocks (~> 3.9.0)
rspec-core (3.8.2) rspec-core (3.9.1)
rspec-support (~> 3.8.0) rspec-support (~> 3.9.1)
rspec-expectations (3.8.4) rspec-expectations (3.9.1)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0) rspec-support (~> 3.9.0)
rspec-mocks (3.8.1) rspec-mocks (3.9.1)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0) rspec-support (~> 3.9.0)
rspec-support (3.8.2) rspec-support (3.9.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.24)
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
+4
View File
@@ -1,6 +1,10 @@
# Rscons # Rscons
![rscons logo](img/rscons_logo_1000.png)
Rscons (https://github.com/holtrop/rscons) is an open-source build system Rscons (https://github.com/holtrop/rscons) is an open-source build system
for developers. for developers.
Rscons is written in Ruby, and is inspired by [SCons](https://scons.org/) and [waf](https://waf.io/). Rscons is written in Ruby, and is inspired by [SCons](https://scons.org/) and [waf](https://waf.io/).
See [https://holtrop.github.io/rscons/index.html](https://holtrop.github.io/rscons/index.html) for User Guide and Installation instructions.
+17 -8
View File
@@ -6,12 +6,11 @@ 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"
CLEAN.include %w[build_test_run .yardoc yard coverage test] CLEAN.include %w[build_test_run .yardoc yard coverage test]
CLOBBER.include %w[dist pkg] CLOBBER.include %w[dist gen large_project pkg]
task :build_dist do task :build_dist do
sh "ruby rb/build_dist.rb" sh "ruby rb/build_dist.rb"
@@ -37,11 +36,6 @@ task :dspec, [:example_string] => :build_dist do |task, args|
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,8 +82,23 @@ EOF
FileUtils.cp("dist/rscons", "large_project") FileUtils.cp("dist/rscons", "large_project")
end end
task :user_guide do unless RbConfig::CONFIG["host"]["msys"]
require "yard"
YARD::Rake::YardocTask.new do |yard|
yard.files = ['lib/**/*.rb']
yard.options = ["-ogen/yard"]
end
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 => [
:build_dist,
:spec,
:yard,
:user_guide,
]
@@ -0,0 +1,10 @@
configure do
check_c_header "string.h", check_cpppath: ["./usr1"]
check_c_header "frobulous.h", check_cpppath: ["./usr2"]
end
build do
Environment.new do |env|
env.Object("test.o", "test.c")
end
end
@@ -1,5 +1,5 @@
configure do configure do
check_c_header "not___found.h", fail: false, set_define: "HAVE_NOT___FOUND_H" check_c_header "not___found.h", set_define: "HAVE_NOT___FOUND_H"
end end
build do build do
@@ -0,0 +1,10 @@
configure do
check_cxx_header "string.h", check_cpppath: ["./usr1"]
check_cxx_header "frobulous.h", check_cpppath: ["./usr2"]
end
build do
Environment.new do |env|
env.Object("test.o", "test.cc")
end
end
@@ -0,0 +1,11 @@
configure do
check_d_compiler
check_d_import "std.stdio", check_d_import_path: ["./usr1"]
check_d_import "frobulous", check_d_import_path: ["./usr2"]
end
build do
Environment.new do |env|
env.Object("test.o", "test.d")
end
end
@@ -1,3 +1,4 @@
configure do configure do
check_d_compiler
check_d_import "std.stdio" check_d_import "std.stdio"
end end
@@ -0,0 +1,5 @@
build do
Environment.new(echo: :command) do |env|
env.Library("usr2/libfrobulous.a", "two.c")
end
end
@@ -0,0 +1,10 @@
configure do
check_lib "m", check_libpath: ["./usr1"]
check_lib "frobulous", check_libpath: ["./usr2"]
end
build do
Environment.new(echo: :command) do |env|
env.Program("simple.exe", "simple.c")
end
end
@@ -0,0 +1,30 @@
configure do
custom_check("Checking 'grep' version") do |op|
stdout, stderr, status = op.log_and_test_command(%w[grep --version])
should_fail = true
if status != 0
fail_message = "error executing grep"
elsif stdout =~ /^grep \(GNU grep\) 1\./
fail_message = "too old!"
status = 1
elsif stdout =~ /^grep \(GNU grep\) 2\./
fail_message = "we'll work with it but you should upgrade"
status = 1
should_fail = false
op.store_merge("CPPDEFINES" => "GREP_WORKAROUND")
else
op.store_append("CPPDEFINES" => "GREP_FULL")
end
op.complete(status, success_message: "good!", fail_message: fail_message, fail: should_fail)
end
custom_check("Checking sed -E flag") do |op|
stdout, stderr, status = op.log_and_test_command(%w[sed -E -e s/ab+/rep/], stdin: "abbbc")
op.complete(stdout =~ /repc/ ? 0 : 1, success_message: "good", fail_message: "fail")
end
end
build do
Environment.new do |env|
puts env["CPPDEFINES"]
end
end
+5
View File
@@ -0,0 +1,5 @@
configure do
check_c_compiler "foo123c", fail: false, on_fail: "Install the foo123 package"
check_d_compiler "foo123d", fail: false
check_cxx_compiler "foo123cxx", on_fail: lambda {puts "Install the foo123cxx package"}
end
+4
View File
@@ -0,0 +1,4 @@
int two(void)
{
return 42;
}
+3 -2
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
+3 -2
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",
[], [],
+3 -2
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",
[], [],
+4
View File
@@ -1,3 +1,7 @@
configure do
check_d_compiler "gdc"
end
build do build do
Environment.new(echo: :command) do |env| Environment.new(echo: :command) do |env|
env.Program("hello-d.exe", glob("*.d")) env.Program("hello-d.exe", glob("*.d"))
+9
View File
@@ -0,0 +1,9 @@
configure do
check_d_compiler "ldc2"
end
build do
Environment.new(echo: :command) do |env|
env.Program("hello-d.exe", glob("*.d"))
end
end
+1
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))))
@@ -0,0 +1,5 @@
build do
Environment.new do |env|
env.Program("foo.exe", glob("*.cc", "*.c"))
end
end
+6
View File
@@ -0,0 +1,6 @@
#include <stdio.h>
void foo(void)
{
printf("foo\n");
}
+12
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;
}
+1 -1
View File
@@ -1,6 +1,6 @@
build do build do
Environment.new do |env| Environment.new do |env|
env["LIBS"] += ["c"] env["LIBS"] += ["m"]
env.Program('simple.exe', Dir['*.c']) env.Program('simple.exe', Dir['*.c'])
end end
end end
+2 -2
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
+230 -25
View File
@@ -8,7 +8,7 @@ It supports the following features:
* built-in builders for several common operations * built-in builders for several common operations
* out-of-the-box support for C, C++, and D languages * out-of-the-box support for C, C++, and D languages
* extensibility for other languages or custom builders * extensibility for other languages or custom builders
* compatible with Windows, Linux, and OS X * compatible with Windows, Linux, OS X, and FreeBSD
* colorized output with build progress * colorized output with build progress
* build hooks * build hooks
@@ -24,6 +24,15 @@ custom build operations as well.
Rscons is written in Ruby, and is inspired by [SCons](https://scons.org/) and [waf](https://waf.io/). Rscons is written in Ruby, and is inspired by [SCons](https://scons.org/) and [waf](https://waf.io/).
${remove}
WARNING: This user guide is meant to be preprocessed and rendered by a custom
script.
The markdown source file is not intended to be viewed directly and will not
include all intended content.
Visit [https://holtrop.github.io/rscons/index.html](https://holtrop.github.io/rscons/index.html)
to view the properly rendered version.
${/remove}
## Design Principles ## Design Principles
### Build Correctness ### Build Correctness
@@ -89,7 +98,8 @@ The only dependency required to run Rscons is to have a Ruby interpreter
installed. installed.
The latest release can be downloaded from [https://github.com/holtrop/rscons/releases](https://github.com/holtrop/rscons/releases). The latest release can be downloaded from [https://github.com/holtrop/rscons/releases](https://github.com/holtrop/rscons/releases).
Simply copy the `rscons` executable script into the desired location within Simply copy the `rscons` executable script into the desired location within
the project to be built (typically the root of the repository). the project to be built (typically the root of the repository) and mark it
executable.
## Version Control Setup ## Version Control Setup
@@ -131,6 +141,7 @@ Such configuration checks can include:
* verifying presence of a D import * verifying presence of a D import
* verifying presence of a library * verifying presence of a library
* verifying presence of an executable * verifying presence of an executable
* any custom user-supplied configuration check
##> Build Operation ##> Build Operation
@@ -140,6 +151,17 @@ been performed, a `configure` operation will be automatically invoked.
The `build` operation will execute all builders registered to produce build The `build` operation will execute all builders registered to produce build
targets. targets.
If a `build` operation fails (e.g. due to a compilation failure), Rscons will
log the failed commands.
By default Rscons does not print the failed commands to the console so that it
is easier for the user to focus on the actual compiler failure messages rather
than the compilation command itself.
However, if the user wishes to see the compilation commands, rscons can be
invoked with the `-v` command-line option to show all complilation commands
while building, or, alternatively, following a compilation failure, the user
can invoke rscons with the -F option which will not rebuild but will show the
failed command log from the previous build operation.
##> Clean Operation ##> Clean Operation
A `clean` operation will remove all built target files. A `clean` operation will remove all built target files.
@@ -211,6 +233,7 @@ working compiler:
Each of these methods can take an optional list of compilers to check for. Each of these methods can take an optional list of compilers to check for.
If such a list is supplied, the compilers are tested in the order listed. If such a list is supplied, the compilers are tested in the order listed.
The first compiler option found which passes a compilation test is used.
Here are example calls which also show the default compiler list for each Here are example calls which also show the default compiler list for each
supported language: supported language:
@@ -223,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:
@@ -237,7 +269,7 @@ Example calls:
```ruby ```ruby
configure do configure do
check_c_header "getopt.h", fail: false, set_define: "HAVE_GETOPT_H" check_c_header "getopt.h", set_define: "HAVE_GETOPT_H"
check_c_header "FreeType2.h" check_c_header "FreeType2.h"
check_cxx_header "memory" check_cxx_header "memory"
end end
@@ -245,15 +277,9 @@ end
#### Options #### Options
##### `:fail` ##### `:check_cpppath`
If the `:fail` option is set to `false`, then the absence of the header file Optionally specifies an array of paths to look for the header file in.
will not result in the configure option failing.
##### `: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
@@ -270,6 +296,12 @@ configure do
end end
``` ```
#### Options
##### `:check_d_import_path`
Optionally specifies an array of paths to look for the module in.
###> Checking for a Library ###> Checking for a Library
The `check_lib` method can be used to check for the presence of a library. The `check_lib` method can be used to check for the presence of a library.
@@ -288,15 +320,15 @@ end
#### Options #### Options
##### `:fail` ##### `:check_libpath`
If the `:fail` option is set to `false`, then the absence of the library Optionally specifies an array of paths to look for the library in.
will not result in the configure option failing.
##### `:set_define` ##### `:use`
If set, a build define of the specified String will be added to the If not set, the library will be used by default in all `Environment` objects.
`CPPDEFINES` construction variable array if the requested library is found. If set, the library will only be used in `Environment` objects that have a
matching `:use` flag set.
###> Checking for a Program ###> Checking for a Program
@@ -340,12 +372,100 @@ 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` ##### `:use`
If not set, the library will be used by default in all `Environment` objects.
If set, the library will only be used in `Environment` objects that have a
matching `:use` flag set.
###> Custom Configuration Checks
The `Rsconscript` author can add custom configuration checks to be performed
during the rscons `configure` operation.
Here is an example from `build_tests/configure/custom_config_check.rb` showing
a custom configuration check:
```ruby
${include build_tests/configure/custom_config_check.rb}
```
A custom configuration check is created by calling the `custom_check` method
and passing a block.
The contents of the block should perform the custom configuration checking
logic.
This logic can include executing a test command or other arbitrary operations.
An argument `op` is passed to the block.
This object is an instance of the [`ConfigureOp` class](../yard/Rscons/ConfigureOp.html)
class and provides several methods that can be used to aid with the custom
configuration check.
The [`log_and_test_command`](../yard/Rscons/ConfigureOp.html#log_and_test_command-instance_method)
method can be used to execute a test command and retrieve its results.
The command and its output are also logged to the config.log file.
The [`store_merge`](../yard/Rscons/ConfigureOp.html#store_merge-instance_method),
[`store_append`](../yard/Rscons/ConfigureOp.html#store_append-instance_method),
and [`store_parse`](../yard/Rscons/ConfigureOp.html#store_parse-instance_method)
methods can be used to store construction variables for Environments created
during the `build` operation.
Finally, the [`complete`](../yard/Rscons/ConfigureOp.html#complete-instance_method)
method can be used to complete the configuration check and indicate a success
or failure.
While performing a custom configuration check, it can sometimes be useful to
be able to construct an Environment to use the set of default construction
variables as defined so far in the configuration block, for example to expand
construction variables to build a test command.
The normal `Environment` class cannot be used within the `configure` block,
however the [`BasicEnvironment`](../yard/Rscons/BasicEnvironment.html) class
can be used for such a purpose.
For example, to expand the current `${CCCMD}` value:
```ruby
configure do
custom_check("Checking something to do with CCCMD") do
command = BasicEnvironment.new.expand_varref("${CCCMD}")
# ...
end
end
```
###> Global Configuration Check Options
#### `:fail`
If the `:fail` option is set to `false`, then the absence of the package or 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. 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` #### `: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 If set, a build define of the specified String will be added to the
`CPPDEFINES` construction variable array if the requested package is found. `CPPDEFINES` construction variable array if the requested package is found.
@@ -399,7 +519,8 @@ files.
###> Specifying Source Files: The glob Method ###> Specifying Source Files: The glob Method
The `glob` method can be used to find files matching the patterns specified. The [`glob`](../yard/Rscons/Script/Dsl.html#glob-instance_method) method can be
used to find files matching the patterns specified.
It supports a syntax similar to the Ruby [Dir.glob method](https://ruby-doc.org/core-2.5.1/Dir.html#method-c-glob) but operates more deterministically. It supports a syntax similar to the Ruby [Dir.glob method](https://ruby-doc.org/core-2.5.1/Dir.html#method-c-glob) but operates more deterministically.
Example use: Example use:
@@ -622,6 +743,24 @@ A platform-dependent program suffix will be appended to the target name if one
is not specified. is not specified.
This can be controlled with the `PROGSUFFIX` construction variable. This can be controlled with the `PROGSUFFIX` construction variable.
##### Direct Mode
The Program builder supports a "direct" mode which is activated by specifying
the `:direct` option.
In the direct mode, all source files are passed directly to the compiler
together and compiled and linked in one step, rather than being individually
compiled to separate object files first.
This mode allows taking advantage of any multi-file compilation capabilities
of the compiler.
However, it also requires recompiling all source files when any one of them
has changed.
Example use:
```ruby
env.Program("myprog", Rscons.glob("src/**/*.c"), direct: true)
```
####> The SharedLibrary Builder ####> The SharedLibrary Builder
```ruby ```ruby
@@ -638,6 +777,24 @@ they are not specified by the user.
These values can be controlled by overriding the `SHLIBPREFIX` and These values can be controlled by overriding the `SHLIBPREFIX` and
`SHLIBSUFFIX` construction variables. `SHLIBSUFFIX` construction variables.
##### Direct Mode
The SharedLibrary builder supports a "direct" mode which is activated by
specifying the `:direct` option.
In the direct mode, all source files are passed directly to the compiler
together and compiled and linked in one step, rather than being individually
compiled to separate object files first.
This mode allows taking advantage of any multi-file compilation capabilities
of the compiler.
However, it also requires recompiling all source files when any one of them
has changed.
Example use:
```ruby
env.SharedLibrary("mydll", Rscons.glob("src/**/*.c"), direct: true)
```
####> The SharedObject Builder ####> The SharedObject Builder
```ruby ```ruby
@@ -703,6 +860,18 @@ Post-build hooks are only invoked if the build operation was a success.
Build hooks and post-build hooks can register new build targets. Build hooks and post-build hooks can register new build targets.
###> Barriers
Normally Rscons will parallelize all builders.
A barrier can be used to separate sets of build targets.
All build targets registered before the barrier is created will be built before
Rscons will schedule any build targets after the barrier.
In other words, build targets are not parallelized across a barrier.
```ruby
env.barrier
```
##> Extending Rscons ##> Extending Rscons
### Adding New Languages ### Adding New Languages
@@ -869,7 +1038,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
@@ -894,7 +1063,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"))
@@ -952,9 +1121,9 @@ the `Rscons::Command` class and then `wait_for` the Command object.
However, this is a common enough case that Rscons provides a few However, this is a common enough case that Rscons provides a few
convenience methods to handle this: convenience methods to handle this:
* `Rscons::Builder#register_command` * [`Rscons::Builder#register_command`](../yard/Rscons/Builder.html#register_command-instance_method)
* `Rscons::Builder#standard_command` * [`Rscons::Builder#standard_command`](../yard/Rscons/Builder.html#standard_command-instance_method)
* `Rscons::Builder#finalize_command` * [`Rscons::Builder#finalize_command`](../yard/Rscons/Builder.html#finalize_command-instance_method)
The `register_command` helper method can be used to create a Command object The `register_command` helper method can be used to create a Command object
and wait for it to complete. and wait for it to complete.
@@ -1104,6 +1273,42 @@ build do
end end
``` ```
##> ./configure && make
You can make your Rscons-based project more familiar to users of
autoconf-generated projects by creating a `configure` script and a `Makefile`
for the user.
Such users may be used to executing:
```
./configure
make
```
to build a project.
To do this, create a `configure` script with contents similar to the following:
```
#!/bin/sh
exec "$(dirname "$0")"/rscons configure "$@"
```
and make it executable with `chmod +x configure`.
If you want your users to be able to build/clean a project with `make` but
still make use of Rscons under the hood, you can create a `Makefile` with
contents something like this:
```
.PHONY: all
all:
./rscons build
.PHONY: clean
clean:
./rscons clean
```
##> YARD API Documentation ##> YARD API Documentation
See [here](../yard/index.html) for Rscons YARD API Documentation. See [here](../yard/index.html) for Rscons YARD API Documentation.
Binary file not shown.

After

Width:  |  Height:  |  Size: 242 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

+2 -12
View File
@@ -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"
+2 -1
View File
@@ -3,6 +3,7 @@ module Rscons
module Ansi module Ansi
class << self class << self
# ANSI terminal reset sequence.
RESET = "\e[0m" RESET = "\e[0m"
# Write a message to an IO with ANSI escape codes. # Write a message to an IO with ANSI escape codes.
@@ -83,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?
+3
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
@@ -192,6 +194,7 @@ module Rscons
begin begin
@script.configure(co) @script.configure(co)
rescue ConfigureOp::ConfigureFailure rescue ConfigureOp::ConfigureFailure
Ansi.write($stderr, :red, "Configuration failed", :reset, "\n")
rv = 1 rv = 1
end end
co.close(rv == 0) co.close(rv == 0)
+3 -2
View File
@@ -4,10 +4,11 @@ module Rscons
# Create a BasicEnvironment object. # Create a BasicEnvironment object.
# #
# @api private
#
# @param options [Hash] # @param options [Hash]
# Construction options. # Construction options.
# @option options [String, Array<String>] :use
# Use flag(s). If specified, any configuration flags which were saved
# with a corresponding `:use` value will be applied to this Environment.
def initialize(options = {}) def initialize(options = {})
@varset = VarSet.new(Rscons::DEFAULT_CONSTRUCTION_VARIABLES) @varset = VarSet.new(Rscons::DEFAULT_CONSTRUCTION_VARIABLES)
load_configuration_data!(options) load_configuration_data!(options)
+1
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)
+1 -1
View File
@@ -9,7 +9,7 @@ module Rscons
def finalize_command_with_depfile def finalize_command_with_depfile
deps = @sources deps = @sources
if File.exists?(@vars["_DEPFILE"]) if File.exists?(@vars["_DEPFILE"])
deps += Util.parse_makefile_deps(@vars["_DEPFILE"]) deps += Util.parse_dependency_file(@vars["_DEPFILE"])
end end
@cache.register_build(@target, @command, deps.uniq, @env) @cache.register_build(@target, @command, deps.uniq, @env)
true true
+2 -1
View File
@@ -8,6 +8,7 @@ module Rscons
module Builders module Builders
module Mixins module Mixins
# Mixin for builders needing to build object files.
module Object module Object
include Depfile include Depfile
@@ -77,7 +78,7 @@ module Rscons
if @vars[:direct] if @vars[:direct]
@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)
+1 -1
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)
+2
View File
@@ -8,6 +8,8 @@ module Rscons
include Mixins::Program include Mixins::Program
class << self class << self
# Custom new method which will delegate to the correct class depending
# on the options specified.
def new(options, *more) def new(options, *more)
unless File.basename(options[:target])["."] unless File.basename(options[:target])["."]
options[:target] += options[:env].expand_varref("${PROGSUFFIX}", options[:vars]) options[:target] += options[:env].expand_varref("${PROGSUFFIX}", options[:vars])
+2
View File
@@ -8,6 +8,8 @@ module Rscons
include Mixins::Program include Mixins::Program
class << self class << self
# Custom new method which will delegate to the correct class depending
# on the options specified.
def new(options, *more) def new(options, *more)
libprefix = options[:env].expand_varref("${SHLIBPREFIX}", options[:vars]) libprefix = options[:env].expand_varref("${SHLIBPREFIX}", options[:vars])
unless File.basename(options[:target]).start_with?(libprefix) unless File.basename(options[:target]).start_with?(libprefix)
+2
View File
@@ -7,6 +7,8 @@ module Rscons
include Mixins::Object include Mixins::Object
class << self class << self
# Content component to add to build path to separate objects built
# using this builder from others.
def extra_path def extra_path
"_shared" "_shared"
end end
+2 -1
View File
@@ -1,11 +1,12 @@
require "rscons" require "rscons"
require "optparse" require "optparse"
# CLI usage string.
USAGE = <<EOF USAGE = <<EOF
Usage: #{$0} [global options] [operation] [operation options] Usage: #{$0} [global options] [operation] [operation options]
Global options: Global options:
-F, --show-failure Show previous failed command(s) and exit -F, --show-failure Show failed command log from previous build and exit
--version Show rscons version and exit --version Show rscons version and exit
-h, --help Show rscons help and exit -h, --help Show rscons help and exit
-r COLOR, --color=COLOR Set color mode (off, auto, force) -r COLOR, --color=COLOR Set color mode (off, auto, force)
+246 -150
View File
@@ -62,6 +62,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 +73,7 @@ module Rscons
cc = ccc.find do |cc| cc = ccc.find do |cc|
test_c_compiler(cc) test_c_compiler(cc)
end end
common_config_checks(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 +84,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 +95,7 @@ module Rscons
cc = ccc.find do |cc| cc = ccc.find do |cc|
test_cxx_compiler(cc) test_cxx_compiler(cc)
end end
common_config_checks(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 +106,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 +117,7 @@ module Rscons
dc = cdc.find do |dc| dc = cdc.find do |dc|
test_d_compiler(dc) test_d_compiler(dc)
end end
common_config_checks(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.
@@ -122,11 +134,12 @@ module Rscons
if status == 0 if status == 0
store_parse(stdout, options) store_parse(stdout, options)
end end
common_config_checks(status, options) complete(status, options)
end end
# Check for a C header. # Check for a C header.
def check_c_header(header_name, options = {}) def check_c_header(header_name, options = {})
check_cpppath = [nil] + (options[:check_cpppath] || [])
Ansi.write($stdout, "Checking for C header '", :cyan, header_name, :reset, "'... ") Ansi.write($stdout, "Checking for C header '", :cyan, header_name, :reset, "'... ")
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh| File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
fh.puts <<-EOF fh.puts <<-EOF
@@ -139,15 +152,30 @@ module Rscons
vars = { vars = {
"LD" => "${CC}", "LD" => "${CC}",
"_SOURCES" => "#{@work_dir}/cfgtest.c", "_SOURCES" => "#{@work_dir}/cfgtest.c",
"_TARGET" => "#{@work_dir}/cfgtest.exe", "_TARGET" => "#{@work_dir}/cfgtest.o",
"_DEPFILE" => "#{@work_dir}/cfgtest.mf",
} }
command = BasicEnvironment.new.build_command("${LDCMD}", vars) status = 1
check_cpppath.each do |cpppath|
env = BasicEnvironment.new
if cpppath
env["CPPPATH"] += Array(cpppath)
end
command = env.build_command("${CCCMD}", vars)
_, _, status = log_and_test_command(command) _, _, status = log_and_test_command(command)
common_config_checks(status, options) if status == 0
if cpppath
store_append({"CPPPATH" => Array(cpppath)}, options)
end
break
end
end
complete(status, options)
end end
# Check for a C++ header. # Check for a C++ header.
def check_cxx_header(header_name, options = {}) def check_cxx_header(header_name, options = {})
check_cpppath = [nil] + (options[:check_cpppath] || [])
Ansi.write($stdout, "Checking for C++ header '", :cyan, header_name, :reset, "'... ") Ansi.write($stdout, "Checking for C++ header '", :cyan, header_name, :reset, "'... ")
File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh| File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh|
fh.puts <<-EOF fh.puts <<-EOF
@@ -160,15 +188,30 @@ module Rscons
vars = { vars = {
"LD" => "${CXX}", "LD" => "${CXX}",
"_SOURCES" => "#{@work_dir}/cfgtest.cxx", "_SOURCES" => "#{@work_dir}/cfgtest.cxx",
"_TARGET" => "#{@work_dir}/cfgtest.exe", "_TARGET" => "#{@work_dir}/cfgtest.o",
"_DEPFILE" => "#{@work_dir}/cfgtest.mf",
} }
command = BasicEnvironment.new.build_command("${LDCMD}", vars) status = 1
check_cpppath.each do |cpppath|
env = BasicEnvironment.new
if cpppath
env["CPPPATH"] += Array(cpppath)
end
command = env.build_command("${CXXCMD}", vars)
_, _, status = log_and_test_command(command) _, _, status = log_and_test_command(command)
common_config_checks(status, options) if status == 0
if cpppath
store_append({"CPPPATH" => Array(cpppath)}, options)
end
break
end
end
complete(status, options)
end end
# Check for a D import. # Check for a D import.
def check_d_import(d_import, options = {}) def check_d_import(d_import, options = {})
check_d_import_path = [nil] + (options[:check_d_import_path] || [])
Ansi.write($stdout, "Checking for D import '", :cyan, d_import, :reset, "'... ") Ansi.write($stdout, "Checking for D import '", :cyan, d_import, :reset, "'... ")
File.open("#{@work_dir}/cfgtest.d", "wb") do |fh| File.open("#{@work_dir}/cfgtest.d", "wb") do |fh|
fh.puts <<-EOF fh.puts <<-EOF
@@ -181,15 +224,30 @@ module Rscons
vars = { vars = {
"LD" => "${DC}", "LD" => "${DC}",
"_SOURCES" => "#{@work_dir}/cfgtest.d", "_SOURCES" => "#{@work_dir}/cfgtest.d",
"_TARGET" => "#{@work_dir}/cfgtest.exe", "_TARGET" => "#{@work_dir}/cfgtest.o",
"_DEPFILE" => "#{@work_dir}/cfgtest.mf",
} }
command = BasicEnvironment.new.build_command("${LDCMD}", vars) status = 1
check_d_import_path.each do |d_import_path|
env = BasicEnvironment.new
if d_import_path
env["D_IMPORT_PATH"] += Array(d_import_path)
end
command = env.build_command("${DCCMD}", vars)
_, _, status = log_and_test_command(command) _, _, status = log_and_test_command(command)
common_config_checks(status, options) if status == 0
if d_import_path
store_append({"D_IMPORT_PATH" => Array(d_import_path)}, options)
end
break
end
end
complete(status, options)
end end
# Check for a library. # Check for a library.
def check_lib(lib, options = {}) def check_lib(lib, options = {})
check_libpath = [nil] + (options[:check_libpath] || [])
Ansi.write($stdout, "Checking for library '", :cyan, lib, :reset, "'... ") Ansi.write($stdout, "Checking for library '", :cyan, lib, :reset, "'... ")
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh| File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
fh.puts <<-EOF fh.puts <<-EOF
@@ -204,129 +262,49 @@ module Rscons
"_SOURCES" => "#{@work_dir}/cfgtest.c", "_SOURCES" => "#{@work_dir}/cfgtest.c",
"_TARGET" => "#{@work_dir}/cfgtest.exe", "_TARGET" => "#{@work_dir}/cfgtest.exe",
} }
command = BasicEnvironment.new.build_command("${LDCMD}", vars) status = 1
check_libpath.each do |libpath|
env = BasicEnvironment.new
if libpath
env["LIBPATH"] += Array(libpath)
end
command = env.build_command("${LDCMD}", vars)
_, _, status = log_and_test_command(command) _, _, status = log_and_test_command(command)
if status == 0
if libpath
store_append({"LIBPATH" => Array(libpath)}, options)
end
break
end
end
if status == 0 if status == 0
store_append({"LIBS" => [lib]}, options) store_append({"LIBS" => [lib]}, options)
end end
common_config_checks(status, options) complete(status, options)
end end
# Check for a executable program. # Check for a executable program.
def check_program(program, options = {}) def check_program(program, options = {})
Ansi.write($stdout, "Checking for program '", :cyan, program, :reset, "'... ") Ansi.write($stdout, "Checking for program '", :cyan, program, :reset, "'... ")
path = Util.find_executable(program) path = Util.find_executable(program)
common_config_checks(path ? 0 : 1, options.merge(success_message: path)) complete(path ? 0 : 1, options.merge(success_message: path))
end
private
# Test a C compiler.
#
# @param cc [String]
# C compiler to test.
#
# @return [Boolean]
# Whether the C compiler tested successfully.
def test_c_compiler(cc)
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
fh.puts <<-EOF
#include <stdio.h>
int main(int argc, char * argv[]) {
printf("Success.\\n");
return 0;
}
EOF
end
command = %W[#{cc} -o #{@work_dir}/cfgtest.exe #{@work_dir}/cfgtest.c]
merge = {"CC" => cc}
_, _, status = log_and_test_command(command)
if status == 0
stdout, _, status = log_and_test_command(["#{@work_dir}/cfgtest.exe"])
if status == 0 and stdout =~ /Success/
store_merge(merge)
true
end
end
end
# Test a C++ compiler.
#
# @param cc [String]
# C++ compiler to test.
#
# @return [Boolean]
# Whether the C++ compiler tested successfully.
def test_cxx_compiler(cc)
File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh|
fh.puts <<-EOF
#include <iostream>
using namespace std;
int main(int argc, char * argv[]) {
cout << "Success." << endl;
return 0;
}
EOF
end
command = %W[#{cc} -o #{@work_dir}/cfgtest.exe #{@work_dir}/cfgtest.cxx]
merge = {"CXX" => cc}
_, _, status = log_and_test_command(command)
if status == 0
stdout, _, status = log_and_test_command(["#{@work_dir}/cfgtest.exe"])
if status == 0 and stdout =~ /Success/
store_merge(merge)
true
end
end
end
# Test a D compiler.
#
# @param dc [String]
# D compiler to test.
#
# @return [Boolean]
# Whether the D compiler tested successfully.
def test_d_compiler(dc)
File.open("#{@work_dir}/cfgtest.d", "wb") do |fh|
fh.puts <<-EOF
import std.stdio;
int main() {
writeln("Success.");
return 0;
}
EOF
end
[:gdc, :ldc2].find do |dc_test|
case dc_test
when :gdc
command = %W[#{dc} -o #{@work_dir}/cfgtest.exe #{@work_dir}/cfgtest.d]
merge = {"DC" => dc}
when :ldc2
command = %W[#{dc} -of #{@work_dir}/cfgtest.exe #{@work_dir}/cfgtest.d]
env = BasicEnvironment.new
merge = {
"DC" => dc,
"DCCMD" => env["DCCMD"].map {|e| if e == "-o"; "-of"; else; e; end},
"LDCMD" => env["LDCMD"].map {|e| if e == "-o"; "-of"; else; e; end},
}
end
_, _, status = log_and_test_command(command)
if status == 0
stdout, _, status = log_and_test_command(["#{@work_dir}/cfgtest.exe"])
if status == 0 and stdout =~ /Success/
store_merge(merge)
true
end
end
end
end end
# Execute a test command and log the result. # Execute a test command and log the result.
def log_and_test_command(command) #
# @param command [Array<String>]
# Command to execute.
# @param options [Hash]
# Optional arguments.
# @option options [String] :stdin
# Data to send to standard input stream of the executed command.
#
# @return [String, String, Process::Status]
# stdout, stderr, status
def log_and_test_command(command, options = {})
begin begin
@log_fh.puts("Command: #{command.join(" ")}") @log_fh.puts("Command: #{command.join(" ")}")
stdout, stderr, status = Open3.capture3(*command) stdout, stderr, status = Open3.capture3(*command, stdin_data: options[:stdin])
@log_fh.puts("Exit status: #{status.to_i}") @log_fh.puts("Exit status: #{status.to_i}")
@log_fh.write(stdout) @log_fh.write(stdout)
@log_fh.write(stderr) @log_fh.write(stderr)
@@ -342,6 +320,10 @@ module Rscons
# Hash containing the variables to merge. # Hash containing the variables to merge.
# @param options [Hash] # @param options [Hash]
# Options. # Options.
# @option options [String] :use
# A 'use' name. If specified, the construction variables are only applied
# to an Environment if the Environment is constructed with a matching
# `:use` value.
def store_merge(vars, options = {}) def store_merge(vars, options = {})
store_vars = store_common(options) store_vars = store_common(options)
store_vars["merge"] ||= {} store_vars["merge"] ||= {}
@@ -356,6 +338,10 @@ module Rscons
# Hash containing the variables to append. # Hash containing the variables to append.
# @param options [Hash] # @param options [Hash]
# Options. # Options.
# @option options [String] :use
# A 'use' name. If specified, the construction variables are only applied
# to an Environment if the Environment is constructed with a matching
# `:use` value.
def store_append(vars, options = {}) def store_append(vars, options = {})
store_vars = store_common(options) store_vars = store_common(options)
store_vars["append"] ||= {} store_vars["append"] ||= {}
@@ -374,12 +360,151 @@ module Rscons
# String containing the flags to parse. # String containing the flags to parse.
# @param options [Hash] # @param options [Hash]
# Options. # Options.
# @option options [String] :use
# A 'use' name. If specified, the construction variables are only applied
# to an Environment if the Environment is constructed with a matching
# `:use` value.
def store_parse(flags, options = {}) def store_parse(flags, options = {})
store_vars = store_common(options) store_vars = store_common(options)
store_vars["parse"] ||= [] store_vars["parse"] ||= []
store_vars["parse"] << flags store_vars["parse"] << flags
end end
# Perform processing common to several configure checks.
#
# @param status [Process::Status, Integer]
# Process exit code. 0 for success, non-zero for error.
# @param options [Hash]
# Common check options.
# @option options [Boolean] :fail
# Whether to fail configuration if the requested item is not found.
# This defaults to true if the :set_define option is not specified,
# otherwise defaults to false if :set_define option is specified.
# @option options [String] :set_define
# A define to set (in CPPDEFINES) if the requested item is found.
# @option options [String] :success_message
# Message to print on success (default "found").
def complete(status, options)
success_message = options[:success_message] || "found"
fail_message = options[:fail_message] || "not found"
if status == 0
Ansi.write($stdout, :green, "#{success_message}\n")
if options[:set_define]
store_append("CPPDEFINES" => [options[:set_define]])
end
else
should_fail =
if options.has_key?(:fail)
options[:fail]
else
!options[:set_define]
end
color = should_fail ? :red : :yellow
Ansi.write($stdout, color, "#{fail_message}\n")
if options[:on_fail].is_a?(String)
$stdout.puts(options[:on_fail])
elsif options[:on_fail].is_a?(Proc)
options[:on_fail].call
end
if should_fail
raise ConfigureFailure.new
end
end
end
private
# Test a C compiler.
#
# @param cc [String]
# C compiler to test.
#
# @return [Boolean]
# Whether the C compiler tested successfully.
def test_c_compiler(cc)
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
fh.puts <<-EOF
int fun(int val) {
return val * 2;
}
EOF
end
command = %W[#{cc} -c -o #{@work_dir}/cfgtest.o #{@work_dir}/cfgtest.c]
merge = {"CC" => cc}
_, _, status = log_and_test_command(command)
if status == 0
store_merge(merge)
true
end
end
# Test a C++ compiler.
#
# @param cc [String]
# C++ compiler to test.
#
# @return [Boolean]
# Whether the C++ compiler tested successfully.
def test_cxx_compiler(cc)
File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh|
fh.puts <<-EOF
template<typename T>
T fun(T val) {
return val * 2;
}
EOF
end
command = %W[#{cc} -c -o #{@work_dir}/cfgtest.o #{@work_dir}/cfgtest.cxx]
merge = {"CXX" => cc}
_, _, status = log_and_test_command(command)
if status == 0
store_merge(merge)
true
end
end
# Test a D compiler.
#
# @param dc [String]
# D compiler to test.
#
# @return [Boolean]
# Whether the D compiler tested successfully.
def test_d_compiler(dc)
File.open("#{@work_dir}/cfgtest.d", "wb") do |fh|
fh.puts <<-EOF
import core.math;
int fun() {
return 0;
}
EOF
end
[:gdc, :ldc2].find do |dc_test|
case dc_test
when :gdc
command = %W[#{dc} -c -o #{@work_dir}/cfgtest.o #{@work_dir}/cfgtest.d]
merge = {"DC" => dc}
when :ldc2
# ldc2 on Windows expect an object file suffix of .obj.
ldc_objsuffix = RUBY_PLATFORM =~ /mingw|msys/ ? ".obj" : ".o"
command = %W[#{dc} -c -of #{@work_dir}/cfgtest#{ldc_objsuffix} #{@work_dir}/cfgtest.d]
env = BasicEnvironment.new
merge = {
"DC" => dc,
"DCCMD" => env["DCCMD"].map {|e| e.sub(/^-o$/, "-of")},
"LDCMD" => env["LDCMD"].map {|e| e.sub(/^-o$/, "-of")},
"DDEPGEN" => ["-deps=${_DEPFILE}"],
}
merge["OBJSUFFIX"] = [ldc_objsuffix]
end
_, _, status = log_and_test_command(command)
if status == 0
store_merge(merge)
true
end
end
end
# Common functionality for all store methods. # Common functionality for all store methods.
# #
# @param options [Hash] # @param options [Hash]
@@ -403,34 +528,5 @@ module Rscons
end end
end end
# Perform processing common to several configure checks.
#
# @param status [Process::Status, Integer]
# Process exit code.
# @param options [Hash]
# Common check options.
# @option options [Boolean] :fail
# Whether to fail configuration if the requested item is not found.
# @option options [String] :set_define
# A define to set (in CPPDEFINES) if the requested item is found.
# @option options [String] :success_message
# Message to print on success (default "found").
def common_config_checks(status, options)
success_message = options[:success_message] || "found"
if status == 0
Ansi.write($stdout, :green, "#{success_message}\n")
if options[:set_define]
store_append("CPPDEFINES" => [options[:set_define]])
end
else
if options.has_key?(:fail) and not options[:fail]
Ansi.write($stdout, :yellow, "not found\n")
else
Ansi.write($stdout, :red, "not found\n")
raise ConfigureFailure.new
end
end
end
end end
end end
+1 -1
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.
+6 -7
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
@@ -59,6 +58,9 @@ module Rscons
# :command, :short, or :off (default :short) # :command, :short, or :off (default :short)
# @option options [Boolean] :exclude_builders # @option options [Boolean] :exclude_builders
# Whether to omit adding default builders (default false) # Whether to omit adding default builders (default false)
# @option options [String, Array<String>] :use
# Use flag(s). If specified, any configuration flags which were saved
# with a corresponding `:use` value will be applied to this Environment.
# #
# If a block is given, the Environment object is yielded to the block and # If a block is given, the Environment object is yielded to the block and
# when the block returns, the {#process} method is automatically called. # when the block returns, the {#process} method is automatically called.
@@ -248,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.
@@ -284,7 +286,7 @@ 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 += "\nRun `#{$0} -F` to see the failed command(s)." msg += "\nUse -F to view the failed command log from the previous build operation"
end end
raise BuildError.new(msg) raise BuildError.new(msg)
end end
@@ -699,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
+15
View File
@@ -3,6 +3,7 @@ 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.
class Dsl class Dsl
# Create a Dsl. # Create a Dsl.
def initialize(script) def initialize(script)
@@ -57,6 +58,7 @@ module Rscons
end end
end end
# DSL available to the 'configure' block.
class ConfigureDsl class ConfigureDsl
# Create a ConfigureDsl. # Create a ConfigureDsl.
# #
@@ -81,6 +83,19 @@ module Rscons
@configure_op.__send__(method_name, *args) @configure_op.__send__(method_name, *args)
end end
end end
# Perform a custom configuration check.
#
# @param message [String]
# Custom configuration check message (e.g. "Checking for foo").
# rscons will add "... " to the end of the message.
# @yieldparam configure_op [ConfigureOp]
# {ConfigureOp} object.
# @return [void]
def custom_check(message, &block)
$stdout.write(message + "... ")
block[@configure_op]
end
end end
# @return [String, nil] # @return [String, nil]
+43 -7
View File
@@ -1,4 +1,5 @@
module Rscons module Rscons
# A collection of stand-alone utility methods.
module Util module Util
class << self class << self
@@ -8,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?("/")
@@ -67,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/
@@ -159,7 +160,7 @@ module Rscons
end end
end end
# Parse dependencies from a Makefile. # Parse dependencies from a Makefile or ldc2 dependency file.
# #
# This method is used internally by Rscons builders. # This method is used internally by Rscons builders.
# #
@@ -168,7 +169,7 @@ module Rscons
# #
# @return [Array<String>] # @return [Array<String>]
# Paths of dependency files. # Paths of dependency files.
def parse_makefile_deps(mf_fname) def parse_dependency_file(mf_fname)
deps = [] deps = []
buildup = '' buildup = ''
File.read(mf_fname).each_line do |line| File.read(mf_fname).each_line do |line|
@@ -176,7 +177,11 @@ module Rscons
buildup += ' ' + $1 buildup += ' ' + $1
else else
buildup += ' ' + line buildup += ' ' + line
if buildup =~ /^.*: (.*)$/ if buildup =~ /^[^:]+\(\S+\)\s*:.*?:[^:]+\((\S+)\)/
# ldc2-style dependency line
deps << $1
elsif buildup =~ /^.*: (.*)$/
# Makefile-style dependency line
mf_deps = $1 mf_deps = $1
deps += mf_deps.split(' ').map(&:strip) deps += mf_deps.split(' ').map(&:strip)
end end
@@ -186,6 +191,32 @@ 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
private private
# Check if a directory contains a certain executable. # Check if a directory contains a certain executable.
@@ -194,11 +225,15 @@ 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)
executable = executable.downcase executable = executable.downcase
dir_entries = Dir.entries(path_entry) dir_entries = Dir.entries(path_entry)
dir_entries.find do |entry| dir_entries.find do |entry|
@@ -211,6 +246,7 @@ module Rscons
return path return path
end end
end end
end
else else
path = "#{path_entry}/#{executable}" path = "#{path_entry}/#{executable}"
return path if is_executable[path] return path if is_executable[path]
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons module Rscons
# Project version. # Project version.
VERSION = "2.0.0beta1" VERSION = "2.2.0"
end end
+6 -1
View File
@@ -1,6 +1,6 @@
<html> <html>
<head> <head>
<title>RScons User Guide<%= subpage_title %> - Version <%= Rscons::VERSION %></title> <title>Rscons User Guide<%= subpage_title %> - Version <%= Rscons::VERSION %></title>
<style> <style>
body { body {
background-color: #CCC; background-color: #CCC;
@@ -31,6 +31,11 @@
.separator { .separator {
height: 2em; height: 2em;
} }
.img_block_center {
display: block;
margin-left: auto;
margin-right: auto;
}
.page_nav { .page_nav {
width: 100%; width: 100%;
table-layout: fixed; table-layout: fixed;
+7 -5
View File
@@ -59,9 +59,10 @@ combined_file.each do |line|
end end
require "zlib" require "zlib"
require "base64"
compressed_script = Zlib::Deflate.deflate(stripped_comments.join) compressed_script = Zlib::Deflate.deflate(stripped_comments.join)
escaped_compressed_script = compressed_script.gsub("#", "#1").gsub("\n", "#2").gsub("\r", "#3").gsub("\0", "#4") encoded_compressed_script = Base64.encode64(compressed_script).gsub("\n", "")
hash = Digest::MD5.hexdigest(escaped_compressed_script) hash = Digest::MD5.hexdigest(encoded_compressed_script)
FileUtils.mkdir_p(DIST) FileUtils.mkdir_p(DIST)
File.open("#{DIST}/#{PROG_NAME}", "wb", 0755) do |fh| File.open("#{DIST}/#{PROG_NAME}", "wb", 0755) do |fh|
@@ -70,9 +71,10 @@ File.open("#{DIST}/#{PROG_NAME}", "wb", 0755) do |fh|
script = File.join(File.dirname(__FILE__), ".rscons-#{VERSION}-#{hash}.rb") script = File.join(File.dirname(__FILE__), ".rscons-#{VERSION}-#{hash}.rb")
unless File.exists?(script) unless File.exists?(script)
if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/ if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/
escaped_compressed = $1
unescaped_compressed = escaped_compressed.gsub("#4", "\\0").gsub("#3", "\\r").gsub("#2", "\\n").gsub("#1", "#")
require "zlib" require "zlib"
require "base64"
encoded_compressed = $1
unescaped_compressed = Base64.decode64(encoded_compressed)
inflated = Zlib::Inflate.inflate(unescaped_compressed) inflated = Zlib::Inflate.inflate(unescaped_compressed)
File.open(script, "wb") do |fh| File.open(script, "wb") do |fh|
fh.write(inflated) fh.write(inflated)
@@ -85,6 +87,6 @@ load script
if __FILE__ == $0 if __FILE__ == $0
Rscons::Cli.run(ARGV) Rscons::Cli.run(ARGV)
end end
#==>#{escaped_compressed_script} #==>#{encoded_compressed_script}
EOF EOF
end end
+14 -1
View File
@@ -59,6 +59,8 @@ class Generator
line = @lines.slice!(0) line = @lines.slice!(0)
if line =~ /^```(.*)$/ if line =~ /^```(.*)$/
@sections.last.append(gather_code_section($1)) @sections.last.append(gather_code_section($1))
elsif line.chomp == "${remove}"
remove_section
elsif line =~ /^(#+)(>)?\s*(.*)$/ elsif line =~ /^(#+)(>)?\s*(.*)$/
level_text, new_page_text, title_text = $1, $2, $3 level_text, new_page_text, title_text = $1, $2, $3
level = $1.size level = $1.size
@@ -152,7 +154,8 @@ class Generator
end end
def render_toc def render_toc
toc_content = %[<h1>Table of Contents</h1>\n] toc_content = %[<img class="img_block_center" src="rscons_logo_1000.png" />]
toc_content += %[<h1>Table of Contents</h1>\n]
@sections.each do |section| @sections.each do |section|
indent = section.number.split(".").size - 1 indent = section.number.split(".").size - 1
toc_content += %[<span style="padding-left: #{4 * indent}ex;">] toc_content += %[<span style="padding-left: #{4 * indent}ex;">]
@@ -196,6 +199,15 @@ class Generator
end end
end end
def remove_section
loop do
line = @lines.slice!(0)
if line.chomp == "${/remove}"
break
end
end
end
def get_next_section_number(level) def get_next_section_number(level)
if @current_section_number.size == level - 1 if @current_section_number.size == level - 1
@current_section_number << 1 @current_section_number << 1
@@ -212,5 +224,6 @@ end
input = load_file("doc/user_guide.md") input = load_file("doc/user_guide.md")
FileUtils.rm_rf("gen/user_guide") FileUtils.rm_rf("gen/user_guide")
FileUtils.mkdir_p("gen/user_guide") FileUtils.mkdir_p("gen/user_guide")
FileUtils.cp("img/rscons_logo_1000.png", "gen/user_guide")
Generator.new(input, "gen/user_guide/user_guide.html", false) Generator.new(input, "gen/user_guide/user_guide.html", false)
Generator.new(input, "gen/user_guide", true) Generator.new(input, "gen/user_guide", true)
+330 -120
View File
@@ -65,6 +65,9 @@ describe Rscons do
def create_exe(exe_name, contents) def create_exe(exe_name, contents)
exe_file = "#{@build_test_run_dir}/_bin/#{exe_name}" exe_file = "#{@build_test_run_dir}/_bin/#{exe_name}"
if RUBY_PLATFORM =~ /mingw/
exe_file += ".bat"
end
File.open(exe_file, "wb") do |fh| File.open(exe_file, "wb") do |fh|
fh.puts("#!/bin/sh") fh.puts("#!/bin/sh")
fh.puts(contents) fh.puts(contents)
@@ -140,9 +143,13 @@ 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["PATH"] = "#{@build_test_run_dir}/_bin#{File::PATH_SEPARATOR}#{env["PATH"]}" path = ["#{@build_test_run_dir}/_bin", "#{env["PATH"]}"]
if options[:path]
path = Array(options[:path]) + path
end
env["PATH"] = path.join(File::PATH_SEPARATOR)
stdout, stderr, status = Open3.capture3(env, *command) stdout, stderr, status = Open3.capture3(env, *command)
File.open("#{@build_test_run_dir}/.stdout", "wb") do |fh| File.open("#{@build_test_run_dir}/.stdout", "wb") do |fh|
fh.write(stdout) fh.write(stdout)
@@ -180,6 +187,10 @@ EOF
end end
end end
def nr(str)
str.gsub("\r", "")
end
########################################################################### ###########################################################################
# Tests # Tests
########################################################################### ###########################################################################
@@ -188,8 +199,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
@@ -197,7 +208,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
@@ -205,8 +216,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
@@ -224,19 +235,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
@@ -247,7 +258,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 ""
@@ -261,7 +272,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
@@ -274,13 +285,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
@@ -310,9 +321,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
@@ -321,7 +332,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
@@ -336,9 +347,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
@@ -349,7 +360,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
@@ -366,12 +377,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
@@ -388,7 +399,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 ""
@@ -407,7 +418,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
@@ -423,13 +434,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
@@ -470,10 +481,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
@@ -482,12 +493,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
@@ -496,8 +507,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
@@ -505,7 +516,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
@@ -513,12 +524,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 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
@@ -526,14 +537,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
@@ -541,11 +552,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
@@ -553,8 +564,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 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 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
@@ -570,8 +581,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")
@@ -588,18 +599,53 @@ EOF
expect(result.stdout).to eq "" expect(result.stdout).to eq ""
end end
unless ENV["omit_gdc_tests"] unless RUBY_PLATFORM =~ /mingw|msys/
it "supports building D sources" 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!"
end
end
it "supports building D sources with ldc2" do
test_dir("d")
result = run_rscons(rsconscript: "build-ldc2.rb")
expect(result.stderr).to eq ""
slines = lines(result.stdout)
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.d.o(bj)? -deps=build/e.1/main.d.o(bj)?.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o(bj)?.mf mod.d}])
verify_lines(slines, [%r{ldc2 -of hello-d.exe build/e.1/main.d.o(bj)? build/e.1/mod.d.o(bj)?}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!" expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
end end
it "rebuilds D modules with ldc2 when deep dependencies change" do
test_dir("d")
result = run_rscons(rsconscript: "build-ldc2.rb")
expect(result.stderr).to eq ""
slines = lines(result.stdout)
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.d.o(bj)? -deps=build/e.1/main.d.o(bj)?.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o(bj)?.mf mod.d}])
verify_lines(slines, [%r{ldc2 -of hello-d.exe build/e.1/main.d.o(bj)? build/e.1/mod.d.o(bj)?}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
contents = File.read("mod.d", mode: "rb").sub("42", "33")
File.open("mod.d", "wb") do |fh|
fh.write(contents)
end
result = run_rscons(rsconscript: "build-ldc2.rb")
expect(result.stderr).to eq ""
slines = lines(result.stdout)
verify_lines(slines, [%r{ldc2 -c -of build/e.1/main.d.o(bj)? -deps=build/e.1/main.d.o(bj)?.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o(bj)?.mf mod.d}])
verify_lines(slines, [%r{ldc2 -of hello-d.exe build/e.1/main.d.o(bj)? build/e.1/mod.d.o(bj)?}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 33!"
end
unless 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")
@@ -614,18 +660,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
@@ -633,13 +679,9 @@ EOF
test_dir("shared_library") test_dir("shared_library")
result = run_rscons(rsconscript: "shared_library_d.rb") result = run_rscons(rsconscript: "shared_library_d.rb")
# Currently gdc produces an error while trying to build the shared expect(result.stderr).to eq ""
# library. Since this isn't really an rscons problem, I'm commenting out
# this check. I'm not sure what I want to do about D support at this
# point anyway...
#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}])
@@ -665,7 +707,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
@@ -673,7 +715,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
@@ -691,23 +733,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
@@ -715,9 +757,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
@@ -725,9 +767,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
@@ -737,7 +779,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
@@ -745,7 +787,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
@@ -760,7 +802,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
@@ -798,8 +840,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
@@ -880,7 +922,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
@@ -890,7 +932,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
@@ -902,7 +944,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
@@ -921,7 +963,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}])
@@ -931,7 +973,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
@@ -998,7 +1040,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 /^Run.*-F.*to see the failed command/ expect(result.stderr).to match /^Use.*-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])
@@ -1062,7 +1104,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 ""
@@ -1395,6 +1437,18 @@ 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
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")
@@ -1488,7 +1542,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 simple.o.deppy simple.c},
]) ])
end end
@@ -1512,7 +1566,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
@@ -1531,7 +1585,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}])
@@ -1542,7 +1596,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
@@ -1675,7 +1729,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
@@ -1718,7 +1772,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
@@ -1739,6 +1793,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")
@@ -1746,6 +1801,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"
@@ -1761,13 +1817,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 "$@"]
@@ -1776,6 +1833,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"
@@ -1799,7 +1857,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
@@ -1835,6 +1893,21 @@ EOF
expect(result.status).to eq 0 expect(result.status).to eq 0
expect(result.stdout).to_not match /-DHAVE_/ expect(result.stdout).to_not match /-DHAVE_/
end end
it "modifies CPPPATH based on check_cpppath" do
test_dir "configure"
FileUtils.mkdir_p("usr1")
FileUtils.mkdir_p("usr2")
File.open("usr2/frobulous.h", "wb") do |fh|
fh.puts("#define FOO 42")
end
result = run_rscons(rsconscript: "check_c_header_cpppath.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
result = run_rscons(rsconscript: "check_c_header_cpppath.rb", rscons_args: %w[-v])
expect(result.stdout).to_not match %r{-I./usr1}
expect(result.stdout).to match %r{-I./usr2}
end
end end
context "check_cxx_header" do context "check_cxx_header" do
@@ -1849,7 +1922,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
@@ -1861,6 +1934,21 @@ EOF
expect(result.status).to eq 0 expect(result.status).to 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
it "modifies CPPPATH based on check_cpppath" do
test_dir "configure"
FileUtils.mkdir_p("usr1")
FileUtils.mkdir_p("usr2")
File.open("usr2/frobulous.h", "wb") do |fh|
fh.puts("#define FOO 42")
end
result = run_rscons(rsconscript: "check_cxx_header_cpppath.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
result = run_rscons(rsconscript: "check_cxx_header_cpppath.rb", rscons_args: %w[-v])
expect(result.stdout).to_not match %r{-I./usr1}
expect(result.stdout).to match %r{-I./usr2}
end
end end
context "check_d_import" do context "check_d_import" do
@@ -1875,7 +1963,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
@@ -1887,6 +1975,21 @@ EOF
expect(result.status).to eq 0 expect(result.status).to 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
it "modifies D_IMPORT_PATH based on check_d_import_path" do
test_dir "configure"
FileUtils.mkdir_p("usr1")
FileUtils.mkdir_p("usr2")
File.open("usr2/frobulous.d", "wb") do |fh|
fh.puts("int foo = 42;")
end
result = run_rscons(rsconscript: "check_d_import_d_import_path.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
result = run_rscons(rsconscript: "check_d_import_d_import_path.rb", rscons_args: %w[-v])
expect(result.stdout).to_not match %r{-I./usr1}
expect(result.stdout).to match %r{-I./usr2}
end
end end
context "check_lib" do context "check_lib" do
@@ -1901,7 +2004,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
@@ -1941,6 +2044,23 @@ EOF
expect(result.stdout).to match /Checking for library 'm'... found/ expect(result.stdout).to match /Checking for library 'm'... found/
expect(result.stdout).to_not match /-lm/ expect(result.stdout).to_not match /-lm/
end end
it "modifies LIBPATH based on check_libpath" do
test_dir "configure"
FileUtils.mkdir_p("usr1")
FileUtils.mkdir_p("usr2")
result = run_rscons(rsconscript: "check_lib_libpath1.rb")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
result = run_rscons(rsconscript: "check_lib_libpath2.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
result = run_rscons(rsconscript: "check_lib_libpath2.rb")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match %r{-L\./usr2}
expect(result.stdout).to_not match %r{-L\./usr1}
end
end end
context "check_program" do context "check_program" do
@@ -1952,10 +2072,20 @@ EOF
expect(result.stdout).to match /Checking for program 'find'... .*find/ expect(result.stdout).to match /Checking for program 'find'... .*find/
end end
context "with non-existent PATH entries" do
it "succeeds when the requested program is found" do
test_dir "configure"
result = run_rscons(rsconscript: "check_program_success.rb", op: "configure", path: "/foo/bar")
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match /Checking for program 'find'... .*find/
end
end
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
@@ -1987,7 +2117,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
@@ -2024,6 +2154,84 @@ EOF
end end
end end
context "custom_check" do
context "when running a test command" do
context "when executing the command fails" do
context "when failures are fatal" do
it "fails configuration with the correct error message" do
test_dir "configure"
create_exe "grep", "exit 4"
result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure")
expect(result.stderr).to match /Configuration failed/
expect(result.stdout).to match /Checking 'grep' version\.\.\. error executing grep/
expect(result.status).to_not eq 0
end
end
context "when the custom logic indicates a failure" do
it "fails configuration with the correct error message" do
test_dir "configure"
create_exe "grep", "echo 'grep (GNU grep) 1.1'"
result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure")
expect(result.stderr).to match /Configuration failed/
expect(result.stdout).to match /Checking 'grep' version\.\.\. too old!/
expect(result.status).to_not eq 0
end
end
end
context "when failures are not fatal" do
context "when the custom logic indicates a failure" do
it "displays the correct message and does not fail configuration" do
test_dir "configure"
create_exe "grep", "echo 'grep (GNU grep) 2.1'"
result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.stdout).to match /Checking 'grep' version\.\.\. we'll work with it but you should upgrade/
expect(result.status).to eq 0
result = run_rscons(rsconscript: "custom_config_check.rb", op: "build")
expect(result.stderr).to eq ""
expect(result.stdout).to match /GREP_WORKAROUND/
expect(result.status).to eq 0
end
end
end
context "when the custom logic indicates success" do
it "passes configuration with the correct message" do
test_dir "configure"
create_exe "grep", "echo 'grep (GNU grep) 3.0'"
result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.stdout).to match /Checking 'grep' version\.\.\. good!/
expect(result.status).to eq 0
result = run_rscons(rsconscript: "custom_config_check.rb", op: "build")
expect(result.stderr).to eq ""
expect(result.stdout).to match /GREP_FULL/
expect(result.status).to eq 0
end
end
it "allows passing standard input data to the executed command" do
test_dir "configure"
result = run_rscons(rsconscript: "custom_config_check.rb", op: "configure")
expect(result.stderr).to eq ""
expect(result.stdout).to match /Checking sed -E flag\.\.\. good/
expect(result.status).to eq 0
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'"
@@ -2034,8 +2242,8 @@ EOF
expect(result.stdout).to match /Setting build directory\.\.\. bb/ expect(result.stdout).to match /Setting build directory\.\.\. bb/
expect(result.stdout).to match %r{Setting prefix\.\.\. /my/prefix} expect(result.stdout).to match %r{Setting prefix\.\.\. /my/prefix}
expect(result.stdout).to match /Checking for C compiler\.\.\. gcc/ expect(result.stdout).to match /Checking for C compiler\.\.\. gcc/
expect(result.stdout).to match /Checking for C\+\+ compiler\.\.\. g++/ expect(result.stdout).to match /Checking for C\+\+ compiler\.\.\. g\+\+/
expect(result.stdout).to match /Checking for D compiler\.\.\. gdc/ expect(result.stdout).to match /Checking for D compiler\.\.\. (gdc|ldc2)/
expect(result.stdout).to match /Checking for package 'mypackage'\.\.\. found/ expect(result.stdout).to match /Checking for package 'mypackage'\.\.\. found/
expect(result.stdout).to match /Checking for C header 'stdio.h'\.\.\. found/ expect(result.stdout).to match /Checking for C header 'stdio.h'\.\.\. found/
expect(result.stdout).to match /Checking for C\+\+ header 'iostream'\.\.\. found/ expect(result.stdout).to match /Checking for C\+\+ header 'iostream'\.\.\. found/
@@ -2062,11 +2270,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
@@ -2175,7 +2385,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")
@@ -2270,13 +2480,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
+21 -5
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
@@ -121,7 +121,10 @@ EOF
before(:each) do before(:each) do
stub_const("File::PATH_SEPARATOR", ";") stub_const("File::PATH_SEPARATOR", ";")
stub_const("RbConfig::CONFIG", "host_os" => "mingw") stub_const("RbConfig::CONFIG", "host_os" => "mingw")
expect(ENV).to receive(:[]).with("PATH").and_return("C:\\bin;C:\\Windows") expect(ENV).to receive(:[]).with("PATH").and_return("C:\\none;C:\\bin;C:\\Windows")
allow(File).to receive(:directory?).with("C:\\none").and_return(false)
allow(File).to receive(:directory?).with("C:\\bin").and_return(true)
allow(File).to receive(:directory?).with("C:\\Windows").and_return(true)
allow(Dir).to receive(:entries).with("C:\\bin").and_return(%w[one.com]) allow(Dir).to receive(:entries).with("C:\\bin").and_return(%w[one.com])
allow(Dir).to receive(:entries).with("C:\\Windows").and_return(%w[two.exe Three.bat]) allow(Dir).to receive(:entries).with("C:\\Windows").and_return(%w[two.exe Three.bat])
end end
@@ -197,12 +200,12 @@ EOF
end end
end end
describe ".parse_makefile_deps" do describe ".parse_dependency_file" do
it 'handles dependencies on one line' do it 'handles dependencies on one line' do
expect(File).to receive(:read).with('makefile').and_return(<<EOS) expect(File).to receive(:read).with('makefile').and_return(<<EOS)
module.o: source.cc module.o: source.cc
EOS EOS
expect(Util.parse_makefile_deps('makefile')).to eq ['source.cc'] expect(Util.parse_dependency_file('makefile')).to eq ['source.cc']
end end
it 'handles dependencies split across many lines' do it 'handles dependencies split across many lines' do
@@ -211,9 +214,22 @@ module.o: module.c \\
module.h \\ module.h \\
other.h other.h
EOS EOS
expect(Util.parse_makefile_deps('makefile')).to eq [ expect(Util.parse_dependency_file('makefile')).to eq [
'module.c', 'module.h', 'other.h'] 'module.c', 'module.h', 'other.h']
end end
it "parses ldc2-style dependency lines" do
expect(File).to receive(:read).with("deps.out").and_return(<<EOS)
std.traits (/usr/lib/ldc/x86_64-linux-gnu/include/d/std/traits.d) : private : std.meta (/usr/lib/ldc/x86_64-linux-gnu/include/d/std/meta.d):staticIndexOf
std.conv (/usr/lib/ldc/x86_64-linux-gnu/include/d/std/conv.d) : private : std.array (/usr/lib/ldc/x86_64-linux-gnu/include/d/std/array.d):array
std.stdio (/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d) : private static : core.sys.posix.sys.socket (/usr/lib/ldc/x86_64-linux-gnu/include/d/core/sys/posix/sys/socket.d) -> sock
EOS
expect(Util.parse_dependency_file("deps.out")).to eq [
"/usr/lib/ldc/x86_64-linux-gnu/include/d/std/meta.d",
"/usr/lib/ldc/x86_64-linux-gnu/include/d/std/array.d",
"/usr/lib/ldc/x86_64-linux-gnu/include/d/core/sys/posix/sys/socket.d",
]
end
end end
end end
-10
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)