421 lines
12 KiB
Markdown
421 lines
12 KiB
Markdown
# Overview
|
|
|
|
Rscons is an open-source build system for developers.
|
|
It supports the following features:
|
|
|
|
* 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, and OS X
|
|
* colorized output with build progress
|
|
* build hooks
|
|
|
|
At its core, Rscons is mainly an engine to:
|
|
|
|
* determine the proper order to perform build operations,
|
|
* determine whether each build target is up to date or in need of rebuild, and
|
|
* schedule those build operations across multiple threads as efficiently as possible.
|
|
|
|
Along the way, Rscons provides a concise syntax for specifying common types of
|
|
build operations, but also provides an extensible framework for performing
|
|
custom build operations as well.
|
|
|
|
Rscons is written in Ruby, and is inspired by [SCons](https://scons.org/) and [waf](https://waf.io/).
|
|
|
|
## Design Principles
|
|
|
|
### Build Correctness
|
|
|
|
The number one design principle in Rscons is build correctness.
|
|
This means that a build operation will be performed when Rscons cannot
|
|
determine that a build target is already up-to-date.
|
|
A build target will be built whenever:
|
|
|
|
* the target file has been removed or changed since it was last built
|
|
* the command to build the target file is different from the previous command
|
|
used to build it
|
|
* any of the target file's dependency files have changed since the last time
|
|
the target was built
|
|
|
|
Importantly, Rscons uses the content of a source (dependency) file to determine
|
|
whether a rebuild is necessary, not simply the timestamp of the file.
|
|
This is because relying solely on the timestamp of the file can lead to an
|
|
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.
|
|
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.
|
|
Rscons also supports build hooks, which allow the user to further fine-tune the
|
|
build system's operation.
|
|
A build hook, for example, can be used to set a build option for only source
|
|
files coming from a particular source directory.
|
|
|
|
### Build Efficiency
|
|
|
|
Rscons will automatically determine the number of threads to use based on the
|
|
host CPU configuration, and will schedule jobs as efficiently as possible
|
|
across the available threads in order to complete the build operation in as
|
|
little time as possible.
|
|
As development occurs and build operations are executed, Rscons makes use of a
|
|
cache file in order to avoid rebuilding a target when it is already up to date.
|
|
|
|
### Build Directory
|
|
|
|
Rscons was designed to store temporary build artifacts (for example, object
|
|
files, dependency files, etc...) in a `build` directory.
|
|
This keeps files generated by the build cleanly separated from user-controlled
|
|
source files.
|
|
|
|
# Installation
|
|
|
|
Rscons is designed to be distributed as a stand-alone single file script that
|
|
can be copied into and versioned in a project's source tree.
|
|
The only dependency required to run Rscons is to have a Ruby interpreter
|
|
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).
|
|
|
|
## Version Control Setup
|
|
|
|
The following files should be added to source control:
|
|
|
|
* `rscons`
|
|
* `Rsconscript`
|
|
|
|
Add the following line to `.gitignore` (or the equivalent thereof for different
|
|
version control systems):
|
|
|
|
/.rscons*
|
|
/build/
|
|
|
|
# The Build Script
|
|
|
|
Rscons looks for instructions for what to build by reading a build script file
|
|
called `Rsconscript` (or `Rsconscript.rb`).
|
|
Here is a simple example `Rsconscript` file:
|
|
|
|
build do
|
|
Environment.new do |env|
|
|
env.Program("myprog.exe", glob("src/**/*.c"))
|
|
end
|
|
end
|
|
|
|
This `Rsconscript` file would instruct Rscons to produce a *Program* target
|
|
called `myprog.exe` which is to be built from all C source files found
|
|
(recursively) under the `src` directory.
|
|
|
|
The `Rsconscript` file is a Ruby script.
|
|
|
|
# Command-Line Operation
|
|
|
|
Rscons is typically invoked from the command-line as `./rscons`.
|
|
Rscons supports several build *operations*:
|
|
|
|
* configure
|
|
* build
|
|
* clean
|
|
* distclean
|
|
* install
|
|
* uninstall
|
|
|
|
## Configure Operation
|
|
|
|
The `configure` operation will initialize the Rscons cache file and build
|
|
directory.
|
|
It will also perform any configuration checks requested by the build script.
|
|
Such configuration checks can include:
|
|
|
|
* verifying operation of a compiler
|
|
* loading compilation/linker flags from a config program (e.g. `pkg-config`)
|
|
* verifying presence of a C/C++ header file
|
|
* verifying presence of a D import
|
|
* verifying presence of a library
|
|
* verifying presence of an executable
|
|
|
|
## Build Operation
|
|
|
|
If a `build` operation is requested and a `configure` operation has not yet
|
|
been performed, a `configure` operation will be automatically invoked.
|
|
|
|
The `build` operation will execute all builders registered to produce build
|
|
targets.
|
|
|
|
## Clean Operation
|
|
|
|
A `clean` operation will remove all built target files.
|
|
It will not remove items installed by an `install` operation.
|
|
It will not remove the cached configuration options.
|
|
|
|
## Distclean Operation
|
|
|
|
A `distclean` operation will remove 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.
|
|
It will not removed items installed by an `install` operation.
|
|
|
|
## Install Operation
|
|
|
|
An `install` operation will perform a `build` (and if necessary, first a
|
|
`configure` as well).
|
|
In addition it will execute any `Install` or `InstallDirectory` builders to
|
|
install items into the specified install directory.
|
|
|
|
## Uninstall Operation
|
|
|
|
An `uninstall` operation will remove any items installed by an `install`
|
|
operation.
|
|
It will not remove all built target files, just the installed copies.
|
|
|
|
# Writing the Build Script
|
|
|
|
## Configuration Operations
|
|
|
|
A `configure` block is optional.
|
|
It can be used to perform various checks and setup operations for a project.
|
|
Example `configure` block:
|
|
|
|
configure do
|
|
check_cxx_compiler
|
|
check_c_header "getopt.h"
|
|
end
|
|
|
|
### Checking for a Compiler
|
|
|
|
The following methods can be used within a `configure` block to check for a
|
|
working compiler:
|
|
|
|
* `check_c_compiler`
|
|
* `check_cxx_compiler`
|
|
* `check_d_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.
|
|
|
|
Here are example calls which also show the default compiler list for each
|
|
supported language:
|
|
|
|
configure do
|
|
check_c_compiler "gcc", "clang"
|
|
check_cxx_compiler "g++", "clang++"
|
|
check_d_compiler "gdc", "ldc2"
|
|
end
|
|
|
|
### Checking for a Header File
|
|
|
|
The following methods can be used to check for the presence of a header file:
|
|
|
|
* `check_c_header` will check for a C header to be present
|
|
* `check_cxx_header` will check for a C++ header to be present
|
|
|
|
Each of these methods take the name of the header file to check for as the
|
|
first argument, and take an optional Hash of arguments as the second argument.
|
|
|
|
Example calls:
|
|
|
|
configure do
|
|
check_c_header "getopt.h", fail: false, set_define: "HAVE_GETOPT_H"
|
|
check_c_header "FreeType2.h"
|
|
check_cxx_header "memory"
|
|
end
|
|
|
|
#### Options
|
|
|
|
##### `:fail`
|
|
|
|
If the `:fail` option is set to `false`, then the absence of the header file
|
|
will not result in the configure option failing.
|
|
|
|
##### `:set_define`
|
|
|
|
If set, a build define of the specified String will be added to the
|
|
`CPPDEFINES` construction variable array if the requested header is found.
|
|
|
|
### Checking for a D Import
|
|
|
|
The `check_d_import` method can be used to check for the presence of D import.
|
|
|
|
This method takes the name of the import to check for as the first argument.
|
|
|
|
Example calls:
|
|
|
|
configure do
|
|
check_d_import "std.stdio"
|
|
check_d_import "std.numeric"
|
|
end
|
|
|
|
### Checking for a Library
|
|
|
|
The `check_lib` method can be used to check for the presence of a library.
|
|
|
|
This method takes the name of the library to check for as the first argument,
|
|
and take an optional Hash of arguments as the second argument.
|
|
|
|
Example calls:
|
|
|
|
configure do
|
|
check_lib "kpty", fail: false, set_define: "HAVE_LIBKPTY"
|
|
check_lib "GL"
|
|
end
|
|
|
|
#### Options
|
|
|
|
##### `:fail`
|
|
|
|
If the `:fail` option is set to `false`, then the absence of the library
|
|
will not result in the configure option failing.
|
|
|
|
##### `:set_define`
|
|
|
|
If set, a build define of the specified String will be added to the
|
|
`CPPDEFINES` construction variable array if the requested library is found.
|
|
|
|
### Checking for a Program
|
|
|
|
The `check_program` method can check for the existence of an executable in the
|
|
host operating system environment.
|
|
|
|
Example call:
|
|
|
|
configure do
|
|
check_program "xxd"
|
|
end
|
|
|
|
### Checking for a Package Configuration
|
|
|
|
The `check_cfg` method can be used to check for the existence of a package as
|
|
well as import any build options (e.g. include path, defines, libraries to link
|
|
against, etc...) required to use the package.
|
|
|
|
This method takes a Hash of options as its only argument.
|
|
|
|
Example calls:
|
|
|
|
configure do
|
|
check_cfg package: "zlib"
|
|
check_cfg program: "freetype-config", fail: false, set_define: "HAVE_FREETYPE"
|
|
end
|
|
|
|
#### Options
|
|
|
|
##### `:package`
|
|
|
|
If the `:package` option is set to a value, the `pkg-config` program will be
|
|
used to look for package configuration flags for the specified package.
|
|
|
|
##### `:program`
|
|
|
|
If the `:program` option is given, the program specified will be used to look
|
|
for configuration flags.
|
|
|
|
##### `:fail`
|
|
|
|
If the `:fail` option is set to `false`, then the absence of the package or
|
|
program requested will not result in the configure option failing.
|
|
|
|
##### `:set_define`
|
|
|
|
If set, a build define of the specified String will be added to the
|
|
`CPPDEFINES` construction variable array if the requested package is found.
|
|
|
|
## Build Operations
|
|
|
|
The `build` block is used to create Environments and register build targets.
|
|
An Rscons build script would not be very useful without a `build` block.
|
|
|
|
Here is an example `build` block demonstrating how to register a build target:
|
|
|
|
build do
|
|
Environment.new do |env|
|
|
env.Program("myprog.exe", glob("src/**/*.c"))
|
|
end
|
|
end
|
|
|
|
This `Rsconscript` would build an executable called `myprog.exe` from all C
|
|
source files found recursively under the `src` directory.
|
|
|
|
### Environments
|
|
|
|
An Environment is a collection of:
|
|
|
|
- construction variables
|
|
- build hooks
|
|
- registered build targets
|
|
|
|
All build targets must be registered within an `Environment`.
|
|
|
|
### Construction Variables
|
|
|
|
Construction variables are values assigned to keys within an Environment.
|
|
Construction variables are used by Builders to produce output files.
|
|
See [#Default Construction Variables] for a reference of all built-in
|
|
construction variables.
|
|
|
|
Example:
|
|
|
|
build do
|
|
Environment.new do |env|
|
|
env["CCFLAGS"] += %w[-O2 -Wall]
|
|
env["LIBS"] += %w[m]
|
|
end
|
|
end
|
|
|
|
This example modifies the `CCFLAGS` construction variable to add `-O2` and
|
|
`-Wall` to the compilation commands used for C and C++ source files.
|
|
It also instructs the linker to link against the `m` library.
|
|
|
|
### Build Hooks
|
|
|
|
A build hook is a Ruby block that is called whenever Rscons is about to invoke
|
|
a builder to produce a build target.
|
|
Rscons also supports post-build hooks which are called after the builder has
|
|
produced the build target.
|
|
A build hook can be used to modify construction variables depending on the
|
|
build target or source file names.
|
|
|
|
Example:
|
|
|
|
build do
|
|
Environment.new do |env|
|
|
env["CFLAGS"] << "-Wall"
|
|
env.add_build_hook do |builder|
|
|
# Compile sources from under src/tests without the -Wall flag.
|
|
if builder.sources.first =~ %r{src/tests/}
|
|
builder.vars["CFLAGS"] -= %w[-Wall]
|
|
end
|
|
end
|
|
env.Program("program.exe", glob("src/**/*.c"))
|
|
end
|
|
end
|
|
|
|
This example script would compile all C sources under the `src` directory with
|
|
the `-Wall` flag except for sources under the `src/tests` directory.
|
|
|
|
## Extending Rscons
|
|
|
|
### Adding New Languages
|
|
|
|
### Adding New Builders
|
|
|
|
# Reference
|
|
|
|
## Default Builders
|
|
|
|
## Default Construction Variables
|
|
|
|
# License
|
|
|
|
Rscons is licensed under the terms of the MIT License.
|
|
|
|
# Change Log
|
|
|
|
[TODO]
|