Compare commits

...

11 Commits

12 changed files with 681 additions and 142 deletions

View File

@ -1,3 +1,25 @@
## v3.0.0
- #136 - Move rsconscache into build directory
- #140 - Support naming environments
- #143 - Add Size builder
- #142 - Add 'sh' script DSL method
- #144 - Add FileUtils class methods to script DSL
- #145 - Support environment variable to set rscons build directory
- #146 - Add ^^/ shortcut to top-level build directory
- #139 - Add tasks
- #147 - Add task options
- #148 - Add license/copyright to distributable script
- #150 - Add env.expand() shortcut method to expand paths and construction variables
- #152 - Add download script method
- #153 - Allow passing spawn options to sh
- #154 - Record build directory absolute path
- #149 - Add shortcut method for creating environments
- #131 - Only configure if necessary
- #151 - Store configure task parameters in configuration cache data
- #137 - Add variants
- #155 - Add build_dir script method
## v2.3.0
### New Features

View File

@ -4,7 +4,39 @@
Rscons (https://github.com/holtrop/rscons) is an open-source build system
for developers.
It supports the following features:
Rscons is written in Ruby, and is inspired by [SCons](https://scons.org/) and [waf](https://waf.io/).
* multi-threaded job execution
* auto-configuration
* 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, OS X, and FreeBSD
* colorized output with build progress
* build hooks
* user-defined tasks with dependencies and custom parameters
* build variants
At its core, Rscons is mainly an engine to:
* determine the proper order to perform build steps,
* determine whether each build target is up to date or in need of rebuild, and
* schedule those build steps across multiple threads as efficiently as possible.
Along the way, Rscons provides a concise syntax for specifying common types of
build steps, but also provides an extensible framework for performing
custom build operations as well.
Rscons takes inspiration from:
* [SCons](https://scons.org/)
* [waf](https://waf.io/)
* [rake](https://github.com/ruby/rake)
* [CMake](https://cmake.org/)
* [Autoconf](https://www.gnu.org/software/autoconf/)
Rscons is written in Ruby.
The only requirement to run Rscons is that the system has a Ruby interpreter
installed.
See [https://holtrop.github.io/rscons/index.html](https://holtrop.github.io/rscons/index.html) for User Guide and Installation instructions.

View File

@ -0,0 +1,3 @@
default do
touch "#{build_dir}/a.file"
end

View File

@ -0,0 +1,8 @@
variant "debug", default: false
variant "release"
with_variants do
env "prog" do |env|
env.Program("^/prog.exe", "prog.c")
end
end

View File

@ -1,5 +0,0 @@
variant_group
variant_group
variant "foo"
with_variants do
end

View File

@ -0,0 +1,14 @@
variant "one"
variant "two", default: false
configure do
if variant_enabled?("one")
puts "one enabled"
end
if variant_enabled?("two")
puts "two enabled"
end
if variant_enabled?("three")
puts "three enabled"
end
end

View File

@ -12,6 +12,7 @@ It supports the following features:
* colorized output with build progress
* build hooks
* user-defined tasks with dependencies and custom parameters
* build variants
At its core, Rscons is mainly an engine to:
@ -67,8 +68,8 @@ incorrect decision being made to not rebuild when a rebuild is necessary.
### Build Flexibility
Rscons supports multiple configurations of compilation flags or build options
across multiple environments to build output files in different ways according
to the user's desire.
across multiple environments or build variants to build output files in
different ways according to the user's desire.
For example, the same source files can be built into a release executable, but
also compiled with different compilation flags or build options into a test
executable.
@ -143,17 +144,52 @@ different version control systems):
Rscons is typically invoked from the command-line as `./rscons`.
./rscons [global options] [[task] [task options] ...]
Usage: ./rscons [global options] [[task] [task options] ...]
Global options:
-b BUILD, --build=BUILD Set build directory (default: build)
-f FILE Use FILE as Rsconscript
-F, --show-failure Show failed command log from previous build and exit
-h, --help Show rscons help and exit
-j N, --nthreads=N Set number of threads
-r COLOR, --color=COLOR Set color mode (off, auto, force)
-v, --verbose Run verbosely
--version Show rscons version and exit
-A, --all
Show all tasks (even those without descriptions) in task list. Use in
conjunction with the -T argument.
-b BUILD, --build=BUILD
Set build directory (default: build).
-e VS, --variants=VS
Enable or disable variants. VS is a comma-separated list of variant
entries. If the entry begins with "-" the variant is disabled instead of
enabled. If the full list begins with "+" or "-" then it modifies the
variants that are enabled by default by only enabling or disabling the
listed variants. Otherwise, the enabled set of variants is as given and
any variants not listed are disabled. The set of enabled variants is
remembered from when the project is configured.
-f FILE
Use FILE as Rsconscript.
-F, --show-failure
Show failed command log from previous build and exit (does not load build
script).
-h, --help
Show rscons help and exit (does not load build script).
-j N, --nthreads=N
Set number of threads (local default: 16).
-r COLOR, --color=COLOR
Set color mode (off, auto, force).
-T, --tasks
Show task list and parameters and exit (loads build script). By default
only tasks with a description are listed. Use -AT to show all tasks whether
they have a description or not.
-v, --verbose
Run verbosely. This causes Rscons to print the full build command used by
each builder.
--version
Show rscons version and exit (does not load build script).
The user can list any number of tasks on the command line.
Any parameters beginning with a "-" that follow a task are interpreted as task
@ -181,6 +217,19 @@ Rscons execution.
The user can also invoke Rscons with the `-v` global command-line option which
will cause Rscons to print each command it is executing.
## Rscons Operation Phases
When Rscons executes, it performs the following phases:
- Parse the command line.
- This is the last step if --help, --version, or --show-failure is specified.
- Load the build script.
- This is the last step if --tasks is specified.
- Configure the project by running the `configure` task if necessary (the
project has not yet been configured, autoconf is set to true, and the user
is requesting to execute a task that is marked with autoconf set to true)
- Execute user-requested tasks.
#> The Build Script
Rscons looks for instructions for what to build by reading a build script file
@ -188,11 +237,9 @@ called `Rsconscript` (or `Rsconscript.rb`).
Here is a simple example `Rsconscript` file:
```ruby
default do
Environment.new do |env|
env do |env|
env.Program("myprog.exe", glob("src/**/*.c"))
end
end
```
This `Rsconscript` file would instruct Rscons to produce a *Program* target
@ -203,7 +250,7 @@ The `Rsconscript` file is a Ruby script.
##> Tasks
Tasks are the high-level user interface for performing functionality in a build
Tasks are a high-level user interface for performing functionality in a build
script.
Tasks can create Environments that perform compilation/linking steps.
Tasks can also execute arbitrary commands or perform any miscellaneous logic.
@ -219,7 +266,7 @@ Example:
```ruby
task "build" do
Environment.new do |env|
env do |env|
env.Program("^^/proj.elf", glob("src/**/*.c"))
end
end
@ -240,9 +287,13 @@ Any newly specified dependencies are added to the current dependencies.
Any action block is appended to the task's list of action blocks to execute
when the task is executed.
Note that for a simple project, the build script may not need to define any
tasks at all and could just make use of the Rscons built-in default task (see
${#Default Task}).
###> Task Parameters
Tasks can also take parameters.
Tasks can accept parameters.
Parameters are defined by the build script author, and have default values.
The user can override parameter values on the command line.
@ -261,7 +312,7 @@ task "build", params: [
param("myparam", "defaultvalue", true, "My special parameter"),
param("xyz", nil, false, "Enable the xyz feature"),
] do |task, params|
Environment.new do |env|
env do |env|
env["CPPDEFINES"] << "SOMEMACRO=#{params["myparam"]}"
if params["flag"]
env["CPPDEFINES"] << "ENABLE_FEATURE_XYZ"
@ -399,8 +450,6 @@ configuration functionality that Rscons provides.
The `default` task is special in that Rscons will execute it if no other task
has been requested by the user on the command line.
It is entirely feasible for the default task to be the only task defined for a
project, and simple projects may wish to do just that.
The default task can also be used to declare a dependency on another task that
would effectively become the default.
@ -432,7 +481,8 @@ It will not remove the cached configuration options.
The `distclean` task is built-in to Rscons.
It removes all built target files and all cached configuration options.
Generally it will get the project directory back to the state it was in when
unpacked before any configuration or build operations took place.
unpacked or checked out, before any configuration or build operations took
place.
It will not remove items installed by an Install builder.
####> Install Task
@ -734,18 +784,17 @@ If set, a build define of the specified String will be added to the
##> Building Targets
Building target files is accomplished by using Environments.
Environments are typically created within the default task or any user-defined
tasks.
Environments can be created at the top level of the build script, or from
within a task action block.
Here is an example `default` task block demonstrating how to create an
Environment and register a build target:
Environments are created with the `env` build script method.
Here is an example build script that creates an Environment and registers a
Program build target:
```ruby
default do
Environment.new do |env|
env do |env|
env.Program("myprog.exe", glob("src/**/*.c"))
end
end
```
This `Rsconscript` would build an executable called `myprog.exe` from all C
@ -759,25 +808,22 @@ An Environment includes:
- a collection of construction variables
- a collection of build hooks
- a collection of user-registered build targets
- a build root
- a build root directory
All build targets must be registered within an `Environment`.
If the user does not specify a name for the environment, a name will be
automatically generated based on the Environment's internal ID, for example
"e.1".
The Environment's build root is a directory created within the top-level
Rscons build directory.
It is based on the Environment name.
The Environment's build root is a directory with the same name as the
Environment, created within the top-level Rscons build directory.
By default it holds all intermediate files generated by Rscons that are needed
to produce a user-specified build target.
For example, for the `Rsconscript`:
```ruby
default do
Environment.new(name: "myproj") do |env|
env "myproj" do |env|
env.Program("myprog.exe", glob("src/**/*.c"))
end
end
```
Rscons will place an object file and dependency file corresponding to each C
@ -786,6 +832,12 @@ Assuming a top-level build directory of "build", the Environment's build root
would be "build/myproj".
This keeps the intermediate generated build artifacts separate from the source
files.
Source and target paths passed to a Builder (e.g. Program) can begin with "^/"
to indicate that Rscons should expand those paths to be relative to the
Environment's build root.
If a source or target path passed to a Builder begins with "^^/", it is
expanded to be relative to the Rscons top-level build directory (but outside
the Environment's build root).
###> Construction Variables
@ -797,12 +849,10 @@ construction variables.
Example:
```ruby
default do
Environment.new do |env|
env do |env|
env["CCFLAGS"] += %w[-O2 -Wall]
env["LIBS"] += %w[m]
end
end
```
This example modifies the `CCFLAGS` construction variable to add `-O2` and
@ -813,6 +863,7 @@ It also instructs the linker to link against the `m` library.
* uppercase strings - the default construction variables that Rscons uses
* strings beginning with "_" - set and used internally by builders
* strings with a ":" as "#{task_name}:#{parameter_name}" - set to task parameter values
* symbols, lowercase strings - reserved as user-defined construction variables
###> Builders
@ -834,7 +885,8 @@ The `target` parameter is the path to the output file or directory.
The `sources` parameter is the path or paths to the input file(s) to be used
by the builder.
In the `target` and `sources` parameters, the user can explicitly refer to a
path within the Environment's build root by beginning the path with "^/".
path within the Environment's build root by beginning the path with "^/", or to
a path within the Rscons top-level built directory by beginning with "^^/".
The `vars` parameter is an optional Hash which can include construction
variables to be used for this build target.
Any construction variable values specified in this parameter will override
@ -1099,7 +1151,7 @@ and flags can be specified with `SIZEFLAGS`.
###> Phony Targets
rscons supports phony build targets.
Rscons supports phony build targets.
Normally, a builder produces an output file, and executes whenever the input
files or command have changed.
A phony build target can be used to register a builder that does not produce
@ -1142,8 +1194,7 @@ build target or source file names.
Example:
```ruby
default do
Environment.new do |env|
env do |env|
env["CFLAGS"] << "-Wall"
env.add_build_hook do |builder|
# Compile sources from under src/tests without the -Wall flag.
@ -1153,7 +1204,6 @@ default do
end
env.Program("program.exe", glob("src/**/*.c"))
end
end
```
This example script would compile all C sources under the `src` directory with
@ -1166,7 +1216,7 @@ Build hooks and post-build hooks can register new build targets.
###> Barriers
Normally Rscons will parallelize all builders.
Normally Rscons will parallelize all builders executed within an Environment.
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.
@ -1176,14 +1226,117 @@ In other words, build targets are not parallelized across a barrier.
env.barrier
```
##> Variants
Rscons supports build variants.
Variants can be used to built multiple variations of the same item with a
specific change.
For example, a desktop application with the same sources could be built to
target KDE or GNOME using build variants.
It is up to the build script author to define the variants and what effect they
have on the build.
This build script defines "kde" and "gnome" variants:
```ruby
variant "kde"
variant "gnome"
with_variants do
env "prog" do |env|
if variant "kde"
env["CPPDEFINES"] << "KDE"
end
if variant "gnome"
env["CPPDEFINES"] << "GNOME"
end
env.Program("^/prog.exe", "src/**/*.cpp")
end
end
```
The `variant` build script method has two uses:
* At the top of the build script, it defines a variant.
* Within a `with_variants` block, it queries for whether the given variant is
active.
The `with_variants` build script method allows the power of variants to be
harnessed.
It iterates through each enabled variant and calls the given block.
In this example, the block would be called twice, once with the "kde" variant
active, and the second time with the "gnome" variant active.
Each `env()` call creates an Environment, so two environments are created.
When an Environment is created within a `with_variants` block, the
Environment's name has the active variant(s) appended to the given Environment
name (if any), and separated by a "-".
In this example, a "prog-kde" Environment would be created with build root
build/prog-kde and -DKDE would be passed to the compiler when compiling each
source.
Next a "prog-gnome" Environment would be created with build root
build/prog-gnome and -DGNOME would be passed to the compiler when compiling
the sources.
Variants are enabled by default, but can be disabled by passing a `false` value
to the `:default` option of the `variant` method.
For example:
```ruby
${include build_tests/variants/default.rb}
```
The `rscons` command line interface provides a `-e`/`--variants` argument which
allows the user to enable a different set of variants from those enabled by
default according to the build script author.
This argument accepts a comma-separated list of variants to enable.
Each entry in the list can begin with "-" to disable the variant instead of
enable it.
If the list begins with "+" or "-", then the entire given list modifies the
defaults given in the build script.
Otherwise, it exactly specifies which variants should be enabled, and any
variant not listed is disabled.
When the project is configured, the set of enabled variants is recorded and
remembered for later Rscons invocations.
This way, a user working on a single variant of a project does not need to
specify the `-e`/`--variants` option on each build operation.
The `variant_enabled?` build script method can be called to query whether the
given variant is enabled.
###> Variant Groups
Variants may be grouped, which allows the build script author to define
multiple combinations of desired variations to build with.
For example:
```ruby
${include build_tests/variants/multiple_groups.rb}
```
This build script executes the block given to `with_variants` four times and
results in four Environments being created:
* prog-kde-debug
* prog-kde-release
* prog-gnome-debug
* prog-gnome-release
The command `./rscons -e-debug` would build just "prog-kde-release" and "prog-gnome-release".
The command `./rscons --variants kde,release` would build just "prog-kde-release".
##> Build Script Methods
`rscons` provides several methods that a build script can use.
* `autoconf` (see ${#Configure Task})
* `build_dir` which returns the path to the top-level Rscons build directory
* `clean` (see ${#Clean Task})
* `configure` (see ${#Configure Task})
* `default` (see ${#Default Task})
* `download` (see ${#Downloading Files: The download Method})
* `distclean` (see ${#Distclean Task})
* `glob` (see ${#Finding Files: The glob Method})
* `install` (see ${#Install Task})
@ -1197,6 +1350,10 @@ env.barrier
* `sh` (see (${#Executing Commands: The sh Method})
* `task` (see ${#Tasks})
* `uninstall` (see ${#Uninstall Task})
* `variant` (see ${#Variants})
* `variant_enabled?` (see ${#Variant Groups})
* `variant_group` (see ${#Variant Groups})
* `with_variants` (see ${#Variant Groups})
Additionally, the following methods from the Ruby
[FileUtils](https://ruby-doc.org/stdlib-3.1.0/libdoc/fileutils/rdoc/FileUtils.html)
@ -1237,16 +1394,26 @@ rather than file system directory ordering).
Example use:
```ruby
default do
Environment.new do |env|
env do |env|
env.Program("mytests", glob("src/**/*.cc", "test/**/*.cc"))
end
end
```
This example would build the `mytests` executable from all `.cc` source files
found recursively under the `src` or `test` directory.
###> Downloading Files: The download Method
The [`download`](../yard/Rscons/Script/GlobalDsl.html#download-instance_method)
method can be used to download a file from a given URL.
Example use:
```ruby
default do
end
```
###> PATH Management
`rscons` provides methods for management of the `PATH` environment variable.
@ -1354,6 +1521,18 @@ default do
end
```
Options that Ruby's `spawn` method accepts can also be passed in to the `sh`
method.
For example, to execute the given command with a different working directory,
the `:chdir` option can be specified:
```ruby
default do
# Execute 'ls' from within the 'src' directory:
sh "ls", chdir: "src"
end
```
##> Extending Rscons
### Adding New Languages
@ -1406,7 +1585,7 @@ class Rscons::Builders::Mine < Rscons::Builder
end
default do
Environment.new do |env|
env do |env|
env.add_builder(Rscons::Builders::Mine)
end
end
@ -1429,7 +1608,7 @@ Rscons::DEFAULT_BUILDERS << :Special
load "SpecialBuilder.rb"
default do
Environment.new do |env|
env do |env|
# A build target using the "Special" builder can be registered.
env.Special("target", "source")
end
@ -1656,40 +1835,36 @@ ${include lib/rscons/default_construction_variables.rb}
### Example: Building a C Program
```ruby
default do
Environment.new do |env|
env do |env|
env["CFLAGS"] << "-Wall"
env.Program("program", glob("src/**/*.c"))
end
end
```
### Example: Building a D Program
```ruby
default do
Environment.new do |env|
env do |env|
env["DFLAGS"] << "-Wall"
env.Program("program", glob("src/**/*.d"))
end
end
```
### Example: Cloning an Environment
```ruby
default do
main_env = Environment.new do |env|
env["CFLAGS"] = ["-DSOME_DEFINE", "-O3"]
main_env = env do |env|
env["CFLAGS"] = ["-fshort-enums", "-O3"]
env["CPPDEFINES"] << "SOME_DEFINE"
env["LIBS"] = ["SDL"]
env.Program("program", glob("src/**/*.cc"))
end
debug_env = main_env.clone do |env|
test_env = main_env.clone do |env|
env["CFLAGS"] -= ["-O3"]
env["CFLAGS"] += ["-g", "-O0"]
env.Program("program-debug", glob("src/**/*.cc"))
end
env["CPPDEFINES"] = "ENABLE_TESTS"
env.Program("program-test", glob("src/**/*.cc"))
end
```
@ -1709,20 +1884,17 @@ EOF
end
end
default do
Environment.new do |env|
env do |env|
env.add_builder(GenerateFoo)
env.GenerateFoo("foo.h", [])
env.Program("a.out", glob("*.c"))
end
end
```
### Example: Using different compilation flags for some sources
```ruby
default do
Environment.new do |env|
env do |env|
env["CFLAGS"] = ["-O3", "-Wall"]
env.add_build_hook do |builder|
if builder.sources.first =~ %r{src/third-party/}
@ -1731,27 +1903,22 @@ default do
end
env.Program("program", glob("**/*.cc"))
end
end
```
### Example: Creating a static library
```ruby
default do
Environment.new do |env|
env do |env|
env.Library("mylib.a", glob("src/**/*.c"))
end
end
```
### Example: Creating a C++ parser source from a Yacc/Bison input file
```ruby
default do
Environment.new do |env|
env do |env|
env.CFile("^/parser.tab.cc", "parser.yy")
end
end
```
##> ./configure && make

View File

@ -5,8 +5,8 @@ module Rscons
# Functionality for an instance of the rscons application invocation.
class Application
# @return [Array<String>]
# Active variant names.
# @return [Array<Hash>]
# Active variants.
attr_reader :active_variants
# @return [String]
@ -54,15 +54,26 @@ module Rscons
# List of task(s) to execute.
# @param show_tasks [Boolean]
# Flag to show tasks and exit.
# @param all_tasks [Boolean]
# Flag to show all tasks (not just those with a description).
# @param enabled_variants [String]
# User-specified variants list.
#
# @return [Integer]
# Process exit code (0 on success).
def run(rsconscript, tasks_and_params, show_tasks)
def run(rsconscript, tasks_and_params, show_tasks, all_tasks, enabled_variants)
Cache.instance["failed_commands"] = []
@enabled_variants = enabled_variants
if enabled_variants == "" && !tasks_and_params.include?("configure")
if cache_enabled_variants = Cache.instance["configuration_data"]["enabled_variants"]
@enabled_variants = cache_enabled_variants
end
end
@script = Script.new
@script.load(rsconscript)
enable_variants
if show_tasks
show_script_tasks
show_script_tasks(all_tasks)
return 0
end
apply_task_params(tasks_and_params)
@ -79,6 +90,32 @@ module Rscons
0
end
# Apply user-specified variant enables and complain if they don't make
# sense given the build script variant configuration.
def enable_variants
unless @_variants_enabled
if @enabled_variants != ""
exact = !(@enabled_variants =~ /^(\+|-)/)
enabled_variants = @enabled_variants.split(",")
specified_variants = {}
enabled_variants.each do |enable_variant|
enable_variant =~ /^(\+|-)?(.*)$/
enable_disable, variant_name = $1, $2
specified_variants[variant_name] = enable_disable != "-"
end
each_variant do |variant|
if specified_variants.include?(variant[:name])
variant[:enabled] = specified_variants[variant[:name]]
elsif exact
variant[:enabled] = false
end
end
end
@_variants_enabled = true
end
check_enabled_variants
end
# Show the last failures.
#
# @return [void]
@ -131,6 +168,7 @@ module Rscons
#
# @return [void]
def check_configure
enable_variants
unless Cache.instance["configuration_data"]["configured"]
if @script.autoconf
configure
@ -168,6 +206,7 @@ module Rscons
co.close(false)
raise e
end
Cache.instance["configuration_data"]["enabled_variants"] = @enabled_variants
co.close(true)
end
@ -213,24 +252,48 @@ module Rscons
end
options = options.dup
options[:name] = name
options[:active] = options.fetch(:default, true)
options[:enabled] = options.fetch(:default, true)
options[:key] = options.fetch(:key, name)
@variant_groups.last[:variants] << options
end
end
# Check if a variant is enabled.
#
# This can be used, for example, in a configuration block to omit or
# include configuration checks based on which variants have been
# configured.
#
# @param variant_name [String]
# Variant name.
#
# @return [Boolean]
# Whether the requested variant is enabled.
def variant_enabled?(variant_name)
each_variant do |variant|
if variant[:name] == variant_name
return variant[:enabled]
end
end
false
end
# Create a variant group.
def variant_group(*args, &block)
if args.first.is_a?(String)
name = args.slice!(0)
end
@variant_groups << {name: name, variants: []}
options = args.first || {}
@variant_groups << options.merge(name: name, variants: [])
if block
block[]
end
end
# Iterate through variants.
# Iterate through enabled variants.
#
# The given block is called for each combination of enabled variants
# across the defined variant groups.
def with_variants(&block)
if @active_variants
raise "with_variants cannot be called within another with_variants block"
@ -238,18 +301,15 @@ module Rscons
if @variant_groups.empty?
raise "with_variants cannot be called with no variants defined"
end
if @variant_groups.any? {|variant_group| variant_group[:variants].empty?}
raise "Error: empty variant group found"
end
iter_vgs = lambda do |variants|
if variants.size == @variant_groups.size
@active_variants = variants
iter_vgs = lambda do |iter_variants|
if iter_variants.size == @variant_groups.size
@active_variants = iter_variants.compact
block[]
@active_variants = nil
else
@variant_groups[variants.size][:variants].each do |variant|
if variant[:active]
iter_vgs[variants + [variant]]
@variant_groups[iter_variants.size][:variants].each do |variant|
if variant[:enabled]
iter_vgs[iter_variants + [variant]]
end
end
end
@ -259,10 +319,33 @@ module Rscons
private
def show_script_tasks
def check_enabled_variants
@variant_groups.each do |variant_group|
enabled_count = variant_group[:variants].count do |variant|
variant[:enabled]
end
if enabled_count == 0
message = "No variants enabled for variant group"
if variant_group[:name]
message += " #{variant_group[:name].inspect}"
end
raise RsconsError.new(message)
end
end
end
def each_variant
@variant_groups.each do |variant_group|
variant_group[:variants].each do |variant|
yield variant
end
end
end
def show_script_tasks(all_tasks)
puts "Tasks:"
Task[].sort.each do |task_name, task|
if task.description
if task.description || all_tasks
puts %[ #{sprintf("%-27s", task_name)} #{task.description}]
task.params.each do |param_name, param|
arg_text = "--#{param_name}"
@ -273,6 +356,15 @@ module Rscons
end
end
end
unless @variant_groups.empty?
@variant_groups.each do |variant_group|
puts "\nVariant group#{variant_group[:name] ? " '#{variant_group[:name]}'" : ""}:"
variant_group[:variants].each do |variant|
puts " #{variant[:name]}#{variant[:enabled] ? " (enabled)" : ""}"
end
end
end
end
def apply_task_params(tasks_and_params)

View File

@ -54,13 +54,23 @@ module Rscons
def run_toplevel(argv)
rsconscript = nil
show_tasks = false
all_tasks = false
enabled_variants = ""
OptionParser.new do |opts|
opts.on("-A", "--all") do
all_tasks = true
end
opts.on("-b", "--build DIR") do |build_dir|
Rscons.application.build_dir = build_dir
end
opts.on("-e", "--variants VS") do |variants|
enabled_variants = variants
end
opts.on("-f FILE") do |f|
rsconscript = f
end
@ -127,7 +137,7 @@ module Rscons
end
begin
Rscons.application.run(rsconscript, tasks_and_params, show_tasks)
Rscons.application.run(rsconscript, tasks_and_params, show_tasks, all_tasks, enabled_variants)
rescue RsconsError => e
Ansi.write($stderr, :red, e.message, :reset, "\n")
1
@ -139,15 +149,49 @@ module Rscons
Usage: #{$0} [global options] [[task] [task options] ...]
Global options:
-b BUILD, --build=BUILD Set build directory (default: build)
-f FILE Use FILE as Rsconscript
-F, --show-failure Show failed command log from previous build and exit
-h, --help Show rscons help and exit
-j N, --nthreads=N Set number of threads (local default: #{Rscons.application.n_threads})
-r COLOR, --color=COLOR Set color mode (off, auto, force)
-T, --tasks Show task list and parameters and exit
-v, --verbose Run verbosely
--version Show rscons version and exit
-A, --all
Show all tasks (even those without descriptions) in task list. Use in
conjunction with the -T argument.
-b BUILD, --build=BUILD
Set build directory (default: build).
-e VS, --variants=VS
Enable or disable variants. VS is a comma-separated list of variant
entries. If the entry begins with "-" the variant is disabled instead of
enabled. If the full list begins with "+" or "-" then it modifies the
variants that are enabled by default by only enabling or disabling the
listed variants. Otherwise, the enabled set of variants is as given and
any variants not listed are disabled. The set of enabled variants is
remembered from when the project is configured.
-f FILE
Use FILE as Rsconscript.
-F, --show-failure
Show failed command log from previous build and exit (does not load build
script).
-h, --help
Show rscons help and exit (does not load build script).
-j N, --nthreads=N
Set number of threads (local default: #{Rscons.application.n_threads}).
-r COLOR, --color=COLOR
Set color mode (off, auto, force).
-T, --tasks
Show task list and parameters and exit (loads build script). By default
only tasks with a description are listed. Use -AT to show all tasks whether
they have a description or not.
-v, --verbose
Run verbosely. This causes Rscons to print the full build command used by
each builder.
--version
Show rscons version and exit (does not load build script).
EOF
end

View File

@ -102,7 +102,6 @@ module Rscons
end.compact
@name = [base_name, *variant_keys].join("-")
options = args.first || {}
Rscons.application.check_configure
unless Cache.instance["configuration_data"]["configured"]
raise "Project must be configured before creating an Environment"
end

View File

@ -10,6 +10,14 @@ module Rscons
@script = script
end
# Get the Rscons build directory path.
#
# @return [String]
# Rscons build directory path.
def build_dir
Rscons.application.build_dir
end
# Return a list of paths matching the specified pattern(s).
#
# A pattern can contain a "/**" component to recurse through directories.
@ -95,6 +103,7 @@ module Rscons
# Create an environment.
def env(*args, &block)
Rscons.application.check_configure
Environment.new(*args, &block)
end
@ -250,6 +259,11 @@ module Rscons
Rscons.application.variant(*args)
end
# Check if a variant is enabled.
def variant_enabled?(*args)
Rscons.application.variant_enabled?(*args)
end
# Create a variant group.
def variant_group(*args, &block)
Rscons.application.variant_group(*args, &block)
@ -257,6 +271,7 @@ module Rscons
# Iterate through variants.
def with_variants(&block)
Rscons.application.enable_variants
Rscons.application.with_variants(&block)
end

View File

@ -2956,6 +2956,26 @@ EOF
expect(result.stdout).to_not match /^\s*one\b/
expect(result.stdout).to_not match /^\s*two\b/
end
context "with -A flag" do
it "displays all tasks and their parameters" do
test_dir "tasks"
result = run_rscons(args: %w[-f tasks.rb -AT])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
verify_lines(lines(result.stdout), [
"Tasks:",
/\bone\b/,
/\btwo\b/,
/\bthree\b\s+Task three/,
/\bfour\b\s+Task four/,
/--myparam=MYPARAM\s+My special parameter/,
/--myp2\s+My parameter 2/,
/\bfive\b/,
/\bsix\b/,
])
end
end
end
context "download script method" do
@ -3100,12 +3120,140 @@ EOF
expect(result.status).to_not eq 0
end
it "raises an error when with_variants is called with an empty variant group" do
it "allows specifying the exact enabled variants on the command line 1" do
test_dir "variants"
result = run_rscons(args: %w[-f error_with_variants_with_empty_variant_group.rb])
expect(result.stderr).to match %r{Error: empty variant group found}
result = run_rscons(args: %w[-v -f multiple_groups.rb -e kde,debug])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(File.exist?("build/prog-kde-debug/prog.exe")).to be_truthy
expect(File.exist?("build/prog-kde-release/prog.exe")).to be_falsey
expect(File.exist?("build/prog-gnome-debug/prog.exe")).to be_falsey
expect(File.exist?("build/prog-gnome-release/prog.exe")).to be_falsey
end
it "allows specifying the exact enabled variants on the command line 2" do
test_dir "variants"
result = run_rscons(args: %w[-v -f multiple_groups.rb -e kde,gnome,release])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(File.exist?("build/prog-kde-debug/prog.exe")).to be_falsey
expect(File.exist?("build/prog-kde-release/prog.exe")).to be_truthy
expect(File.exist?("build/prog-gnome-debug/prog.exe")).to be_falsey
expect(File.exist?("build/prog-gnome-release/prog.exe")).to be_truthy
end
it "allows disabling a single variant on the command line" do
test_dir "variants"
result = run_rscons(args: %w[-v -f multiple_groups.rb --variants=-kde])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(File.exist?("build/prog-kde-debug/prog.exe")).to be_falsey
expect(File.exist?("build/prog-kde-release/prog.exe")).to be_falsey
expect(File.exist?("build/prog-gnome-debug/prog.exe")).to be_truthy
expect(File.exist?("build/prog-gnome-release/prog.exe")).to be_truthy
end
it "allows turning off variants by default" do
test_dir "variants"
result = run_rscons(args: %w[-v -f default.rb])
expect(File.exist?("build/prog-debug/prog.exe")).to be_falsey
expect(File.exist?("build/prog-release/prog.exe")).to be_truthy
end
it "allows turning on an off-by-default-variant from the command line" do
test_dir "variants"
result = run_rscons(args: %w[-v -f default.rb -e +debug])
expect(File.exist?("build/prog-debug/prog.exe")).to be_truthy
expect(File.exist?("build/prog-release/prog.exe")).to be_truthy
end
it "allows only turning on an off-by-default-variant from the command line" do
test_dir "variants"
result = run_rscons(args: %w[-v -f default.rb -e debug])
expect(File.exist?("build/prog-debug/prog.exe")).to be_truthy
expect(File.exist?("build/prog-release/prog.exe")).to be_falsey
end
it "exits with an error if no variant in a variant group is activated" do
test_dir "variants"
result = run_rscons(args: %w[-v -f multiple_groups.rb --variants=kde])
expect(result.stderr).to match %r{No variants enabled for variant group}
expect(result.status).to_not eq 0
end
it "allows querying if a variant is enabled" do
test_dir "variants"
result = run_rscons(args: %w[-f variant_enabled.rb configure])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match %r{one enabled}
expect(result.stdout).to_not match %r{two enabled}
expect(result.stdout).to_not match %r{three enabled}
result = run_rscons(args: %w[-f variant_enabled.rb --variants=+two configure])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to match %r{one enabled}
expect(result.stdout).to match %r{two enabled}
expect(result.stdout).to_not match %r{three enabled}
result = run_rscons(args: %w[-f variant_enabled.rb --variants=two configure])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(result.stdout).to_not match %r{one enabled}
expect(result.stdout).to match %r{two enabled}
expect(result.stdout).to_not match %r{three enabled}
end
it "shows available variants with -T" do
test_dir "variants"
result = run_rscons(args: %w[-f multiple_groups.rb -T])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
verify_lines(lines(result.stdout), [
"Variant group 'desktop-environment':",
" kde (enabled)",
" gnome (enabled)",
"Variant group 'debug':",
" debug (enabled)",
" release (enabled)",
])
result = run_rscons(args: %w[-f multiple_groups.rb -e gnome,release configure])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
result = run_rscons(args: %w[-f multiple_groups.rb -T])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
verify_lines(lines(result.stdout), [
"Variant group 'desktop-environment':",
" kde",
" gnome (enabled)",
"Variant group 'debug':",
" debug",
" release (enabled)",
])
end
end
context "build_dir method" do
it "returns the top-level build directory path 1" do
test_dir "typical"
result = run_rscons(args: %w[-f build_dir.rb])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(File.exist?("build/a.file")).to be_truthy
end
it "returns the top-level build directory path 2" do
test_dir "typical"
result = run_rscons(args: %w[-f build_dir.rb -b bb])
expect(result.stderr).to eq ""
expect(result.status).to eq 0
expect(File.exist?("bb/a.file")).to be_truthy
end
end
end