Compare commits

..
50 Commits
Author SHA1 Message Date
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
51 changed files with 988 additions and 250 deletions

No files matched your search

+48
View File
@@ -1,3 +1,51 @@
## 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
### New Features
+4
View File
@@ -1,6 +1,10 @@
# Rscons
![rscons logo](img/rscons_logo_1000.png)
Rscons (https://github.com/holtrop/rscons) is an open-source build system
for developers.
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.
+8 -1
View File
@@ -11,7 +11,7 @@ require "rake/clean"
require "fileutils"
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
sh "ruby rb/build_dist.rb"
@@ -93,3 +93,10 @@ task :user_guide do
end
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
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
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,10 @@
configure do
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
@@ -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
+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")
env["build_root"] = env.build_root
env["inc_c"] = "inc.c"
env.build_after("${build_root}/program.o", "${inc_c}")
env.Program("program.exe", ["program.c", "inc.c"])
env.Object("program.o", "program.c")
env.build_after("program.o", "${inc_c}")
env.Program("program.exe", ["program.o", "inc.c"])
end
end
+3 -2
View File
@@ -4,8 +4,9 @@ build do
env["inc_h"] = "inc.h"
env.Copy("copy_inc.h", "${inc_h}")
env.depends("${build_root}/program.o", "${inc_h}")
env.Program("program.exe", ["program.c", "inc.c"])
env.depends("program.o", "${inc_h}")
env.Object("program.o", "program.c")
env.Program("program.exe", ["program.o", "inc.c"])
inc_c = env.Command("inc.c",
[],
+3 -2
View File
@@ -4,8 +4,9 @@ build do
env["inc_h"] = "inc.h"
env.Copy("copy_inc.h", "${inc_h}")
env.depends("${build_root}/program.o", "${inc_h}")
env.Program("program.exe", ["program.c", "inc.c"])
env.depends("program.o", "${inc_h}")
env.Object("program.o", "program.c")
env.Program("program.exe", ["program.o", "inc.c"])
env.Command("inc.c",
[],
+4
View File
@@ -1,3 +1,7 @@
configure do
check_d_compiler "gdc"
end
build do
Environment.new(echo: :command) do |env|
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
@@ -0,0 +1,6 @@
build do
Environment.new do |env|
env.Program("program.exe", "program.c")
env.Install("install/program.exe", "program.exe")
end
end
@@ -0,0 +1,8 @@
#include <stdio.h>
#include <unistd.h>
int main(int argc, char * argv[])
{
sleep(2);
return 0;
}
+1
View File
@@ -4,6 +4,7 @@ build do
require 'yaml'
env.add_builder(:JsonToYaml) do |params|
unless @cache.up_to_date?(@target, :JsonToYaml, @sources, @env)
print_run_message("JsonToYaml #{@target}", nil)
@cache.mkdir_p(File.dirname(@target))
File.open(@target, 'w') do |f|
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
Environment.new do |env|
env["LIBS"] += ["c"]
env["LIBS"] += ["m"]
env.Program('simple.exe', Dir['*.c'])
end
end
+2 -2
View File
@@ -2,9 +2,9 @@ build do
Environment.new(echo: :command) do |env|
env.append('CPPPATH' => glob('src/**/*/'))
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"
elsif File.basename(builder.target) == "two.o"
elsif File.basename(builder.sources.first) == "two.c"
builder.vars["CFLAGS"] << "-O2"
end
end
+201 -9
View File
@@ -8,7 +8,7 @@ It supports the following features:
* built-in builders for several common operations
* out-of-the-box support for C, C++, and D languages
* 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
* 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/).
${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
### Build Correctness
@@ -89,7 +98,8 @@ The only dependency required to run Rscons is to have a Ruby interpreter
installed.
The latest release can be downloaded from [https://github.com/holtrop/rscons/releases](https://github.com/holtrop/rscons/releases).
Simply copy the `rscons` executable script into the desired location within
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
@@ -131,6 +141,7 @@ Such configuration checks can include:
* verifying presence of a D import
* verifying presence of a library
* verifying presence of an executable
* any custom user-supplied configuration check
##> 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
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
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.
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
supported language:
@@ -237,7 +260,7 @@ Example calls:
```ruby
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_cxx_header "memory"
end
@@ -245,10 +268,16 @@ end
#### Options
##### `:check_cpppath`
Optionally specifies an array of paths to look for the header file in.
##### `:fail`
If the `:fail` option is set to `false`, then the absence of the header file
will not result in the configure option failing.
The `:fail` option defaults to `true` if the `:set_define` option is not
defined, and defaults to `false` if the `:set_define` option is defined.
##### `:set_define`
@@ -270,6 +299,12 @@ configure do
end
```
#### Options
##### `:check_d_import_path`
Optionally specifies an array of paths to look for the module in.
###> Checking for a Library
The `check_lib` method can be used to check for the presence of a library.
@@ -288,16 +323,28 @@ end
#### Options
##### `:check_libpath`
Optionally specifies an array of paths to look for the library in.
##### `:fail`
If the `:fail` option is set to `false`, then the absence of the library
will not result in the configure option failing.
The `:fail` option defaults to `true` if the `:set_define` option is not
defined, and defaults to `false` if the `:set_define` option is defined.
##### `:set_define`
If set, a build define of the specified String will be added to the
`CPPDEFINES` construction variable array if the requested library is found.
##### `:use`
If not set, the library will be used by default in all `Environment` objects.
If set, the library will only be used in `Environment` objects that have a
matching `:use` flag set.
###> Checking for a Program
The `check_program` method can check for the existence of an executable in the
@@ -344,12 +391,72 @@ for configuration flags.
If the `:fail` option is set to `false`, then the absence of the package or
program requested will not result in the configure option failing.
The `:fail` option defaults to `true` if the `:set_define` option is not
defined, and defaults to `false` if the `:set_define` option is defined.
##### `:set_define`
If set, a build define of the specified String will be added to the
`CPPDEFINES` construction variable array if the requested package is found.
##### `:use`
If not set, the library will be used by default in all `Environment` objects.
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
```
##> Build Operations
The `build` block is used to create Environments and register build targets.
@@ -399,7 +506,8 @@ files.
###> 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.
Example use:
@@ -622,6 +730,24 @@ A platform-dependent program suffix will be appended to the target name if one
is not specified.
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
```ruby
@@ -638,6 +764,24 @@ they are not specified by the user.
These values can be controlled by overriding the `SHLIBPREFIX` and
`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
```ruby
@@ -703,6 +847,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.
###> 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
### Adding New Languages
@@ -869,7 +1025,7 @@ Here is our Custom builder example extended to print its status:
```ruby
class Rscons::Builders::Custom < Rscons::Builder
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|
fh.write("Target file created.")
end
@@ -894,7 +1050,7 @@ Here is a Custom builder which combines its source files similar to what the
class Rscons::Builders::Custom < Rscons::Builder
def run(options)
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|
@sources.each do |source|
fh.write(File.read(source, mode: "rb"))
@@ -952,9 +1108,9 @@ the `Rscons::Command` class and then `wait_for` the Command object.
However, this is a common enough case that Rscons provides a few
convenience methods to handle this:
* `Rscons::Builder#register_command`
* `Rscons::Builder#standard_command`
* `Rscons::Builder#finalize_command`
* [`Rscons::Builder#register_command`](../yard/Rscons/Builder.html#register_command-instance_method)
* [`Rscons::Builder#standard_command`](../yard/Rscons/Builder.html#standard_command-instance_method)
* [`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
and wait for it to complete.
@@ -1104,6 +1260,42 @@ build do
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
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

-10
View File
@@ -64,16 +64,6 @@ module Rscons
target.is_a?(Symbol)
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 [Array<String>] The shell and flag.
+1
View File
@@ -3,6 +3,7 @@ module Rscons
module Ansi
class << self
# ANSI terminal reset sequence.
RESET = "\e[0m"
# Write a message to an IO with ANSI escape codes.
+3
View File
@@ -75,6 +75,8 @@ module Rscons
end
if rv == 0
build(operation_options)
else
rv
end
when "clean"
clean
@@ -192,6 +194,7 @@ module Rscons
begin
@script.configure(co)
rescue ConfigureOp::ConfigureFailure
Ansi.write($stderr, :red, "Configuration failed", :reset, "\n")
rv = 1
end
co.close(rv == 0)
+3 -2
View File
@@ -4,10 +4,11 @@ module Rscons
# Create a BasicEnvironment object.
#
# @api private
#
# @param options [Hash]
# 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 = {})
@varset = VarSet.new(Rscons::DEFAULT_CONSTRUCTION_VARIABLES)
load_configuration_data!(options)
+1
View File
@@ -57,6 +57,7 @@ module Rscons
printed_message = true
end
@cache.mkdir_p(File.dirname(dest), install: @install_builder)
FileUtils.rm_f(dest)
FileUtils.cp(src, dest, :preserve => true)
end
@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
deps = @sources
if File.exists?(@vars["_DEPFILE"])
deps += Util.parse_makefile_deps(@vars["_DEPFILE"])
deps += Util.parse_dependency_file(@vars["_DEPFILE"])
end
@cache.register_build(@target, @command, deps.uniq, @env)
true
+2 -1
View File
@@ -8,6 +8,7 @@ module Rscons
module Builders
module Mixins
# Mixin for builders needing to build object files.
module Object
include Depfile
@@ -77,7 +78,7 @@ module Rscons
if @vars[:direct]
@env.get_build_fname(target, depfilesuffix, self.class)
else
Rscons.set_suffix(target, depfilesuffix)
"#{target}#{depfilesuffix}"
end
@cache.mkdir_p(File.dirname(@vars["_DEPFILE"]))
command = @env.build_command(@command_template, @vars)
+1 -1
View File
@@ -23,7 +23,7 @@ module Rscons
@vars["_PREPROCESS_DEPGEN"] = depgen
@vars["_TARGET"] = @target
@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)
self.produces(@vars["_DEPFILE"])
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
class << self
# Custom new method which will delegate to the correct class depending
# on the options specified.
def new(options, *more)
unless File.basename(options[:target])["."]
options[:target] += options[:env].expand_varref("${PROGSUFFIX}", options[:vars])
+2
View File
@@ -8,6 +8,8 @@ module Rscons
include Mixins::Program
class << self
# Custom new method which will delegate to the correct class depending
# on the options specified.
def new(options, *more)
libprefix = options[:env].expand_varref("${SHLIBPREFIX}", options[:vars])
unless File.basename(options[:target]).start_with?(libprefix)
+2
View File
@@ -7,6 +7,8 @@ module Rscons
include Mixins::Object
class << self
# Content component to add to build path to separate objects built
# using this builder from others.
def extra_path
"_shared"
end
+2 -1
View File
@@ -1,11 +1,12 @@
require "rscons"
require "optparse"
# CLI usage string.
USAGE = <<EOF
Usage: #{$0} [global options] [operation] [operation 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
-h, --help Show rscons help and exit
-r COLOR, --color=COLOR Set color mode (off, auto, force)
+198 -103
View File
@@ -69,7 +69,7 @@ module Rscons
cc = ccc.find do |cc|
test_c_compiler(cc)
end
common_config_checks(cc ? 0 : 1, success_message: cc)
complete(cc ? 0 : 1, success_message: cc)
end
# Check for a working C++ compiler.
@@ -87,7 +87,7 @@ module Rscons
cc = ccc.find do |cc|
test_cxx_compiler(cc)
end
common_config_checks(cc ? 0 : 1, success_message: cc)
complete(cc ? 0 : 1, success_message: cc)
end
# Check for a working D compiler.
@@ -105,7 +105,7 @@ module Rscons
dc = cdc.find do |dc|
test_d_compiler(dc)
end
common_config_checks(dc ? 0 : 1, success_message: dc)
complete(dc ? 0 : 1, success_message: dc)
end
# Check for a package or configure program output.
@@ -122,11 +122,12 @@ module Rscons
if status == 0
store_parse(stdout, options)
end
common_config_checks(status, options)
complete(status, options)
end
# Check for a C header.
def check_c_header(header_name, options = {})
check_cpppath = [nil] + (options[:check_cpppath] || [])
Ansi.write($stdout, "Checking for C header '", :cyan, header_name, :reset, "'... ")
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
fh.puts <<-EOF
@@ -139,15 +140,30 @@ module Rscons
vars = {
"LD" => "${CC}",
"_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)
common_config_checks(status, options)
if status == 0
if cpppath
store_append({"CPPPATH" => Array(cpppath)}, options)
end
break
end
end
complete(status, options)
end
# Check for a C++ header.
def check_cxx_header(header_name, options = {})
check_cpppath = [nil] + (options[:check_cpppath] || [])
Ansi.write($stdout, "Checking for C++ header '", :cyan, header_name, :reset, "'... ")
File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh|
fh.puts <<-EOF
@@ -160,15 +176,30 @@ module Rscons
vars = {
"LD" => "${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)
common_config_checks(status, options)
if status == 0
if cpppath
store_append({"CPPPATH" => Array(cpppath)}, options)
end
break
end
end
complete(status, options)
end
# Check for a D import.
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, "'... ")
File.open("#{@work_dir}/cfgtest.d", "wb") do |fh|
fh.puts <<-EOF
@@ -181,15 +212,30 @@ module Rscons
vars = {
"LD" => "${DC}",
"_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)
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
# Check for a library.
def check_lib(lib, options = {})
check_libpath = [nil] + (options[:check_libpath] || [])
Ansi.write($stdout, "Checking for library '", :cyan, lib, :reset, "'... ")
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
fh.puts <<-EOF
@@ -204,19 +250,150 @@ module Rscons
"_SOURCES" => "#{@work_dir}/cfgtest.c",
"_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)
if status == 0
if libpath
store_append({"LIBPATH" => Array(libpath)}, options)
end
break
end
end
if status == 0
store_append({"LIBS" => [lib]}, options)
end
common_config_checks(status, options)
complete(status, options)
end
# Check for a executable program.
def check_program(program, options = {})
Ansi.write($stdout, "Checking for program '", :cyan, program, :reset, "'... ")
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
# Execute a test command and log the result.
#
# @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
@log_fh.puts("Command: #{command.join(" ")}")
stdout, stderr, status = Open3.capture3(*command, stdin_data: options[:stdin])
@log_fh.puts("Exit status: #{status.to_i}")
@log_fh.write(stdout)
@log_fh.write(stderr)
[stdout, stderr, status]
rescue Errno::ENOENT
["", "", 127]
end
end
# Store construction variables for merging into the Cache.
#
# @param vars [Hash]
# Hash containing the variables to merge.
# @param options [Hash]
# 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 = {})
store_vars = store_common(options)
store_vars["merge"] ||= {}
vars.each_pair do |key, value|
store_vars["merge"][key] = value
end
end
# Store construction variables for appending into the Cache.
#
# @param vars [Hash]
# Hash containing the variables to append.
# @param options [Hash]
# 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 = {})
store_vars = store_common(options)
store_vars["append"] ||= {}
vars.each_pair do |key, value|
if store_vars["append"][key].is_a?(Array) and value.is_a?(Array)
store_vars["append"][key] += value
else
store_vars["append"][key] = value
end
end
end
# Store flags to be parsed into the Cache.
#
# @param flags [String]
# String containing the flags to parse.
# @param options [Hash]
# 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 = {})
store_vars = store_common(options)
store_vars["parse"] ||= []
store_vars["parse"] << flags
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
if should_fail
Ansi.write($stdout, :red, "#{fail_message}\n")
raise ConfigureFailure.new
else
Ansi.write($stdout, :yellow, "#{fail_message}\n")
end
end
end
private
@@ -309,7 +486,12 @@ module Rscons
"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},
"DDEPGEN" => ["-deps=${_DEPFILE}"],
}
if RUBY_PLATFORM =~ /mingw/
# ldc2 on Windows expect an object file suffix of .obj.
merge["OBJSUFFIX"] = %w[.obj]
end
end
_, _, status = log_and_test_command(command)
if status == 0
@@ -322,64 +504,6 @@ module Rscons
end
end
# Execute a test command and log the result.
def log_and_test_command(command)
begin
@log_fh.puts("Command: #{command.join(" ")}")
stdout, stderr, status = Open3.capture3(*command)
@log_fh.puts("Exit status: #{status.to_i}")
@log_fh.write(stdout)
@log_fh.write(stderr)
[stdout, stderr, status]
rescue Errno::ENOENT
["", "", 127]
end
end
# Store construction variables for merging into the Cache.
#
# @param vars [Hash]
# Hash containing the variables to merge.
# @param options [Hash]
# Options.
def store_merge(vars, options = {})
store_vars = store_common(options)
store_vars["merge"] ||= {}
vars.each_pair do |key, value|
store_vars["merge"][key] = value
end
end
# Store construction variables for appending into the Cache.
#
# @param vars [Hash]
# Hash containing the variables to append.
# @param options [Hash]
# Options.
def store_append(vars, options = {})
store_vars = store_common(options)
store_vars["append"] ||= {}
vars.each_pair do |key, value|
if store_vars["append"][key].is_a?(Array) and value.is_a?(Array)
store_vars["append"][key] += value
else
store_vars["append"][key] = value
end
end
end
# Store flags to be parsed into the Cache.
#
# @param flags [String]
# String containing the flags to parse.
# @param options [Hash]
# Options.
def store_parse(flags, options = {})
store_vars = store_common(options)
store_vars["parse"] ||= []
store_vars["parse"] << flags
end
# Common functionality for all store methods.
#
# @param options [Hash]
@@ -403,34 +527,5 @@ module Rscons
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
+5 -2
View File
@@ -59,6 +59,9 @@ module Rscons
# :command, :short, or :off (default :short)
# @option options [Boolean] :exclude_builders
# Whether to omit adding default builders (default false)
# @option options [String, 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
# when the block returns, the {#process} method is automatically called.
@@ -248,7 +251,7 @@ module Rscons
if extra_path = builder_class.extra_path
extra_path = "/#{extra_path}"
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
# Build all build targets specified in the Environment.
@@ -284,7 +287,7 @@ module Rscons
unless @process_failures.empty?
msg = @process_failures.join("\n")
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
raise BuildError.new(msg)
end
+15
View File
@@ -3,6 +3,7 @@ module Rscons
# The Script class encapsulates the state of a build script.
class Script
# DSL available to the Rsconscript.
class Dsl
# Create a Dsl.
def initialize(script)
@@ -57,6 +58,7 @@ module Rscons
end
end
# DSL available to the 'configure' block.
class ConfigureDsl
# Create a ConfigureDsl.
#
@@ -81,6 +83,19 @@ module Rscons
@configure_op.__send__(method_name, *args)
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
# @return [String, nil]
+10 -3
View File
@@ -1,4 +1,5 @@
module Rscons
# A collection of stand-alone utility methods.
module Util
class << self
@@ -159,7 +160,7 @@ module Rscons
end
end
# Parse dependencies from a Makefile.
# Parse dependencies from a Makefile or ldc2 dependency file.
#
# This method is used internally by Rscons builders.
#
@@ -168,7 +169,7 @@ module Rscons
#
# @return [Array<String>]
# Paths of dependency files.
def parse_makefile_deps(mf_fname)
def parse_dependency_file(mf_fname)
deps = []
buildup = ''
File.read(mf_fname).each_line do |line|
@@ -176,7 +177,11 @@ module Rscons
buildup += ' ' + $1
else
buildup += ' ' + line
if buildup =~ /^.*: (.*)$/
if buildup =~ /^[^:]+\(\S+\)\s*:.*?:[^:]+\((\S+)\)/
# ldc2-style dependency line
deps << $1
elsif buildup =~ /^.*: (.*)$/
# Makefile-style dependency line
mf_deps = $1
deps += mf_deps.split(' ').map(&:strip)
end
@@ -199,6 +204,7 @@ module Rscons
File.file?(path) and File.executable?(path)
end
if RbConfig::CONFIG["host_os"] =~ /mswin|windows|mingw/i
if File.directory?(path_entry)
executable = executable.downcase
dir_entries = Dir.entries(path_entry)
dir_entries.find do |entry|
@@ -211,6 +217,7 @@ module Rscons
return path
end
end
end
else
path = "#{path_entry}/#{executable}"
return path if is_executable[path]
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons
# Project version.
VERSION = "2.0.0beta1"
VERSION = "2.0.2"
end
+6 -1
View File
@@ -1,6 +1,6 @@
<html>
<head>
<title>RScons User Guide<%= subpage_title %> - Version <%= Rscons::VERSION %></title>
<title>Rscons User Guide<%= subpage_title %> - Version <%= Rscons::VERSION %></title>
<style>
body {
background-color: #CCC;
@@ -31,6 +31,11 @@
.separator {
height: 2em;
}
.img_block_center {
display: block;
margin-left: auto;
margin-right: auto;
}
.page_nav {
width: 100%;
table-layout: fixed;
+7 -5
View File
@@ -59,9 +59,10 @@ combined_file.each do |line|
end
require "zlib"
require "base64"
compressed_script = Zlib::Deflate.deflate(stripped_comments.join)
escaped_compressed_script = compressed_script.gsub("#", "#1").gsub("\n", "#2").gsub("\r", "#3").gsub("\0", "#4")
hash = Digest::MD5.hexdigest(escaped_compressed_script)
encoded_compressed_script = Base64.encode64(compressed_script).gsub("\n", "")
hash = Digest::MD5.hexdigest(encoded_compressed_script)
FileUtils.mkdir_p(DIST)
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")
unless File.exists?(script)
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 "base64"
encoded_compressed = $1
unescaped_compressed = Base64.decode64(encoded_compressed)
inflated = Zlib::Inflate.inflate(unescaped_compressed)
File.open(script, "wb") do |fh|
fh.write(inflated)
@@ -85,6 +87,6 @@ load script
if __FILE__ == $0
Rscons::Cli.run(ARGV)
end
#==>#{escaped_compressed_script}
#==>#{encoded_compressed_script}
EOF
end
+14 -1
View File
@@ -59,6 +59,8 @@ class Generator
line = @lines.slice!(0)
if line =~ /^```(.*)$/
@sections.last.append(gather_code_section($1))
elsif line.chomp == "${remove}"
remove_section
elsif line =~ /^(#+)(>)?\s*(.*)$/
level_text, new_page_text, title_text = $1, $2, $3
level = $1.size
@@ -152,7 +154,8 @@ class Generator
end
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|
indent = section.number.split(".").size - 1
toc_content += %[<span style="padding-left: #{4 * indent}ex;">]
@@ -196,6 +199,15 @@ class Generator
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)
if @current_section_number.size == level - 1
@current_section_number << 1
@@ -212,5 +224,6 @@ end
input = load_file("doc/user_guide.md")
FileUtils.rm_rf("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", true)
+288 -84
View File
@@ -65,6 +65,9 @@ describe Rscons do
def create_exe(exe_name, contents)
exe_file = "#{@build_test_run_dir}/_bin/#{exe_name}"
if RUBY_PLATFORM =~ /mingw/
exe_file += ".bat"
end
File.open(exe_file, "wb") do |fh|
fh.puts("#!/bin/sh")
fh.puts(contents)
@@ -142,7 +145,11 @@ EOF
stdout, stderr, status = nil, nil, nil
Bundler.with_clean_env do
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)
File.open("#{@build_test_run_dir}/.stdout", "wb") do |fh|
fh.write(stdout)
@@ -188,7 +195,7 @@ EOF
test_dir('simple')
result = run_rscons
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"
end
@@ -205,8 +212,8 @@ EOF
result = run_rscons(rsconscript: "command.rb")
expect(result.stderr).to eq ""
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 -o simple.exe build/e.1/simple.o},
%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.c.o},
])
end
@@ -224,7 +231,7 @@ EOF
test_dir('header')
result = run_rscons
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"
end
@@ -274,13 +281,13 @@ EOF
result = run_rscons(rsconscript: "command.rb")
expect(result.stderr).to eq ""
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 -o simple.exe build/e.1/simple.o},
%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.c.o},
])
result = run_rscons(rsconscript: "link_flag_change.rb")
expect(result.stderr).to eq ""
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
@@ -310,9 +317,9 @@ EOF
result = run_rscons(rsconscript: "carat.rb")
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{gcc -c -o build/e.1/one.o -MMD -MF build/e.1/one.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 -o program.exe build/e.1/src/two/two.o build/e.1/one.o},
%r{gcc -c -o build/e.1/one.o -MMD -MF build/e.1/one.o.mf -Isrc -Isrc/one -Isrc/two build/e.1/one.c},
%r{gcc -c -o build/e.1/src/two/two.c.o -MMD -MF build/e.1/src/two/two.c.o.mf -Isrc -Isrc/one -Isrc/two src/two/two.c},
%r{gcc -o program.exe build/e.1/src/two/two.c.o build/e.1/one.o},
])
end
@@ -330,15 +337,29 @@ EOF
expect(result.stderr).to match /Could not find a registered build target "foo"/
end
it "allows replacing a running executable when performing an 'install' operation" do
test_dir("install_running_exe")
result = run_rscons(op: "install")
expect(result.stderr).to eq ""
fcontents = File.read("program.c", mode: "rb")
File.open("program.c", "wb") do |fh|
fh.write(fcontents.sub(/2/, '3'))
end
pid = Process.spawn("install/program.exe")
result = run_rscons(op: "install")
expect(result.stderr).to eq ""
Process.wait(pid)
end
context "clean operation" do
it 'cleans built files' do
test_dir("simple")
result = run_rscons
expect(result.stderr).to eq ""
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])
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?('simple.exe')).to be_falsey
expect(File.exists?('simple.c')).to be_truthy
@@ -349,7 +370,7 @@ EOF
result = run_rscons
expect(result.stderr).to eq ""
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" }
result = run_rscons(op: %w[clean])
expect(File.exists?('build/e.1')).to be_truthy
@@ -366,12 +387,12 @@ EOF
result = run_rscons(rsconscript: "install.rb", op: %W[install])
expect(result.stderr).to eq ""
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])
expect(result.stderr).to eq ""
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
@@ -388,7 +409,7 @@ EOF
result = run_rscons(rsconscript: "install.rb", op: %W[clean])
expect(result.stderr).to eq ""
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])
expect(result.stderr).to eq ""
@@ -470,10 +491,10 @@ EOF
result = run_rscons
expect(result.stderr).to eq ""
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 -o program-debug.exe build/e.1/src/program.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 -o program-release.exe build/e.2/src/program.o},
%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.c.o},
%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.c.o},
])
end
@@ -482,12 +503,12 @@ EOF
result = run_rscons(rsconscript: "clone_all.rb")
expect(result.stderr).to eq ""
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{post build/e.1/src/program.o},
%r{gcc -o program.exe build/e.1/src/program.o},
%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.c.o},
%r{gcc -o program.exe build/e.1/src/program.c.o},
%r{post program.exe},
%r{post build/e.2/src/program.o},
%r{gcc -o program2.exe 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.c.o},
%r{post program2.exe},
])
end
@@ -496,7 +517,7 @@ EOF
test_dir('simple_cc')
result = run_rscons
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"
end
@@ -513,9 +534,9 @@ EOF
result = run_rscons
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{gcc -c -o one.o -MMD -MF one.mf -DONE one.c},
%r{gcc -c -o build/e.1/two.o -MMD -MF build/e.1/two.mf two.c},
%r{gcc -o two_sources.exe one.o build/e.1/two.o},
%r{gcc -c -o one.o -MMD -MF one.o.mf -DONE one.c},
%r{gcc -c -o build/e.1/two.c.o -MMD -MF build/e.1/two.c.o.mf two.c},
%r{gcc -o two_sources.exe one.o build/e.1/two.c.o},
])
expect(File.exists?("two_sources.exe")).to be_truthy
expect(`./two_sources.exe`).to eq "This is a C program with two sources.\n"
@@ -526,14 +547,14 @@ EOF
result = run_rscons
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{gcc -c -o build/e.1/one.o -MMD -MF build/e.1/one.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{ar rcs lib.a build/e.1/one.o build/e.1/two.o},
%r{gcc -c -o build/e.1/three.o -MMD -MF build/e.1/three.mf three.c},
%r{gcc -o library.exe lib.a build/e.1/three.o},
%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.c.o -MMD -MF build/e.1/two.c.o.mf -Dmake_lib two.c},
%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.c.o -MMD -MF build/e.1/three.c.o.mf three.c},
%r{gcc -o library.exe lib.a build/e.1/three.c.o},
])
expect(File.exists?("library.exe")).to be_truthy
expect(`ar t lib.a`).to eq "one.o\ntwo.o\n"
expect(`ar t lib.a`).to eq "one.c.o\ntwo.c.o\n"
end
it 'supports build hooks to override construction variables' do
@@ -541,9 +562,9 @@ EOF
result = run_rscons(rsconscript: "build_hooks.rb")
expect(result.stderr).to eq ""
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/two/two.o -MMD -MF build/e.1/src/two/two.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 -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.c.o -MMD -MF build/e.1/src/two/two.c.o.mf -Isrc/one -Isrc/two -O2 src/two/two.c},
%r{gcc -o build_hook.exe build/e.1/src/one/one.c.o build/e.1/src/two/two.c.o},
])
expect(`./build_hook.exe`).to eq "Hello from two()\n"
end
@@ -553,8 +574,8 @@ EOF
result = run_rscons(rsconscript: "build_hooks_override_vars.rb")
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{gcc -c -o one.o -MMD -MF one.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 one.o -MMD -MF one.o.mf -Isrc -Isrc/one -Isrc/two -O1 src/two/two.c},
%r{gcc -c -o two.o -MMD -MF two.o.mf -Isrc -Isrc/one -Isrc/two -O2 src/two/two.c},
])
expect(File.exists?('one.o')).to be_truthy
expect(File.exists?('two.o')).to be_truthy
@@ -570,7 +591,7 @@ EOF
%r{Compiling simple.c},
%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"
File.open("program.ld", "w") {|fh| fh.puts("2")}
@@ -588,18 +609,51 @@ EOF
expect(result.stdout).to eq ""
end
unless ENV["omit_gdc_tests"]
it "supports building D sources" do
it "supports building D sources with gdc" do
test_dir("d")
result = run_rscons
expect(result.stderr).to eq ""
slines = lines(result.stdout)
verify_lines(slines, [%r{gdc -c -o build/e.1/main.o -MMD -MF build/e.1/main.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 -o hello-d.exe build/e.1/main.o build/e.1/mod.o}])
verify_lines(slines, [%r{gdc -c -o build/e.1/main.d.o -MMD -MF build/e.1/main.d.o.mf main.d}])
verify_lines(slines, [%r{gdc -c -o build/e.1/mod.d.o -MMD -MF build/e.1/mod.d.o.mf mod.d}])
verify_lines(slines, [%r{gdc -o hello-d.exe build/e.1/main.d.o build/e.1/mod.d.o}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
end
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.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o.mf mod.d}])
verify_lines(slines, [%r{ldc2 -of hello-d.exe build/e.1/main.d.o(bj)? build/e.1/mod.d.o(bj)?}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
end
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.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o.mf mod.d}])
verify_lines(slines, [%r{ldc2 -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.mf main.d}])
verify_lines(slines, [%r{ldc2 -c -of build/e.1/mod.d.o(bj)? -deps=build/e.1/mod.d.o.mf mod.d}])
verify_lines(slines, [%r{ldc2 -of hello-d.exe build/e.1/main.d.o(bj)? build/e.1/mod.d.o(bj)?}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 33!"
end
unless ENV["omit_gdc_tests"]
it "links with the D linker when object files were built from D sources" do
test_dir("d")
result = run_rscons(rsconscript: "link_objects.rb")
@@ -614,18 +668,18 @@ EOF
result = run_rscons
expect(result.stderr).to eq ""
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/mod.o -MMD -MF build/e.1/mod.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 -c -o build/e.1/main.d.o -MMD -MF build/e.1/main.d.o.mf main.d}])
verify_lines(slines, [%r{gdc -c -o build/e.1/mod.d.o -MMD -MF build/e.1/mod.d.o.mf mod.d}])
verify_lines(slines, [%r{gdc -o hello-d.exe build/e.1/main.d.o build/e.1/mod.d.o}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
fcontents = File.read("mod.d", mode: "rb").sub("42", "33")
File.open("mod.d", "wb") {|fh| fh.write(fcontents)}
result = run_rscons
expect(result.stderr).to eq ""
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/mod.o -MMD -MF build/e.1/mod.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 -c -o build/e.1/main.d.o -MMD -MF build/e.1/main.d.o.mf main.d}])
verify_lines(slines, [%r{gdc -c -o build/e.1/mod.d.o -MMD -MF build/e.1/mod.d.o.mf mod.d}])
verify_lines(slines, [%r{gdc -o hello-d.exe build/e.1/main.d.o build/e.1/mod.d.o}])
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 33!"
end
@@ -633,11 +687,7 @@ EOF
test_dir("shared_library")
result = run_rscons(rsconscript: "shared_library_d.rb")
# Currently gdc produces an error while trying to build the shared
# 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 ""
expect(result.stderr).to eq ""
slines = lines(result.stdout)
if RUBY_PLATFORM =~ /mingw/
verify_lines(slines, [%r{Linking mine.dll}])
@@ -705,8 +755,8 @@ EOF
test_dir("simple")
result = run_rscons(rsconscript: "register_target_in_build_hook.rb")
expect(result.stderr).to eq ""
expect(File.exists?("build/e.1/simple.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")).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"
end
@@ -715,8 +765,8 @@ EOF
File.open("other.cccc", "w") {|fh| fh.puts}
result = run_rscons(rsconscript: "cxxsuffix.rb")
expect(result.stderr).to eq ""
expect(File.exists?("build/e.1/simple.o")).to be_truthy
expect(File.exists?("build/e.1/other.o")).to be_truthy
expect(File.exists?("build/e.1/simple.cc.o")).to be_truthy
expect(File.exists?("build/e.1/other.cccc.o")).to be_truthy
expect(`./simple.exe`).to eq "This is a simple C++ program\n"
end
@@ -725,8 +775,8 @@ EOF
FileUtils.mv("src/one/one.c", "src/one/one.yargh")
result = run_rscons(rsconscript: "csuffix.rb")
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/two/two.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.c.o")).to be_truthy
expect(`./program.exe`).to eq "Hello from two()\n"
end
@@ -798,7 +848,7 @@ EOF
test_dir("simple")
result = run_rscons(rsconscript: "cvar_array.rb")
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"
end
@@ -880,7 +930,7 @@ EOF
result = run_rscons(rsconscript: "absolute_source_path.rb")
expect(result.stderr).to eq ""
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}])
end
@@ -998,7 +1048,7 @@ EOF
result = run_rscons
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
result = run_rscons(rscons_args: %w[-F])
@@ -1395,6 +1445,18 @@ EOF
expect(result.stdout).to eq ""
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
it "prints a message when the target does not exist" do
test_dir("simple")
@@ -1488,7 +1550,7 @@ EOF
result = run_rscons(rsconscript: "override_depfilesuffix.rb")
expect(result.stderr).to eq ""
verify_lines(lines(result.stdout), [
%r{gcc -c -o simple.o -MMD -MF simple.deppy simple.c},
%r{gcc -c -o simple.o -MMD -MF simple.o.deppy simple.c},
])
end
@@ -1512,7 +1574,7 @@ EOF
test_dir("library")
result = run_rscons(rsconscript: "override_arcmd.rb")
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
it "allows passing object files as sources" do
@@ -1542,7 +1604,7 @@ EOF
test_dir "shared_library"
result = run_rscons(rsconscript: "shared_library_from_object.rb")
expect(result.stderr).to eq ""
expect(File.exists?("one.o"))
expect(File.exists?("one.c.o"))
end
end
@@ -1675,7 +1737,7 @@ EOF
create_exe "gcc", "exit 1"
create_exe "clang", "exit 1"
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.stdout).to match /Checking for C compiler\.\.\. not found/
end
@@ -1718,7 +1780,7 @@ EOF
create_exe "g++", "exit 1"
create_exe "clang++", "exit 1"
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.stdout).to match /Checking for C\+\+ compiler\.\.\. not found/
end
@@ -1761,7 +1823,7 @@ EOF
create_exe "gdc", "exit 1"
create_exe "ldc2", "exit 1"
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.stdout).to match /Checking for D compiler\.\.\. not found/
end
@@ -1799,7 +1861,7 @@ EOF
it "fails when the requested header is not found" do
test_dir "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.stdout).to match /Checking for C header 'not___found\.h'... not found/
end
@@ -1835,6 +1897,21 @@ EOF
expect(result.status).to eq 0
expect(result.stdout).to_not match /-DHAVE_/
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
context "check_cxx_header" do
@@ -1849,7 +1926,7 @@ EOF
it "fails when the requested header is not found" do
test_dir "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.stdout).to match /Checking for C\+\+ header 'not___found\.h'... not found/
end
@@ -1861,6 +1938,21 @@ EOF
expect(result.status).to eq 0
expect(result.stdout).to match /Checking for C\+\+ header 'not___found\.h'... not found/
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
context "check_d_import" do
@@ -1875,7 +1967,7 @@ EOF
it "fails when the requested import is not found" do
test_dir "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.stdout).to match /Checking for D import 'not\.found'... not found/
end
@@ -1887,6 +1979,21 @@ EOF
expect(result.status).to eq 0
expect(result.stdout).to match /Checking for D import 'not\.found'... not found/
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
context "check_lib" do
@@ -1901,7 +2008,7 @@ EOF
it "fails when the requested library is not found" do
test_dir "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.stdout).to match /Checking for library 'mfoofoo'... not found/
end
@@ -1941,6 +2048,23 @@ EOF
expect(result.stdout).to match /Checking for library 'm'... found/
expect(result.stdout).to_not match /-lm/
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
context "check_program" do
@@ -1952,10 +2076,20 @@ EOF
expect(result.stdout).to match /Checking for program 'find'... .*find/
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
test_dir "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.stdout).to match /Checking for program 'program-that-is-not-found'... not found/
end
@@ -1987,7 +2121,7 @@ EOF
it "fails when the configure program given does not exist" do
test_dir "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.stdout).to match /Checking 'my-config'\.\.\. not found/
end
@@ -2024,6 +2158,74 @@ EOF
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
it "does everything" do
test_dir "configure"
create_exe "pkg-config", "echo '-DMYPACKAGE'"
@@ -2034,8 +2236,8 @@ EOF
expect(result.stdout).to match /Setting build directory\.\.\. bb/
expect(result.stdout).to match %r{Setting prefix\.\.\. /my/prefix}
expect(result.stdout).to match /Checking for C compiler\.\.\. gcc/
expect(result.stdout).to match /Checking for C\+\+ compiler\.\.\. g++/
expect(result.stdout).to match /Checking for D compiler\.\.\. gdc/
expect(result.stdout).to match /Checking for C\+\+ compiler\.\.\. g\+\+/
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 C header 'stdio.h'\.\.\. found/
expect(result.stdout).to match /Checking for C\+\+ header 'iostream'\.\.\. found/
@@ -2062,11 +2264,13 @@ EOF
expect(result.status).to_not eq 0
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"
result = run_rscons(rsconscript: "autoconf_fail.rb")
expect(result.stdout).to match /Checking for C compiler\.\.\. not found/
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
it "exits with an error if configuration has not been performed before attempting to create an environment" do
@@ -2270,13 +2474,13 @@ EOF
result = run_rscons(rsconscript: "install.rb", op: %W[install])
expect(result.stderr).to eq ""
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])
expect(result.stderr).to eq ""
expect(result.stdout).to_not match /Removing/
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[. ..]
end
end
+20 -4
View File
@@ -121,7 +121,10 @@ EOF
before(:each) do
stub_const("File::PATH_SEPARATOR", ";")
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:\\Windows").and_return(%w[two.exe Three.bat])
end
@@ -197,12 +200,12 @@ EOF
end
end
describe ".parse_makefile_deps" do
describe ".parse_dependency_file" do
it 'handles dependencies on one line' do
expect(File).to receive(:read).with('makefile').and_return(<<EOS)
module.o: source.cc
EOS
expect(Util.parse_makefile_deps('makefile')).to eq ['source.cc']
expect(Util.parse_dependency_file('makefile')).to eq ['source.cc']
end
it 'handles dependencies split across many lines' do
@@ -211,9 +214,22 @@ module.o: module.c \\
module.h \\
other.h
EOS
expect(Util.parse_makefile_deps('makefile')).to eq [
expect(Util.parse_dependency_file('makefile')).to eq [
'module.c', 'module.h', 'other.h']
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
-10
View File
@@ -1,15 +1,5 @@
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
before(:each) do
Rscons.instance_variable_set(:@shell, nil)