Compare commits

..
1 Commits
Author SHA1 Message Date
josh e952dcbffc wip 2022-02-19 22:27:43 -05:00
81 changed files with 3592 additions and 5111 deletions

No files matched your search

-38
View File
@@ -1,38 +0,0 @@
name: Run RScons Tests
on:
push:
branches:
- master
pull_request:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
ruby-version: ['2.7', '3.0', '3.4']
steps:
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: sudo apt-get update && sudo apt-get install -y gcc gdc ldc clang flex bison
- name: Install dependencies (macOS)
if: runner.os == 'macOS'
run: brew install gcc ldc flex bison
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby-version }}
- name: Install dependencies
run: bundle install
- name: Run tests
run: rake all
+1 -1
View File
@@ -7,5 +7,5 @@
/gen/ /gen/
/large_project/ /large_project/
/pkg/ /pkg/
/test_run/ /test/
/yard/ /yard/
-73
View File
@@ -1,76 +1,3 @@
## v3.3.0
### New Features
- Add support for Ruby 3.4.
- Fix up some macOS test cases.
- #177 - Remove dependency on base64
- #175 - Add support for building object files from LLVM assembly sources
## v3.2.0
### New Features
- #171 - Place object files next to source files for source files in build directory
- #170 - Use top-level build directory as environment build directory for unnamed environments
- #167 - Add upgrading instructions
- #166 - Replace CFile builder with Yacc and Lex builders
### Fixes
- #172 - Fix configuration checks performed with ldc2 compiler
- #169 - Output a better error when pkg-config is not found
- #168 - Cannot output binary to an environment's build root with the same name as the top-level source directory
- #164 - Improve DSL method documentation
## v3.1.0
### New Features
- #162 - Improve configuration error messages
- #163 - Ruby 3.2 compatibility
### Fixes
- #161 - builder registered during build hooks should increase build step count
## v3.0.2
### Fixes
- #159 - Compiler check configure methods should respect :use flag
- #160 - Configure parameters should not be stored as unscoped construction variables
## v3.0.1
### Fixes
- #156 - Avoid running configure operation twice
- #157 - Load configure task arguments before early configure operations
- #158 - Do not configure for clean tasks when not yet configured
## 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 ## v2.3.0
### New Features ### New Features
+1 -1
View File
@@ -9,6 +9,6 @@ To run the rscons specs, the following commands must be available:
* g++ * g++
* clang++ * clang++
* gdc * gdc
* ldc2 * ldc
* flex * flex
* bison * bison
+1 -3
View File
@@ -1,10 +1,8 @@
source 'https://rubygems.org' source 'https://rubygems.org'
gem "base64"
gem "rspec" gem "rspec"
gem "rake" gem "rake"
gem "simplecov" gem "simplecov", "~> 0.15.0"
gem "openssl"
if RbConfig::CONFIG["host"]["msys"] if RbConfig::CONFIG["host"]["msys"]
gem "json", "2.1.0" gem "json", "2.1.0"
else else
+25 -40
View File
@@ -1,61 +1,46 @@
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
base64 (0.3.0) diff-lcs (1.4.4)
date (3.5.1) docile (1.1.5)
diff-lcs (1.6.2) json (2.5.1)
docile (1.4.1) rake (13.0.6)
erb (6.0.1) rdoc (6.3.2)
json (2.18.0) redcarpet (3.5.1)
openssl (4.0.0) rspec (3.10.0)
psych (5.3.1) rspec-core (~> 3.10.0)
date rspec-expectations (~> 3.10.0)
stringio rspec-mocks (~> 3.10.0)
rake (13.3.1) rspec-core (3.10.1)
rdoc (7.1.0) rspec-support (~> 3.10.0)
erb rspec-expectations (3.10.1)
psych (>= 4.0.0)
tsort
redcarpet (3.6.1)
rspec (3.13.2)
rspec-core (~> 3.13.0)
rspec-expectations (~> 3.13.0)
rspec-mocks (~> 3.13.0)
rspec-core (3.13.6)
rspec-support (~> 3.13.0)
rspec-expectations (3.13.5)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0) rspec-support (~> 3.10.0)
rspec-mocks (3.13.7) rspec-mocks (3.10.2)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.13.0) rspec-support (~> 3.10.0)
rspec-support (3.13.6) rspec-support (3.10.2)
simplecov (0.22.0) simplecov (0.15.1)
docile (~> 1.1) docile (~> 1.1.0)
simplecov-html (~> 0.11) json (>= 1.8, < 3)
simplecov_json_formatter (~> 0.1) simplecov-html (~> 0.10.0)
simplecov-html (0.13.2) simplecov-html (0.10.2)
simplecov_json_formatter (0.1.4)
stringio (3.2.0)
syntax (1.2.2) syntax (1.2.2)
tsort (0.2.0) yard (0.9.26)
yard (0.9.38)
PLATFORMS PLATFORMS
ruby ruby
x86-mingw32 x86-mingw32
DEPENDENCIES DEPENDENCIES
base64
json json
openssl
rake rake
rdoc rdoc
redcarpet redcarpet
rspec rspec
simplecov simplecov (~> 0.15.0)
syntax syntax
yard yard
BUNDLED WITH BUNDLED WITH
2.6.2 2.1.4
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2013-2025 Josh Holtrop Copyright (c) 2013-2022 Josh Holtrop
MIT License MIT License
+1 -33
View File
@@ -4,39 +4,7 @@
Rscons (https://github.com/holtrop/rscons) is an open-source build system Rscons (https://github.com/holtrop/rscons) is an open-source build system
for developers. for developers.
It supports the following features:
* multi-threaded job execution Rscons is written in Ruby, and is inspired by [SCons](https://scons.org/) and [waf](https://waf.io/).
* auto-configuration
* built-in builders for several common operations
* out-of-the-box support for Assembly, C, C++, D, and LLVM
* 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. See [https://holtrop.github.io/rscons/index.html](https://holtrop.github.io/rscons/index.html) for User Guide and Installation instructions.
+8 -29
View File
@@ -8,10 +8,8 @@ end
require "rspec/core/rake_task" require "rspec/core/rake_task"
require "rake/clean" require "rake/clean"
require "fileutils" require "fileutils"
require "simplecov"
require "stringio"
CLEAN.include %w[build_test_run .yardoc yard coverage test_run] CLEAN.include %w[build_test_run .yardoc yard coverage test]
CLOBBER.include %w[dist gen large_project pkg] CLOBBER.include %w[dist gen large_project pkg]
task :build_dist do task :build_dist do
@@ -21,43 +19,24 @@ end
RSpec::Core::RakeTask.new(:spec, :example_string) do |task, args| RSpec::Core::RakeTask.new(:spec, :example_string) do |task, args|
ENV["specs"] = "1" ENV["specs"] = "1"
if args.example_string if args.example_string
ENV["partial_specs"] = "1"
task.rspec_opts = %W[-e "#{args.example_string}" -f documentation] task.rspec_opts = %W[-e "#{args.example_string}" -f documentation]
end end
end end
task :spec => :build_dist
task :spec do task :spec do
ENV.delete("specs") ENV.delete("specs")
end end
task :spec => :build_tests
task :spec do
unless ENV["rscons_dist_specs"]
original_stdout = $stdout
sio = StringIO.new
$stdout = sio
SimpleCov.collate Dir["coverage/.resultset.json", "coverage/bt*/.resultset.json"]
$stdout = original_stdout
sio.string.lines.each do |line|
$stdout.write(line) unless line =~ /Coverage report generated for/
end
end
end
task :build_tests do |task, args|
ENV["specs"] = "1"
sh "ruby -Ilib build_tests/build_tests.rb"
ENV.delete("specs")
end
# dspec task is useful to test the distributable release script, but is not # dspec task is useful to test the distributable release script, but is not
# useful for coverage information. # useful for coverage information.
desc "Dist Specs" desc "Dist Specs"
task :dspec, [:example_string] => :build_dist do |task, args| task :dspec, [:example_string] => :build_dist do |task, args|
FileUtils.rm_rf("test_run") FileUtils.mkdir_p("test")
FileUtils.mkdir_p("test_run") FileUtils.cp("dist/rscons", "test/rscons.rb")
FileUtils.cp("dist/rscons", "test_run/rscons.rb") ENV["dist_specs"] = "1"
ENV["rscons_dist_specs"] = "1" Rake::Task["spec"].execute(args.example_string)
Rake::Task["spec"].execute(args) ENV.delete("dist_specs")
Rake::Task["build_tests"].execute(args)
ENV.delete("rscons_dist_specs")
FileUtils.rm_f(Dir.glob(".rscons-*")) FileUtils.rm_f(Dir.glob(".rscons-*"))
end end
-10
View File
@@ -1,10 +0,0 @@
# Upgrading
## v3.2.0
- Replace any calls to `env.CFile()` builder with `env.Lex()` or `env.Yacc()` as required.
## v3.0.0
- Move `build` block contents outside of `build` block and remove `build` call.
- Replace `Environment.new()` calls with `env()`.
File diff suppressed because it is too large. Load diff
+4
View File
@@ -0,0 +1,4 @@
env do |env|
env.CFile("lexer.c", "lexer.l")
env.CFile("parser.c", "parser.y")
end
@@ -0,0 +1,3 @@
env do |env|
env.CFile("file.c", "foo.bar")
end
File renamed without changes.
File renamed without changes.
+2 -2
View File
@@ -1,10 +1,10 @@
debug = env("dbg", echo: :command) do |env| debug = env(echo: :command) do |env|
env['CFLAGS'] = '-O2' env['CFLAGS'] = '-O2'
env['CPPFLAGS'] = '-DSTRING="Debug Version"' env['CPPFLAGS'] = '-DSTRING="Debug Version"'
env.Program('program-debug.exe', Dir['src/*.c']) env.Program('program-debug.exe', Dir['src/*.c'])
end end
release = debug.clone("rls") do |env| release = debug.clone do |env|
env["CPPFLAGS"] = '-DSTRING="Release Version"' env["CPPFLAGS"] = '-DSTRING="Release Version"'
env.Program('program-release.exe', Dir['src/*.c']) env.Program('program-release.exe', Dir['src/*.c'])
end end
+2 -2
View File
@@ -1,4 +1,4 @@
env1 = env("e1", echo: :command) do |env| env1 = env(echo: :command) do |env|
env['CFLAGS'] = '-O2' env['CFLAGS'] = '-O2'
env.add_build_hook do |builder| env.add_build_hook do |builder|
builder.vars['CPPFLAGS'] = '-DSTRING="Hello"' builder.vars['CPPFLAGS'] = '-DSTRING="Hello"'
@@ -9,6 +9,6 @@ env1 = env("e1", echo: :command) do |env|
env.Program('program.exe', Dir['src/*.c']) env.Program('program.exe', Dir['src/*.c'])
end end
env2 = env1.clone("e2") do |env| env2 = env1.clone do |env|
env.Program('program2.exe', Dir['src/*.c']) env.Program('program2.exe', Dir['src/*.c'])
end end
@@ -1,12 +0,0 @@
configure do
check_c_compiler "clang", use: "clang"
check_c_compiler
end
env "t1" do |env|
env.Program("test_gcc.exe", "simple.c")
end
env "t2", use: "clang" do |env|
env.Program("test_clang.exe", "simple.c")
end
@@ -1,5 +0,0 @@
ENV["PATH"] = ""
configure do
check_cfg package: "mypackage"
end
@@ -1,12 +0,0 @@
configure do
check_cxx_compiler "clang++", use: "clang"
check_cxx_compiler
end
env "t1" do |env|
env.Program("test_gcc.exe", "simple.cc")
end
env "t2", use: "clang" do |env|
env.Program("test_clang.exe", "simple.cc")
end
@@ -1,3 +1,3 @@
configure do configure do
check_d_compiler "gdc", "ldc2", "ldc" check_d_compiler "gdc", "ldc2"
end end
@@ -1,12 +0,0 @@
configure do
check_d_compiler "ldc2", use: "ldc2"
check_d_compiler
end
env "t1" do |env|
env.Program("test_gcc.exe", "simple.d")
end
env "t2", use: "ldc2" do |env|
env.Program("test_ldc2.exe", "simple.d")
end
@@ -1,8 +0,0 @@
configure do
check_d_compiler "ldc2"
check_lib "z"
end
env(echo: :command) do |env|
env.Program("simple.exe", "simple.d")
end
@@ -1,6 +0,0 @@
configure do
check_c_compiler
end
env do |env|
puts "Prefix is #{Task["configure"]["prefix"]}"
end
-6
View File
@@ -1,6 +0,0 @@
#include <iostream>
int main(int argc, char *argv[])
{
std::cout << "Hi" << std::endl;
}
-7
View File
@@ -1,7 +0,0 @@
import std.stdio;
int main()
{
writeln("Hello");
return 0;
}
-4
View File
@@ -1,4 +0,0 @@
env do |env|
env.Lex("lexer.c", "lexer.l")
env.Yacc("parser.c", "parser.y")
end
+2 -5
View File
@@ -1,7 +1,4 @@
env(echo: :command) do |env| env(echo: :command) do |env|
env["LIBS"] << "mylib" env.Program('library.exe', ['lib.a', 'three.c'])
env["LIBPATH"] << "." env.Library("lib.a", ['one.c', 'two.c'], 'CPPFLAGS' => ['-Dmake_lib'])
env.Program("library.exe", "one.c")
env.depends("library.exe", "libmylib.a")
env.Library("libmylib.a", ["two.c", "three.c"], "CPPFLAGS" => ["-Dmake_lib"])
end end
+2 -5
View File
@@ -1,11 +1,8 @@
#include <stdio.h> #include <stdio.h>
void two(); #ifdef make_lib
void three();
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
two();
printf("Library\n"); printf("Library\n");
three();
} }
#endif
+1 -1
View File
@@ -1,4 +1,4 @@
env(echo: :command) do |env| env(echo: :command) do |env|
env["ARCMD"] = %w[ar rc ${_TARGET} ${_SOURCES}] env["ARCMD"] = %w[ar rcf ${_TARGET} ${_SOURCES}]
env.Library("lib.a", glob("*.c")) env.Library("lib.a", glob("*.c"))
end end
-3
View File
@@ -1,3 +0,0 @@
void three(void)
{
}
-3
View File
@@ -1,3 +0,0 @@
void two(void)
{
}
-4
View File
@@ -1,4 +0,0 @@
env do |env|
env["LLVMAS_FLAGS"] += %w[-Wno-override-module]
env.Program("llvmtest.exe", %w[main.c one.ll])
end
-8
View File
@@ -1,8 +0,0 @@
configure do
check_c_compiler "clang"
end
env do |env|
env["LLVMAS_FLAGS"] += %w[-Wno-override-module]
env.Program("llvmtest.exe", %w[one.ll two.ll main2.c], direct: true)
end
-6
View File
@@ -1,6 +0,0 @@
extern int one(void);
int main(int argc, char * argv[])
{
return one();
}
-9
View File
@@ -1,9 +0,0 @@
extern int one(void);
extern int two(void);
int main(int argc, char * argv[])
{
one();
two();
return 0;
}
-9
View File
@@ -1,9 +0,0 @@
@str = private unnamed_addr constant [12 x i8] c"hello world\00"
declare i32 @puts(ptr nocapture) nounwind
define i32 @one()
{
call i32 @puts(ptr @str)
ret i32 0
}
-9
View File
@@ -1,9 +0,0 @@
@str = private unnamed_addr constant [12 x i8] c"hello again\00"
declare i32 @puts(ptr nocapture) nounwind
define i32 @two()
{
call i32 @puts(ptr @str)
ret i32 0
}
-14
View File
@@ -1,14 +0,0 @@
class B < Builder
def run(*args)
puts @target
true
end
end
env do |env|
env.add_builder(B)
env.B("one", File.expand_path("two"))
env.B("two")
env.B("three")
env.depends("two", File.expand_path("three"))
end
-15
View File
@@ -1,15 +0,0 @@
class B < Builder
def run(*args)
puts "B:#{@target}"
true
end
end
env do |env|
env.add_builder(B)
env.B("one")
env.B("two")
env.B("three")
env.Barrier(:bar, %w[two three])
env.depends("one", :bar)
end
@@ -1,13 +0,0 @@
env("e", echo: :command) do |env|
source_file = "#{env.build_root}/src/foo.c"
FileUtils.mkdir_p(File.dirname(source_file))
File.open(source_file, "w") do |fh|
fh.puts(<<-EOF)
int main()
{
return 29;
}
EOF
end
env.Program("foo.exe", source_file)
end
@@ -1,7 +1,7 @@
class MyObject < Rscons::Builder class MyObject < Rscons::Builder
def run(options) def run(options)
if @builder if @builder
if File.exist?(@target) if File.exists?(@target)
true true
else else
false false
-7
View File
@@ -1,7 +0,0 @@
env do |env|
env.Program('simple.exe', Dir['*.c'])
end
clean do
puts "custom clean action"
end
-5
View File
@@ -1,5 +0,0 @@
default do
env do |env|
env.Program('simple.exe', Dir['*.c'])
end
end
-1
View File
@@ -1,6 +1,5 @@
env(echo: :command) do |env| env(echo: :command) do |env|
env["LD"] = "gcc" env["LD"] = "gcc"
mkdir_p("libdir")
env["LIBPATH"] += ["libdir"] env["LIBPATH"] += ["libdir"]
env.Program('simple.exe', Dir['*.c']) env.Program('simple.exe', Dir['*.c'])
end end
+1 -1
View File
@@ -4,7 +4,7 @@ class TestBuilder < Rscons::Builder
true true
else else
if @target == "two" if @target == "two"
return false unless File.exist?("one") return false unless File.exists?("one")
end end
wait_time = @env.expand_varref("${wait_time}", @vars) wait_time = @env.expand_varref("${wait_time}", @vars)
@command = ["ruby", "-e", "require 'fileutils'; sleep #{wait_time}; FileUtils.touch('#{@target}');"] @command = ["ruby", "-e", "require 'fileutils'; sleep #{wait_time}; FileUtils.touch('#{@target}');"]
+1 -1
View File
@@ -3,5 +3,5 @@ env do |env|
env["CFLAGS"] += %w[-S] env["CFLAGS"] += %w[-S]
env.Object("one.ssss", "one.c", "CPPFLAGS" => ["-DONE"]) env.Object("one.ssss", "one.c", "CPPFLAGS" => ["-DONE"])
env.Object("two.sss", "two.c") env.Object("two.sss", "two.c")
env.Program("two_sources.exe", %w[one.ssss two.sss], "ASFLAGS" => env["ASFLAGS"] + %w[-x assembler-with-cpp]) env.Program("two_sources.exe", %w[one.ssss two.sss], "ASFLAGS" => env["ASFLAGS"] + %w[-x assembler])
end end
-3
View File
@@ -1,6 +1,3 @@
#ifdef ONE #ifdef ONE
#error ONE should not be defined #error ONE should not be defined
#endif #endif
void two(void)
{
}
@@ -1,4 +0,0 @@
env "src" do |env|
env["CPPPATH"] += glob("src/**")
env.Program("^/src", glob("src/**/*.c"))
end
-3
View File
@@ -1,3 +0,0 @@
default do
touch "#{build_dir}/a.file"
end
@@ -3,6 +3,7 @@ env(echo: :command) do |env|
env.add_build_hook do |builder| env.add_build_hook do |builder|
if builder.name == "Object" && builder.sources.first =~ %r{one\.c} if builder.name == "Object" && builder.sources.first =~ %r{one\.c}
builder.vars["CFLAGS"] << "-O1" builder.vars["CFLAGS"] << "-O1"
builder.sources = ['src/two/two.c']
elsif builder.name == "Object" && builder.target =~ %r{two\.o} elsif builder.name == "Object" && builder.target =~ %r{two\.o}
new_vars = builder.vars.clone new_vars = builder.vars.clone
new_vars["CFLAGS"] << "-O2" new_vars["CFLAGS"] << "-O2"
-15
View File
@@ -1,15 +0,0 @@
class Custom < Rscons::Builder
def run(options)
print_run_message("#{name} #{target}", nil)
true
end
end
env do |env|
env.add_builder(Custom)
env.Custom("t3", :phony1)
env.Custom(:phony1, "t2")
env.Custom("t2", :phony2)
env.Custom(:phony2, "t1")
env.Custom("t1", [])
end
@@ -1,13 +0,0 @@
variant "debug"
variant "release"
with_variants do
env do |env|
if variant("debug")
env["CPPDEFINES"] << "DEBUG"
else
env["CPPDEFINES"] << "NDEBUG"
end
env.Program("^/prog.exe", "prog.c")
end
end
-14
View File
@@ -1,14 +0,0 @@
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
+130 -465
View File
@@ -6,13 +6,12 @@ It supports the following features:
* multi-threaded job execution * multi-threaded job execution
* auto-configuration * auto-configuration
* built-in builders for several common operations * built-in builders for several common operations
* out-of-the-box support for Assembly, C, C++, D, and LLVM * out-of-the-box support for C, C++, and D languages
* extensibility for other languages or custom builders * extensibility for other languages or custom builders
* compatible with Windows, Linux, OS X, and FreeBSD * compatible with Windows, Linux, OS X, and FreeBSD
* colorized output with build progress * colorized output with build progress
* build hooks * build hooks
* user-defined tasks with dependencies and custom parameters * user-defined tasks with dependencies and custom parameters
* build variants
At its core, Rscons is mainly an engine to: At its core, Rscons is mainly an engine to:
@@ -68,8 +67,8 @@ incorrect decision being made to not rebuild when a rebuild is necessary.
### Build Flexibility ### Build Flexibility
Rscons supports multiple configurations of compilation flags or build options Rscons supports multiple configurations of compilation flags or build options
across multiple environments or build variants to build output files in across multiple environments to build output files in different ways according
different ways according to the user's desire. to the user's desire.
For example, the same source files can be built into a release executable, but 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 also compiled with different compilation flags or build options into a test
executable. executable.
@@ -144,52 +143,17 @@ different version control systems):
Rscons is typically invoked from the command-line as `./rscons`. Rscons is typically invoked from the command-line as `./rscons`.
Usage: ./rscons [global options] [[task] [task options] ...] ./rscons [global options] [[task] [task options] ...]
Global options: Global options:
-A, --all -b BUILD, --build=BUILD Set build directory (default: build)
Show all tasks (even those without descriptions) in task list. Use in -f FILE Use FILE as Rsconscript
conjunction with the -T argument. -F, --show-failure Show failed command log from previous build and exit
-h, --help Show rscons help and exit
-b BUILD, --build=BUILD -j N, --nthreads=N Set number of threads
Set build directory (default: build). -r COLOR, --color=COLOR Set color mode (off, auto, force)
-v, --verbose Run verbosely
-e VS, --variants=VS --version Show rscons version and exit
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. 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 Any parameters beginning with a "-" that follow a task are interpreted as task
@@ -217,19 +181,6 @@ Rscons execution.
The user can also invoke Rscons with the `-v` global command-line option which The user can also invoke Rscons with the `-v` global command-line option which
will cause Rscons to print each command it is executing. 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 #> The Build Script
Rscons looks for instructions for what to build by reading a build script file Rscons looks for instructions for what to build by reading a build script file
@@ -237,8 +188,10 @@ called `Rsconscript` (or `Rsconscript.rb`).
Here is a simple example `Rsconscript` file: Here is a simple example `Rsconscript` file:
```ruby ```ruby
env do |env| default do
env.Program("myprog.exe", glob("src/**/*.c")) Environment.new do |env|
env.Program("myprog.exe", glob("src/**/*.c"))
end
end end
``` ```
@@ -250,7 +203,7 @@ The `Rsconscript` file is a Ruby script.
##> Tasks ##> Tasks
Tasks are a high-level user interface for performing functionality in a build Tasks are the high-level user interface for performing functionality in a build
script. script.
Tasks can create Environments that perform compilation/linking steps. Tasks can create Environments that perform compilation/linking steps.
Tasks can also execute arbitrary commands or perform any miscellaneous logic. Tasks can also execute arbitrary commands or perform any miscellaneous logic.
@@ -265,17 +218,21 @@ which they were added.
Example: Example:
```ruby ```ruby
proj_env = env do |env| task "build" do
env.Program("^/proj.elf", glob("src/**/*.c")) Environment.new do |env|
env.Program("^^/proj.elf", glob("src/**/*.c"))
end
end end
task "flash" do task "flash", deps: "build" do
sh "nrfjprog", "-f", "NRF52", "--program", proj_env.expand("^/proj.elf") sh "nrfjprog", "-f", "NRF52", "--program", env.expand("^^/proj.elf")
end end
``` ```
In this example, the `flash` task would first build the proj.elf target and In this example, the `flash` task depends on the `build` task.
then flash it to target with the nrfjprog program. So if the project had not yet been built, and the user executes
`./rscons flash`, the project would first be built and then flashed to the
target.
If the `task` method is called again with the name of an already existing task, If the `task` method is called again with the name of an already existing task,
the task is not overwritten, but rather modified. the task is not overwritten, but rather modified.
@@ -283,41 +240,9 @@ 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 Any action block is appended to the task's list of action blocks to execute
when the task is executed. when the task is executed.
For example, with the `Rsconscript`:
```ruby
task "a" do
puts "A"
end
task "b" do
puts "B"
end
task "c", depends: "a" do
puts "C1"
end
task "c", depends: "b" do
puts "C2"
end
```
The following behavior is observed:
$ ./rscons c
A
B
C1
C2
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 ###> Task Parameters
Tasks can accept parameters. Tasks can also take parameters.
Parameters are defined by the build script author, and have default values. Parameters are defined by the build script author, and have default values.
The user can override parameter values on the command line. The user can override parameter values on the command line.
@@ -336,7 +261,7 @@ task "build", params: [
param("myparam", "defaultvalue", true, "My special parameter"), param("myparam", "defaultvalue", true, "My special parameter"),
param("xyz", nil, false, "Enable the xyz feature"), param("xyz", nil, false, "Enable the xyz feature"),
] do |task, params| ] do |task, params|
env do |env| Environment.new do |env|
env["CPPDEFINES"] << "SOMEMACRO=#{params["myparam"]}" env["CPPDEFINES"] << "SOMEMACRO=#{params["myparam"]}"
if params["flag"] if params["flag"]
env["CPPDEFINES"] << "ENABLE_FEATURE_XYZ" env["CPPDEFINES"] << "ENABLE_FEATURE_XYZ"
@@ -402,7 +327,7 @@ argument (task name) automatically filled in by the shortcut method.
For example: For example:
```ruby ```ruby
default depends: "unpack_compiler" do default deps: "unpack_compiler" do
puts "default task" puts "default task"
end end
``` ```
@@ -410,7 +335,7 @@ end
is equivalent to: is equivalent to:
```ruby ```ruby
task "default", depends: "unpack_compiler" do task "default", deps: "unpack_compiler" do
puts "default task" puts "default task"
end end
``` ```
@@ -470,13 +395,12 @@ end
See ${#Configuring the Project} for more details on how to make use of the See ${#Configuring the Project} for more details on how to make use of the
configuration functionality that Rscons provides. configuration functionality that Rscons provides.
Configure blocks must be defined in the Rsconscript file before any
environments are created.
####> Default Task ####> Default Task
The `default` task is special in that Rscons will execute it if no other task 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. 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 The default task can also be used to declare a dependency on another task that
would effectively become the default. would effectively become the default.
@@ -487,11 +411,11 @@ task "build" do
... ...
end end
task "flash", depends: "build" do task "flash" do
... ...
end end
default depends: "build" default deps: "build"
``` ```
Then when the user runs `./rscons` the "build" task will be executed. Then when the user runs `./rscons` the "build" task will be executed.
@@ -508,8 +432,7 @@ It will not remove the cached configuration options.
The `distclean` task is built-in to Rscons. The `distclean` task is built-in to Rscons.
It removes all built target files and all cached configuration options. 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 Generally it will get the project directory back to the state it was in when
unpacked or checked out, before any configuration or build operations took unpacked before any configuration or build operations took place.
place.
It will not remove items installed by an Install builder. It will not remove items installed by an Install builder.
####> Install Task ####> Install Task
@@ -811,18 +734,17 @@ If set, a build define of the specified String will be added to the
##> Building Targets ##> Building Targets
Building target files is accomplished by using Environments. Building target files is accomplished by using Environments.
Environments can be created at the top level of the build script, or from Environments are typically created within the default task or any user-defined
within a task action block. tasks.
Environments are created with the Here is an example `default` task block demonstrating how to create an
[`env`](../yard/Rscons/Script/GlobalDsl.html#env-instance_method) build script Environment and register a build target:
method.
Here is an example build script that creates an Environment and registers a
Program build target:
```ruby ```ruby
env do |env| default do
env.Program("myprog.exe", glob("src/**/*.c")) Environment.new do |env|
env.Program("myprog.exe", glob("src/**/*.c"))
end
end end
``` ```
@@ -837,21 +759,24 @@ An Environment includes:
- a collection of construction variables - a collection of construction variables
- a collection of build hooks - a collection of build hooks
- a collection of user-registered build targets - a collection of user-registered build targets
- a build root directory - a build root
All build targets must be registered within an `Environment`. All build targets must be registered within an `Environment`.
If the user does not specify a name for the environment, a name will be 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 automatically generated based on the Environment's internal ID, for example
"e.1". "e.1".
The Environment's build root is a directory with the same name as the The Environment's build root is a directory created within the top-level
Environment, created within the top-level Rscons build directory. Rscons build directory.
It is based on the Environment name.
By default it holds all intermediate files generated by Rscons that are needed By default it holds all intermediate files generated by Rscons that are needed
to produce a user-specified build target. to produce a user-specified build target.
For example, for the `Rsconscript`: For example, for the `Rsconscript`:
```ruby ```ruby
env "myproj" do |env| default do
env.Program("myprog.exe", glob("src/**/*.c")) Environment.new(name: "myproj") do |env|
env.Program("myprog.exe", glob("src/**/*.c"))
end
end end
``` ```
@@ -861,12 +786,6 @@ Assuming a top-level build directory of "build", the Environment's build root
would be "build/myproj". would be "build/myproj".
This keeps the intermediate generated build artifacts separate from the source This keeps the intermediate generated build artifacts separate from the source
files. 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 ###> Construction Variables
@@ -878,9 +797,11 @@ construction variables.
Example: Example:
```ruby ```ruby
env do |env| default do
env["CCFLAGS"] += %w[-O2 -Wall] Environment.new do |env|
env["LIBS"] += %w[m] env["CCFLAGS"] += %w[-O2 -Wall]
env["LIBS"] += %w[m]
end
end end
``` ```
@@ -892,7 +813,6 @@ It also instructs the linker to link against the `m` library.
* uppercase strings - the default construction variables that Rscons uses * uppercase strings - the default construction variables that Rscons uses
* strings beginning with "_" - set and used internally by builders * strings beginning with "_" - set and used internally by builders
* lowercase strings with a ":" as "#{task_name}:#{parameter_name}" - set to task parameter values
* symbols, lowercase strings - reserved as user-defined construction variables * symbols, lowercase strings - reserved as user-defined construction variables
###> Builders ###> Builders
@@ -914,8 +834,7 @@ 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 The `sources` parameter is the path or paths to the input file(s) to be used
by the builder. by the builder.
In the `target` and `sources` parameters, the user can explicitly refer to a 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 "^/", or to path within the Environment's build root by beginning the path with "^/".
a path within the Rscons top-level built directory by beginning with "^^/".
The `vars` parameter is an optional Hash which can include construction The `vars` parameter is an optional Hash which can include construction
variables to be used for this build target. variables to be used for this build target.
Any construction variable values specified in this parameter will override Any construction variable values specified in this parameter will override
@@ -925,11 +844,11 @@ There are several default builders that are built-in to Rscons:
* `Command`, which executes a user-defined command to produce the target. * `Command`, which executes a user-defined command to produce the target.
* `Copy`, which copies files or directories to a specified destination. * `Copy`, which copies files or directories to a specified destination.
* `CFile`, which builds a C or C++ source file from a lex or yacc input file.
* `Directory`, which creates a directory. * `Directory`, which creates a directory.
* `Disassemble`, which disassembles an object file to a disassembly listing. * `Disassemble`, which disassembles an object file to a disassembly listing.
* `Install`, which installs files or directories to a specified destination. * `Install`, which installs files or directories to a specified destination.
* `InstallDirectory`, which creates a directory in an install destination. * `InstallDirectory`, which creates a directory in an install destination.
* `Lex`, which builds a source file from a lex input file.
* `Library`, which collects object files into a static library archive file. * `Library`, which collects object files into a static library archive file.
* `Object`, which compiles source files to produce an object file. * `Object`, which compiles source files to produce an object file.
* `Preprocess`, which invokes the C/C++ preprocessor on a source file. * `Preprocess`, which invokes the C/C++ preprocessor on a source file.
@@ -939,7 +858,6 @@ There are several default builders that are built-in to Rscons:
* `SharedObject`, which compiles source files to produce an object file, in a * `SharedObject`, which compiles source files to produce an object file, in a
way that is able to be used to create a shared library. way that is able to be used to create a shared library.
* `Size`, which runs the 'size' utility on an executable file. * `Size`, which runs the 'size' utility on an executable file.
* `Yacc`, which builds a source file from a yacc input file.
####> The Command Builder ####> The Command Builder
@@ -966,6 +884,17 @@ The `Command` builder supports the following construction variables:
`CMD_STDOUT` is expanded for variable references, so the token `${_TARGET}` `CMD_STDOUT` is expanded for variable references, so the token `${_TARGET}`
can be used, for example. can be used, for example.
####> The CFile Builder
```ruby
env.CFile(target, source)
# Example
env.CFile("^/parser/parser.c", "parser.y")
```
The `CFile` builder will generate a C or C++ source file from a lex (.l, .ll)
or yacc (.y, .yy) input file.
####> The Copy Builder ####> The Copy Builder
```ruby ```ruby
@@ -1038,17 +967,6 @@ the `InstallDirectory` builder.
The `uninstall` task removes targets created by the `InstallDirectory` builder The `uninstall` task removes targets created by the `InstallDirectory` builder
but not by the `Directory` builder. but not by the `Directory` builder.
####> The Lex Builder
```ruby
env.Lex(target, source)
# Example
env.Lex("^/lexer.c", "lexer.l")
```
The `Lex` builder will generate a source file from a lex (.l, .ll)
input file.
####> The Library Builder ####> The Library Builder
```ruby ```ruby
@@ -1179,20 +1097,9 @@ stores its output in the target file.
The size executable can be specified with the `SIZE` construction variable, The size executable can be specified with the `SIZE` construction variable,
and flags can be specified with `SIZEFLAGS`. and flags can be specified with `SIZEFLAGS`.
####> The Yacc Builder
```ruby
env.Yacc(target, source)
# Example
env.Yacc("^/parser.c", "parser.y")
```
The `Yacc` builder will generate a source file from a yacc (.y, .yy)
input file.
###> Phony Targets ###> Phony Targets
Rscons supports phony build targets. rscons supports phony build targets.
Normally, a builder produces an output file, and executes whenever the input Normally, a builder produces an output file, and executes whenever the input
files or command have changed. files or command have changed.
A phony build target can be used to register a builder that does not produce A phony build target can be used to register a builder that does not produce
@@ -1235,15 +1142,17 @@ build target or source file names.
Example: Example:
```ruby ```ruby
env do |env| default do
env["CFLAGS"] << "-Wall" Environment.new do |env|
env.add_build_hook do |builder| env["CFLAGS"] << "-Wall"
# Compile sources from under src/tests without the -Wall flag. env.add_build_hook do |builder|
if builder.sources.first =~ %r{src/tests/} # Compile sources from under src/tests without the -Wall flag.
builder.vars["CFLAGS"] -= %w[-Wall] if builder.sources.first =~ %r{src/tests/}
builder.vars["CFLAGS"] -= %w[-Wall]
end
end end
env.Program("program.exe", glob("src/**/*.c"))
end end
env.Program("program.exe", glob("src/**/*.c"))
end end
``` ```
@@ -1257,7 +1166,7 @@ Build hooks and post-build hooks can register new build targets.
###> Barriers ###> Barriers
Normally Rscons will parallelize all builders executed within an Environment. Normally Rscons will parallelize all builders.
A barrier can be used to separate sets of build targets. A barrier can be used to separate sets of build targets.
All build targets registered before the barrier is created will be built before All build targets registered before the barrier is created will be built before
Rscons will schedule any build targets after the barrier. Rscons will schedule any build targets after the barrier.
@@ -1267,140 +1176,14 @@ In other words, build targets are not parallelized across a barrier.
env.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` method call creates an Environment, so two environments are created.
When an Environment is created within a `with_variants` block, the
Environment's build directory name has the active variant(s) keys appended to
the given Environment name, 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.
The key for a variant is the variant's name by default but can be overridden by
passing a `:key` value to the `variant` method.
A `nil` value for the `:key` parameter will omit that variant from appearing
in the environment's build directory name.
For example:
```ruby
variant "debug"
variant "release", key: nil
with_variants do
env "prog" do |env|
end
end
```
In this example, one "prog" environment will be created for the "debug" variant
with the build directory "prog-debug", and another "prog" environment will be
created for the "release" variant with the build directory "prog".
The build directory for the release variant of the "prog" environment is just
"prog" instead of "prog-release" because the key for the "release" variant is
set to `nil`.
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 ##> Build Script Methods
`rscons` provides several methods that a build script can use. `rscons` provides several methods that a build script can use.
* `autoconf` (see ${#Configure Task}) * `autoconf` (see ${#Configure Task})
* `build_dir` which returns the path to the top-level Rscons build directory
* `clean` (see ${#Clean Task}) * `clean` (see ${#Clean Task})
* `configure` (see ${#Configure Task}) * `configure` (see ${#Configure Task})
* `default` (see ${#Default Task}) * `default` (see ${#Default Task})
* `download` (see ${#Downloading Files: The download Method})
* `distclean` (see ${#Distclean Task}) * `distclean` (see ${#Distclean Task})
* `glob` (see ${#Finding Files: The glob Method}) * `glob` (see ${#Finding Files: The glob Method})
* `install` (see ${#Install Task}) * `install` (see ${#Install Task})
@@ -1414,10 +1197,6 @@ The command `./rscons --variants kde,release` would build just "prog-kde-release
* `sh` (see (${#Executing Commands: The sh Method}) * `sh` (see (${#Executing Commands: The sh Method})
* `task` (see ${#Tasks}) * `task` (see ${#Tasks})
* `uninstall` (see ${#Uninstall Task}) * `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 Additionally, the following methods from the Ruby
[FileUtils](https://ruby-doc.org/stdlib-3.1.0/libdoc/fileutils/rdoc/FileUtils.html) [FileUtils](https://ruby-doc.org/stdlib-3.1.0/libdoc/fileutils/rdoc/FileUtils.html)
@@ -1458,39 +1237,16 @@ rather than file system directory ordering).
Example use: Example use:
```ruby ```ruby
env do |env| default do
env.Program("mytests", glob("src/**/*.cc", "test/**/*.cc")) Environment.new do |env|
env.Program("mytests", glob("src/**/*.cc", "test/**/*.cc"))
end
end end
``` ```
This example would build the `mytests` executable from all `.cc` source files This example would build the `mytests` executable from all `.cc` source files
found recursively under the `src` or `test` directory. 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
download "https://ftp.gnu.org/gnu/gcc/gcc-#{gcc_version}/gcc-#{gcc_version}.tar.xz",
"#{build_dir}/gcc-#{gcc_version}.tar.xz",
sha256_sum: gcc_checksum
end
```
The `download` method downloads the file specified by the URL in the first
parameter, and writes it to the local file specified by the second parameter.
If the `:sha256sum` option is given, this causes two changes to the default
behavior:
* If Rscons finds the local file and it already has this checksum, then the
file will not be downloaded again.
* After downloading, Rscons verifies that the checksum matches what is given
and exits with an error if it does not.
###> PATH Management ###> PATH Management
`rscons` provides methods for management of the `PATH` environment variable. `rscons` provides methods for management of the `PATH` environment variable.
@@ -1598,18 +1354,6 @@ default do
end 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 ##> Extending Rscons
### Adding New Languages ### Adding New Languages
@@ -1617,8 +1361,8 @@ end
The `Object` and `SharedObject` builders that ship with Rscons have an API that The `Object` and `SharedObject` builders that ship with Rscons have an API that
allows the user to register extra languages that can be suppored by the allows the user to register extra languages that can be suppored by the
builders. builders.
In fact, the built-in support for assembly, C, C++, D, and LLVM compilation In fact, the built-in support for assembly, C, C++, and D compilation all make
all make use of this built-in API. use of this built-in API.
To see an example of how this API is used, see the To see an example of how this API is used, see the
`lib/rscons/builders/lang/*.rb` files in the Rscons source repository. `lib/rscons/builders/lang/*.rb` files in the Rscons source repository.
For example, here is how the C++ language is registered: For example, here is how the C++ language is registered:
@@ -1662,7 +1406,7 @@ class Rscons::Builders::Mine < Rscons::Builder
end end
default do default do
env do |env| Environment.new do |env|
env.add_builder(Rscons::Builders::Mine) env.add_builder(Rscons::Builders::Mine)
end end
end end
@@ -1685,7 +1429,7 @@ Rscons::DEFAULT_BUILDERS << :Special
load "SpecialBuilder.rb" load "SpecialBuilder.rb"
default do default do
env do |env| Environment.new do |env|
# A build target using the "Special" builder can be registered. # A build target using the "Special" builder can be registered.
env.Special("target", "source") env.Special("target", "source")
end end
@@ -1912,45 +1656,40 @@ ${include lib/rscons/default_construction_variables.rb}
### Example: Building a C Program ### Example: Building a C Program
```ruby ```ruby
env do |env| default do
env["CFLAGS"] << "-Wall" Environment.new do |env|
env.Program("program", glob("src/**/*.c")) env["CFLAGS"] << "-Wall"
env.Program("program", glob("src/**/*.c"))
end
end end
``` ```
### Example: Building a D Program ### Example: Building a D Program
```ruby ```ruby
env do |env| default do
env["DFLAGS"] << "-Wall" Environment.new do |env|
env.Program("program", glob("src/**/*.d")) env["DFLAGS"] << "-Wall"
end env.Program("program", glob("src/**/*.d"))
``` end
### Example: Building a Program from C and LLVM Sources
```ruby
env do |env|
env["CFLAGS"] << "-Wall"
env.Program("program", glob("src/**/*.{c,ll}"))
end end
``` ```
### Example: Cloning an Environment ### Example: Cloning an Environment
```ruby ```ruby
main_env = env do |env| default do
env["CFLAGS"] = ["-fshort-enums", "-O3"] main_env = Environment.new do |env|
env["CPPDEFINES"] << "SOME_DEFINE" env["CFLAGS"] = ["-DSOME_DEFINE", "-O3"]
env["LIBS"] = ["SDL"] env["LIBS"] = ["SDL"]
env.Program("program", glob("src/**/*.cc")) env.Program("program", glob("src/**/*.cc"))
end end
test_env = main_env.clone do |env| debug_env = main_env.clone do |env|
env["CFLAGS"] -= ["-O3"] env["CFLAGS"] -= ["-O3"]
env["CFLAGS"] += ["-g", "-O0"] env["CFLAGS"] += ["-g", "-O0"]
env["CPPDEFINES"] = "ENABLE_TESTS" env.Program("program-debug", glob("src/**/*.cc"))
env.Program("program-test", glob("src/**/*.cc")) end
end end
``` ```
@@ -1970,123 +1709,49 @@ EOF
end end
end end
env do |env| default do
env.add_builder(GenerateFoo) Environment.new do |env|
env.GenerateFoo("foo.h", []) env.add_builder(GenerateFoo)
env.Program("a.out", glob("*.c")) env.GenerateFoo("foo.h", [])
env.Program("a.out", glob("*.c"))
end
end end
``` ```
### Example: Using different compilation flags for some sources ### Example: Using different compilation flags for some sources
```ruby ```ruby
env do |env| default do
env["CFLAGS"] = ["-O3", "-Wall"] Environment.new do |env|
env.add_build_hook do |builder| env["CFLAGS"] = ["-O3", "-Wall"]
if builder.sources.first =~ %r{src/third-party/} env.add_build_hook do |builder|
build_op[:vars]["CFLAGS"] -= ["-Wall"] if builder.sources.first =~ %r{src/third-party/}
build_op[:vars]["CFLAGS"] -= ["-Wall"]
end
end end
env.Program("program", glob("**/*.cc"))
end end
env.Program("program", glob("**/*.cc"))
end end
``` ```
### Example: Creating a static library ### Example: Creating a static library
```ruby ```ruby
env do |env| default do
env.Library("mylib.a", glob("src/**/*.c")) Environment.new do |env|
env.Library("mylib.a", glob("src/**/*.c"))
end
end end
``` ```
### Example: Creating a C++ parser source from a Yacc/Bison input file ### Example: Creating a C++ parser source from a Yacc/Bison input file
```ruby ```ruby
env do |env|
env.Yacc("^/parser.tab.cc", "parser.yy")
end
```
### Example: GCC Cross Compiler
```ruby
binutils_version = "2.35"
binutils_checksum = "1b11659fb49e20e18db460d44485f09442c8c56d5df165de9461eb09c8302f85"
gcc_version = "10.2.0"
gcc_checksum = "b8dd4368bb9c7f0b98188317ee0254dd8cc99d1e3a18d0ff146c855fe16c1d8c"
install_path = File.expand_path("i686-elf-gcc")
target = "i686-elf"
path_prepend "#{install_path}/bin"
configure do
check_c_compiler "gcc"
check_program "make"
check_program "bison"
check_program "flex"
check_program "texi2any", on_fail: "Install the texinfo package"
check_program "wget"
check_lib "gmp", on_fail: "Install the libgmp-dev package"
check_lib "mpc", on_fail: "Install the libmpc-dev package"
check_lib "mpfr", on_fail: "Install the libmpfr-dev package"
end
default do default do
unless Dir.exist?(install_path) Environment.new do |env|
env.CFile("^/parser.tab.cc", "parser.yy")
# Download archives.
download "https://ftp.gnu.org/gnu/binutils/binutils-#{binutils_version}.tar.xz",
"#{build_dir}/binutils-#{binutils_version}.tar.xz",
sha256sum: binutils_checksum
download "https://ftp.gnu.org/gnu/gcc/gcc-#{gcc_version}/gcc-#{gcc_version}.tar.xz",
"#{build_dir}/gcc-#{gcc_version}.tar.xz",
sha256sum: gcc_checksum
# Extract archives.
sh "tar", "xJf", "binutils-#{binutils_version}.tar.xz",
chdir: build_dir
sh "tar", "xJf", "gcc-#{gcc_version}.tar.xz",
chdir: build_dir
# Build binutils.
rm_rf "#{build_dir}/build-binutils"
mkdir_p "#{build_dir}/build-binutils"
cd "#{build_dir}/build-binutils" do
sh %W[../binutils-#{binutils_version}/configure
--target=#{target} --prefix=#{install_path} --with-sysroot --disable-nls
--disable-werror]
sh "make"
sh "make install"
end
# Build gcc.
rm_rf "#{build_dir}/build-gcc"
mkdir_p "#{build_dir}/build-gcc"
cd "#{build_dir}/build-gcc" do
sh %W[../gcc-#{gcc_version}/configure
--target=#{target} --prefix=#{install_path} --disable-nls
--enable-languages=c,c++ --without-headers]
sh "make all-gcc"
sh "make all-target-libgcc"
sh "make install-gcc"
sh "make install-target-libgcc"
end
end end
end end
clean do
rm_f "#{build_dir}/binutils-#{binutils_version}.tar.xz"
rm_rf "#{build_dir}/binutils-#{binutils_version}"
rm_rf "#{build_dir}/build-binutils"
rm_f "#{build_dir}/gcc-#{gcc_version}.tar.xz"
rm_rf "#{build_dir}/gcc-#{gcc_version}"
rm_rf "#{build_dir}/build-gcc"
end
distclean do
rm_rf install_path
end
``` ```
##> ./configure && make ##> ./configure && make
+2 -14
View File
@@ -23,14 +23,13 @@ module Rscons
# Names of the default builders which will be added to all newly created # Names of the default builders which will be added to all newly created
# {Environment} objects. # {Environment} objects.
DEFAULT_BUILDERS = [ DEFAULT_BUILDERS = [
:Barrier, :CFile,
:Command, :Command,
:Copy, :Copy,
:Directory, :Directory,
:Disassemble, :Disassemble,
:Install, :Install,
:InstallDirectory, :InstallDirectory,
:Lex,
:Library, :Library,
:Object, :Object,
:Preprocess, :Preprocess,
@@ -38,7 +37,6 @@ module Rscons
:SharedLibrary, :SharedLibrary,
:SharedObject, :SharedObject,
:Size, :Size,
:Yacc,
] ]
# Class to represent a fatal error during an Rscons operation. # Class to represent a fatal error during an Rscons operation.
@@ -67,13 +65,6 @@ module Rscons
target.is_a?(Symbol) target.is_a?(Symbol)
end end
# Generate a random phony target name.
#
# @return [Symbol] Phony target name.
def gen_phony_target
("t" + sprintf("%08x", rand(1_000_000..4_000_000_000))).to_sym
end
# Return the system shell and arguments for executing a shell command. # Return the system shell and arguments for executing a shell command.
# #
# @return [Array<String>] The shell and flag. # @return [Array<String>] The shell and flag.
@@ -144,12 +135,11 @@ require_relative "rscons/builders/mixins/object_deps"
require_relative "rscons/builders/mixins/program" require_relative "rscons/builders/mixins/program"
# default builders # default builders
require_relative "rscons/builders/barrier" require_relative "rscons/builders/cfile"
require_relative "rscons/builders/command" require_relative "rscons/builders/command"
require_relative "rscons/builders/copy" require_relative "rscons/builders/copy"
require_relative "rscons/builders/directory" require_relative "rscons/builders/directory"
require_relative "rscons/builders/disassemble" require_relative "rscons/builders/disassemble"
require_relative "rscons/builders/lex"
require_relative "rscons/builders/library" require_relative "rscons/builders/library"
require_relative "rscons/builders/object" require_relative "rscons/builders/object"
require_relative "rscons/builders/preprocess" require_relative "rscons/builders/preprocess"
@@ -158,14 +148,12 @@ require_relative "rscons/builders/shared_library"
require_relative "rscons/builders/shared_object" require_relative "rscons/builders/shared_object"
require_relative "rscons/builders/simple_builder" require_relative "rscons/builders/simple_builder"
require_relative "rscons/builders/size" require_relative "rscons/builders/size"
require_relative "rscons/builders/yacc"
# language support # language support
require_relative "rscons/builders/lang/asm" require_relative "rscons/builders/lang/asm"
require_relative "rscons/builders/lang/c" require_relative "rscons/builders/lang/c"
require_relative "rscons/builders/lang/cxx" require_relative "rscons/builders/lang/cxx"
require_relative "rscons/builders/lang/d" require_relative "rscons/builders/lang/d"
require_relative "rscons/builders/lang/llvm_asm"
# Unbuffer $stdout # Unbuffer $stdout
$stdout.sync = true $stdout.sync = true
+28 -80
View File
@@ -42,16 +42,6 @@ module Rscons
@variant_groups = [] @variant_groups = []
end end
# Return if Rscons is in the task execution phase.
#
# @api private
#
# @return [Boolean]
# If Rscons is in the task execution phase.
def task_execution_phase?
@task_execution_phase
end
# Run the application. # Run the application.
# #
# Execute user-specified tasks. # Execute user-specified tasks.
@@ -64,16 +54,13 @@ module Rscons
# List of task(s) to execute. # List of task(s) to execute.
# @param show_tasks [Boolean] # @param show_tasks [Boolean]
# Flag to show tasks and exit. # 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] # @param enabled_variants [String]
# User-specified variants list. # User-specified variants list.
# #
# @return [Integer] # @return [Integer]
# Process exit code (0 on success). # Process exit code (0 on success).
def run(rsconscript, tasks_and_params, show_tasks, all_tasks, enabled_variants) def run(rsconscript, tasks_and_params, show_tasks, enabled_variants)
Cache.instance["failed_commands"] = [] Cache.instance["failed_commands"] = []
@tasks_and_params = tasks_and_params
@enabled_variants = enabled_variants @enabled_variants = enabled_variants
if enabled_variants == "" && !tasks_and_params.include?("configure") if enabled_variants == "" && !tasks_and_params.include?("configure")
if cache_enabled_variants = Cache.instance["configuration_data"]["enabled_variants"] if cache_enabled_variants = Cache.instance["configuration_data"]["enabled_variants"]
@@ -81,16 +68,13 @@ module Rscons
end end
end end
@script = Script.new @script = Script.new
if should_load_script @script.load(rsconscript)
@script.load(rsconscript)
enable_variants
end
if show_tasks if show_tasks
show_script_tasks(all_tasks) show_script_tasks
return 0 return 0
end end
apply_task_params(false) apply_task_params(tasks_and_params)
@task_execution_phase = true enable_variants
if tasks_and_params.empty? if tasks_and_params.empty?
check_process_environments check_process_environments
if Task.tasks["default"] if Task.tasks["default"]
@@ -172,8 +156,8 @@ module Rscons
def distclean def distclean
cache = Cache.instance cache = Cache.instance
clean clean
cache.clear
FileUtils.rm_rf(@build_dir) FileUtils.rm_rf(@build_dir)
cache.clear
end end
# Check if the project needs to be configured. # Check if the project needs to be configured.
@@ -182,7 +166,6 @@ module Rscons
# #
# @return [void] # @return [void]
def check_configure def check_configure
apply_task_params(true)
enable_variants enable_variants
unless Cache.instance["configuration_data"]["configured"] unless Cache.instance["configuration_data"]["configured"]
if @script.autoconf if @script.autoconf
@@ -209,20 +192,20 @@ module Rscons
# #
# @api private # @api private
# #
# @param options [Hash]
# Options.
#
# @return [void] # @return [void]
def configure def configure
unless @_configured co = ConfigureOp.new(@script)
@_configured = true begin
co = ConfigureOp.new(@script) @script.configure(co)
begin rescue RsconsError => e
@script.configure(co) co.close(false)
rescue RsconsError => e raise e
co.close(false)
raise e
end
Cache.instance["configuration_data"]["enabled_variants"] = @enabled_variants
co.close(true)
end end
Cache.instance["configuration_data"]["enabled_variants"] = @enabled_variants
co.close(true)
end end
# Remove installed files. # Remove installed files.
@@ -237,7 +220,7 @@ module Rscons
cache = Cache.instance cache = Cache.instance
cache.targets(true).each do |target| cache.targets(true).each do |target|
cache.remove_target(target) cache.remove_target(target)
next unless File.exist?(target) next unless File.exists?(target)
puts "Removing #{target}" if verbose puts "Removing #{target}" if verbose
FileUtils.rm_f(target) FileUtils.rm_f(target)
end end
@@ -258,14 +241,6 @@ module Rscons
# #
# @param name [String] # @param name [String]
# Variant name. # Variant name.
# @param options [Hash]
# Optional parameters.
# @option options [String] :default
# Whether the variant is enabled by default (default: true).
# @option options [String] :key
# Variant key, used to name an Environment's build directory. If nil,
# this variant will not contribute to the Environment's build directory
# name.
def variant(name, options = {}) def variant(name, options = {})
if @active_variants if @active_variants
!!@active_variants.find {|variant| variant[:name] == name} !!@active_variants.find {|variant| variant[:name] == name}
@@ -302,15 +277,6 @@ module Rscons
end end
# Create a variant group. # Create a variant group.
#
# @overload variant_group(name, options = {})
# @param name [String]
# Variant group name (optional).
# @param options [Hash]
# Optional variant group parameters.
# @overload variant_group(options = {})
# @param options [Hash]
# Optional variant group parameters.
def variant_group(*args, &block) def variant_group(*args, &block)
if args.first.is_a?(String) if args.first.is_a?(String)
name = args.slice!(0) name = args.slice!(0)
@@ -374,10 +340,10 @@ module Rscons
end end
end end
def show_script_tasks(all_tasks) def show_script_tasks
puts "Tasks:" puts "Tasks:"
Task[].sort.each do |task_name, task| Task[].sort.each do |task_name, task|
if task.description || all_tasks if task.description
puts %[ #{sprintf("%-27s", task_name)} #{task.description}] puts %[ #{sprintf("%-27s", task_name)} #{task.description}]
task.params.each do |param_name, param| task.params.each do |param_name, param|
arg_text = "--#{param_name}" arg_text = "--#{param_name}"
@@ -388,38 +354,20 @@ module Rscons
end end
end end
end end
end
unless @variant_groups.empty? def apply_task_params(tasks_and_params)
@variant_groups.each do |variant_group| tasks_and_params.each do |task_name, task_params|
puts "\nVariant group#{variant_group[:name] ? " '#{variant_group[:name]}'" : ""}:" task_params.each do |param_name, param_value|
variant_group[:variants].each do |variant| if param = Task[task_name].params[param_name]
puts " #{variant[:name]}#{variant[:enabled] ? " (enabled)" : ""}" Task[task_name].set_param_value(param_name, param_value)
else
raise RsconsError.new("Unknown parameter #{param_name.inspect} for task #{task_name}")
end end
end end
end end
end end
def apply_task_params(only_configure)
@tasks_and_params.each do |task_name, task_params|
unless only_configure && task_name != "configure"
task_params.each do |param_name, param_value|
if param = Task[task_name].params[param_name]
Task[task_name].set_param_value(param_name, param_value)
else
raise RsconsError.new("Unknown parameter #{param_name.inspect} for task #{task_name}")
end
end
end
end
end
def should_load_script
return true if @tasks_and_params.empty?
return true if Cache.instance["configuration_data"]["configured"]
return false if (@tasks_and_params.keys - %w[distclean clean uninstall]).empty?
true
end
end end
end end
+9 -21
View File
@@ -29,27 +29,19 @@ module Rscons
# @return [String, Symbol] # @return [String, Symbol]
# Target file name. # Target file name.
attr_reader :target attr_accessor :target
# @return [String, Symbol] # @return [Array<String>]
# Absolute target file name. # Source file name(s).
attr_reader :abstarget attr_accessor :sources
# @return [Array<String, Symbol>]
# Source file names.
attr_reader :sources
# @return [Array<String, Symbol>]
# Absolute source file names.
attr_reader :abssources
# @return [Cache] # @return [Cache]
# Cache instance. # Cache instance.
attr_reader :cache attr_accessor :cache
# @return [Environment] # @return [Environment]
# The {Environment} performing the build operation. # The {Environment} performing the build operation.
attr_reader :env attr_accessor :env
# @return [Hash, VarSet] # @return [Hash, VarSet]
# Construction variables used to perform the build operation. # Construction variables used to perform the build operation.
@@ -57,7 +49,7 @@ module Rscons
# @return [Set<String>] # @return [Set<String>]
# Side effect file(s) produced when this builder runs. # Side effect file(s) produced when this builder runs.
attr_reader :side_effects attr_accessor :side_effects
# @return [Integer] # @return [Integer]
# Build step. # Build step.
@@ -79,11 +71,7 @@ module Rscons
# Extra construction variables. # Extra construction variables.
def initialize(options) def initialize(options)
@target = options[:target] @target = options[:target]
@abstarget = Util.absolute_path(@target) @sources = options[:sources]
@sources = Array(options[:sources])
@abssources = @sources.map do |source|
Util.absolute_path(source)
end
@cache = options[:cache] @cache = options[:cache]
@env = options[:env] @env = options[:env]
@vars = options[:vars] @vars = options[:vars]
@@ -115,7 +103,7 @@ module Rscons
# @return [void] # @return [void]
def produces(*side_effects) def produces(*side_effects)
side_effects.each do |side_effect| side_effects.each do |side_effect|
side_effect_expanded = Util.absolute_path(@env.expand(side_effect)) side_effect_expanded = @env.expand(side_effect)
@env.register_side_effect(side_effect_expanded) @env.register_side_effect(side_effect_expanded)
@side_effects << side_effect_expanded @side_effects << side_effect_expanded
end end
+5 -5
View File
@@ -27,8 +27,8 @@ module Rscons
# env.Directory("dest") # env.Directory("dest")
# env.Install("dest", "bin") # env.Install("dest", "bin")
# env.Install("dest", "share") # env.Install("dest", "share")
self[builder.abstarget] ||= [] self[builder.target] ||= []
self[builder.abstarget] << builder self[builder.target] << builder
end end
# Return the number of remaining build steps. # Return the number of remaining build steps.
@@ -37,7 +37,7 @@ module Rscons
# The number of remaining build steps. # The number of remaining build steps.
def build_steps_remaining def build_steps_remaining
self.reduce(0) do |result, (target, builders)| self.reduce(0) do |result, (target, builders)|
result + builders.count {|b| !b.is_a?(Rscons::Builders::Barrier)} result + builders.size
end end
end end
@@ -54,7 +54,7 @@ module Rscons
# The next builder to run. # The next builder to run.
def get_next_builder_to_run(targets_still_building) def get_next_builder_to_run(targets_still_building)
to_build = self.find do |target, builders| to_build = self.find do |target, builders|
deps = builders.first.abssources + (@build_dependencies[target] || []).to_a deps = builders.first.sources + (@build_dependencies[target] || []).to_a
# All dependencies must have been built for this target to be ready to # All dependencies must have been built for this target to be ready to
# build. # build.
deps.all? do |dep| deps.all? do |dep|
@@ -79,7 +79,7 @@ module Rscons
# not find a builder to run above, then there might be a circular # not find a builder to run above, then there might be a circular
# dependency introduced by the user. # dependency introduced by the user.
if (self.size > 0) and targets_still_building.empty? if (self.size > 0) and targets_still_building.empty?
raise "Could not find a runnable builder. Possible circular dependency for #{self.first[1].first.target}" raise "Could not find a runnable builder. Possible circular dependency for #{self.keys.first}"
end end
end end
-15
View File
@@ -1,15 +0,0 @@
module Rscons
module Builders
# The Barrier builder does not perform any action. It exists as a builder
# on which to place dependencies to ensure that each of its sources are
# built before any build targets which depend on the barrier build target.
class Barrier < Builder
# Run the builder.
def run(options)
true
end
end
end
end
+35
View File
@@ -0,0 +1,35 @@
module Rscons
module Builders
# Build a C or C++ source file given a lex (.l, .ll) or yacc (.y, .yy)
# input file.
#
# Examples::
# env.CFile("parser.tab.cc", "parser.yy")
# env.CFile("lex.yy.cc", "parser.ll")
class CFile < Builder
# Run the builder to produce a build target.
def run(options)
if @command
finalize_command
else
@vars["_TARGET"] = @target
@vars["_SOURCES"] = @sources
case
when @sources.first.end_with?(*@env.expand_varref("${LEXSUFFIX}"))
cmd = "LEX"
message = "Generating lexer"
when @sources.first.end_with?(*@env.expand_varref("${YACCSUFFIX}"))
cmd = "YACC"
message = "Generating parser"
else
raise "Unknown source file #{@sources.first.inspect} for CFile builder"
end
command = @env.build_command("${#{cmd}_CMD}", @vars)
standard_command("#{message} from <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command)
end
end
end
end
end
+4 -4
View File
@@ -14,14 +14,14 @@ module Rscons
# Run the builder to produce a build target. # Run the builder to produce a build target.
def run(options) def run(options)
target_is_dir = (@sources.length > 1) || target_is_dir = (@sources.length > 1) ||
Dir.exist?(@sources.first) || Dir.exists?(@sources.first) ||
Dir.exist?(@target) Dir.exists?(@target)
outdir = target_is_dir ? @target : File.dirname(@target) outdir = target_is_dir ? @target : File.dirname(@target)
# Collect the list of files to copy over. # Collect the list of files to copy over.
file_map = {} file_map = {}
if target_is_dir if target_is_dir
@sources.each do |src| @sources.each do |src|
if Dir.exist? src if Dir.exists? src
Dir.glob("#{src}/**/*", File::FNM_DOTMATCH).select do |f| Dir.glob("#{src}/**/*", File::FNM_DOTMATCH).select do |f|
File.file?(f) File.file?(f)
end.each do |subfile| end.each do |subfile|
@@ -50,7 +50,7 @@ module Rscons
end end
@cache.register_build(dest, :Copy, [src], @env, install: @install_builder) @cache.register_build(dest, :Copy, [src], @env, install: @install_builder)
end end
(target_is_dir ? Dir.exist?(@target) : File.exist?(@target)) ? true : false (target_is_dir ? Dir.exists?(@target) : File.exists?(@target)) ? true : false
end end
end end
+1 -1
View File
@@ -14,7 +14,7 @@ module Rscons
def run(options) def run(options)
if File.directory?(@target) if File.directory?(@target)
true true
elsif File.exist?(@target) elsif File.exists?(@target)
Ansi.write($stderr, :red, "Error: `#{@target}' already exists and is not a directory", :reset, "\n") Ansi.write($stderr, :red, "Error: `#{@target}' already exists and is not a directory", :reset, "\n")
false false
else else
-10
View File
@@ -1,10 +0,0 @@
Rscons::Builders::Object.register(
command: "${LLVMAS_CMD}",
direct_command: "${LLVMAS_CMD:direct}",
suffix: "${LLVMAS_SUFFIX}",
short_description: "Assembling")
Rscons::Builders::SharedObject.register(
command: "${LLVMAS_CMD}",
direct_command: "${LLVMAS_CMD:direct}",
suffix: "${LLVMAS_SUFFIX}",
short_description: "Assembling")
-24
View File
@@ -1,24 +0,0 @@
module Rscons
module Builders
# Build a source file given a lex input file.
#
# Examples::
# env.Lex("lex.c", "parser.l")
# env.Lex("lex.cc", "parser.ll")
class Lex < Builder
# Run the builder to produce a build target.
def run(options)
if @command
finalize_command
else
@vars["_TARGET"] = @target
@vars["_SOURCES"] = @sources
command = @env.build_command("${LEX_CMD}", @vars)
standard_command("Generating lexer source from <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command)
end
end
end
end
end
+1 -1
View File
@@ -8,7 +8,7 @@ module Rscons
# dependency file. # dependency file.
def finalize_command_with_depfile def finalize_command_with_depfile
deps = @sources deps = @sources
if File.exist?(@vars["_DEPFILE"]) if File.exists?(@vars["_DEPFILE"])
deps += Util.parse_dependency_file(@vars["_DEPFILE"]) deps += Util.parse_dependency_file(@vars["_DEPFILE"])
end end
@cache.register_build(@target, @command, deps.uniq, @env) @cache.register_build(@target, @command, deps.uniq, @env)
-24
View File
@@ -1,24 +0,0 @@
module Rscons
module Builders
# Build a source file given a yacc input file.
#
# Examples::
# env.Yacc("parser.c", "parser.y")
# env.Yacc("parser.cc", "parser.yy")
class Yacc < Builder
# Run the builder to produce a build target.
def run(options)
if @command
finalize_command
else
@vars["_TARGET"] = @target
@vars["_SOURCES"] = @sources
command = @env.build_command("${YACC_CMD}", @vars)
standard_command("Generating parser source from <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command)
end
end
end
end
end
+7 -14
View File
@@ -101,7 +101,6 @@ module Rscons
# #
# @return [void] # @return [void]
def write def write
return unless Dir.exist?(File.dirname(cache_file))
@cache["version"] = VERSION @cache["version"] = VERSION
File.open(cache_file, "w") do |fh| File.open(cache_file, "w") do |fh|
fh.puts(JSON.dump(@cache)) fh.puts(JSON.dump(@cache))
@@ -142,14 +141,12 @@ module Rscons
# - each cached dependency file's current checksum matches the checksum # - each cached dependency file's current checksum matches the checksum
# stored in the cache file # stored in the cache file
def up_to_date?(targets, command, deps, env, options = {}) def up_to_date?(targets, command, deps, env, options = {})
deps = deps.map {|dep| Util.absolute_path(dep)}
Array(targets).each do |target| Array(targets).each do |target|
abstarget = Util.absolute_path(target) cache_key = get_cache_key(target)
cache_key = get_cache_key(abstarget)
unless Rscons.phony_target?(abstarget) unless Rscons.phony_target?(target)
# target file must exist on disk # target file must exist on disk
unless File.exist?(abstarget) unless File.exists?(target)
if options[:debug] if options[:debug]
puts "Target #{target} needs rebuilding because it does not exist on disk" puts "Target #{target} needs rebuilding because it does not exist on disk"
end end
@@ -165,9 +162,9 @@ module Rscons
return false return false
end end
unless Rscons.phony_target?(abstarget) unless Rscons.phony_target?(target)
# target must have the same checksum as when it was built last # target must have the same checksum as when it was built last
unless @cache["targets"][cache_key]["checksum"] == lookup_checksum(abstarget) unless @cache["targets"][cache_key]["checksum"] == lookup_checksum(target)
if options[:debug] if options[:debug]
puts "Target #{target} needs rebuilding because it has been changed on disk since being built last" puts "Target #{target} needs rebuilding because it has been changed on disk since being built last"
end end
@@ -204,7 +201,7 @@ module Rscons
end end
# set of user dependencies must match # set of user dependencies must match
user_deps = env.get_user_deps(abstarget) || [] user_deps = env.get_user_deps(target) || []
cached_user_deps = @cache["targets"][cache_key]["user_deps"] || [] cached_user_deps = @cache["targets"][cache_key]["user_deps"] || []
cached_user_deps_fnames = cached_user_deps.map { |dc| dc["fname"] } cached_user_deps_fnames = cached_user_deps.map { |dc| dc["fname"] }
unless user_deps == cached_user_deps_fnames unless user_deps == cached_user_deps_fnames
@@ -249,16 +246,12 @@ module Rscons
# @return [void] # @return [void]
def register_build(targets, command, deps, env, options = {}) def register_build(targets, command, deps, env, options = {})
Array(targets).each do |target| Array(targets).each do |target|
target = Util.absolute_path(target)
target_checksum = target_checksum =
if options[:side_effect] or Rscons.phony_target?(target) if options[:side_effect] or Rscons.phony_target?(target)
"" ""
else else
calculate_checksum(target) calculate_checksum(target)
end end
deps = deps.map do |dep|
Util.absolute_path(dep)
end.uniq
@cache["targets"][get_cache_key(target)] = { @cache["targets"][get_cache_key(target)] = {
"command" => Digest::MD5.hexdigest(command.inspect), "command" => Digest::MD5.hexdigest(command.inspect),
"checksum" => target_checksum, "checksum" => target_checksum,
@@ -313,7 +306,7 @@ module Rscons
parts.each_index do |i| parts.each_index do |i|
next if parts[i] == "" next if parts[i] == ""
subpath = File.join(*parts[0, i + 1]) subpath = File.join(*parts[0, i + 1])
unless File.exist?(subpath) unless File.exists?(subpath)
FileUtils.mkdir_p(subpath) FileUtils.mkdir_p(subpath)
@cache["directories"][subpath] = !!options[:install] @cache["directories"][subpath] = !!options[:install]
end end
+13 -51
View File
@@ -54,15 +54,10 @@ module Rscons
def run_toplevel(argv) def run_toplevel(argv)
rsconscript = nil rsconscript = nil
show_tasks = false show_tasks = false
all_tasks = false
enabled_variants = "" enabled_variants = ""
OptionParser.new do |opts| OptionParser.new do |opts|
opts.on("-A", "--all") do
all_tasks = true
end
opts.on("-b", "--build DIR") do |build_dir| opts.on("-b", "--build DIR") do |build_dir|
Rscons.application.build_dir = build_dir Rscons.application.build_dir = build_dir
end end
@@ -118,13 +113,13 @@ module Rscons
# Find the build script. # Find the build script.
if rsconscript if rsconscript
unless File.exist?(rsconscript) unless File.exists?(rsconscript)
$stderr.puts "Cannot read #{rsconscript}" $stderr.puts "Cannot read #{rsconscript}"
return 1 return 1
end end
else else
rsconscript = DEFAULT_RSCONSCRIPTS.find do |f| rsconscript = DEFAULT_RSCONSCRIPTS.find do |f|
File.exist?(f) File.exists?(f)
end end
end end
@@ -137,7 +132,7 @@ module Rscons
end end
begin begin
Rscons.application.run(rsconscript, tasks_and_params, show_tasks, all_tasks, enabled_variants) Rscons.application.run(rsconscript, tasks_and_params, show_tasks, enabled_variants)
rescue RsconsError => e rescue RsconsError => e
Ansi.write($stderr, :red, e.message, :reset, "\n") Ansi.write($stderr, :red, e.message, :reset, "\n")
1 1
@@ -149,49 +144,16 @@ module Rscons
Usage: #{$0} [global options] [[task] [task options] ...] Usage: #{$0} [global options] [[task] [task options] ...]
Global options: Global options:
-A, --all -b BUILD, --build=BUILD Set build directory (default: build)
Show all tasks (even those without descriptions) in task list. Use in -e VS, --variants=VS Enable or disable variants
conjunction with the -T argument. -f FILE Use FILE as Rsconscript
-F, --show-failure Show failed command log from previous build and exit
-b BUILD, --build=BUILD -h, --help Show rscons help and exit
Set build directory (default: build). -j N, --nthreads=N Set number of threads (local default: #{Rscons.application.n_threads})
-r COLOR, --color=COLOR Set color mode (off, auto, force)
-e VS, --variants=VS -T, --tasks Show task list and parameters and exit
Enable or disable variants. VS is a comma-separated list of variant -v, --verbose Run verbosely
entries. If the entry begins with "-" the variant is disabled instead of --version Show rscons version and exit
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 EOF
end end
+29 -75
View File
@@ -1,6 +1,5 @@
require "fileutils" require "fileutils"
require "open3" require "open3"
require "set"
module Rscons module Rscons
# Class to manage a configure operation. # Class to manage a configure operation.
@@ -11,11 +10,9 @@ module Rscons
# @param script [Script] # @param script [Script]
# Build script. # Build script.
def initialize(script) def initialize(script)
@tested_compilers = {}
@work_dir = "#{Rscons.application.build_dir}/_configure" @work_dir = "#{Rscons.application.build_dir}/_configure"
FileUtils.mkdir_p(@work_dir) FileUtils.mkdir_p(@work_dir)
@log_file_name = "#{@work_dir}/config.log" @log_fh = File.open("#{@work_dir}/config.log", "wb")
@log_fh = File.open(@log_file_name, "wb")
cache = Cache.instance cache = Cache.instance
cache["failed_commands"] = [] cache["failed_commands"] = []
cache["configuration_data"] = {} cache["configuration_data"] = {}
@@ -26,11 +23,14 @@ module Rscons
$stdout.puts "Configuring project..." $stdout.puts "Configuring project..."
end end
end end
vars = {}
Task["configure"].params.each do |name, param| Task["configure"].params.each do |name, param|
unless Rscons.application.silent_configure unless Rscons.application.silent_configure
Ansi.write($stdout, "Setting #{name}... ", :green, param.value, :reset, "\n") Ansi.write($stdout, "Setting #{name}... ", :green, param.value, :reset, "\n")
end end
vars[name] = param.value
end end
store_merge(vars)
end end
# Close the log file handle. # Close the log file handle.
@@ -65,15 +65,9 @@ module Rscons
ccc = %w[gcc clang] ccc = %w[gcc clang]
end end
cc = ccc.find do |cc| cc = ccc.find do |cc|
test_c_compiler(cc, options) test_c_compiler(cc)
end end
if cc complete(cc ? 0 : 1, options.merge(success_message: cc))
@tested_compilers["c"] ||= Set.new
@tested_compilers["c"] << cc
end
complete(cc ? 0 : 1, options.merge(
success_message: cc,
fail_message: "not found (checked #{ccc.join(", ")})"))
end end
# Check for a working C++ compiler. # Check for a working C++ compiler.
@@ -93,15 +87,9 @@ module Rscons
ccc = %w[g++ clang++] ccc = %w[g++ clang++]
end end
cc = ccc.find do |cc| cc = ccc.find do |cc|
test_cxx_compiler(cc, options) test_cxx_compiler(cc)
end end
if cc complete(cc ? 0 : 1, options.merge(success_message: cc))
@tested_compilers["cxx"] ||= Set.new
@tested_compilers["cxx"] << cc
end
complete(cc ? 0 : 1, options.merge(
success_message: cc,
fail_message: "not found (checked #{ccc.join(", ")})"))
end end
# Check for a working D compiler. # Check for a working D compiler.
@@ -118,18 +106,12 @@ module Rscons
end end
if cdc.empty? if cdc.empty?
# Default D compiler search array. # Default D compiler search array.
cdc = %w[gdc ldc2 ldc] cdc = %w[gdc ldc2]
end end
dc = cdc.find do |dc| dc = cdc.find do |dc|
test_d_compiler(dc, options) test_d_compiler(dc)
end end
if dc complete(dc ? 0 : 1, options.merge(success_message: dc))
@tested_compilers["d"] ||= Set.new
@tested_compilers["d"] << dc
end
complete(dc ? 0 : 1, options.merge(
success_message: dc,
fail_message: "not found (checked #{cdc.join(", ")})"))
end end
# Check for a package or configure program output. # Check for a package or configure program output.
@@ -139,12 +121,7 @@ module Rscons
elsif program = options[:program] elsif program = options[:program]
Ansi.write($stdout, "Checking '", :cyan, program, :reset, "'... ") Ansi.write($stdout, "Checking '", :cyan, program, :reset, "'... ")
end end
unless program program ||= "pkg-config"
program = "pkg-config"
unless Util.find_executable(program)
raise RsconsError.new("Error: executable '#{program}' not found")
end
end
args = options[:args] || %w[--cflags --libs] args = options[:args] || %w[--cflags --libs]
command = [program, *args, package].compact command = [program, *args, package].compact
stdout, _, status = log_and_test_command(command) stdout, _, status = log_and_test_command(command)
@@ -266,36 +243,17 @@ module Rscons
def check_lib(lib, options = {}) def check_lib(lib, options = {})
check_libpath = [nil] + (options[:check_libpath] || []) check_libpath = [nil] + (options[:check_libpath] || [])
Ansi.write($stdout, "Checking for library '", :cyan, lib, :reset, "'... ") Ansi.write($stdout, "Checking for library '", :cyan, lib, :reset, "'... ")
if @tested_compilers["d"] File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
source_file = "#{@work_dir}/cfgtest.d" fh.puts <<-EOF
File.open(source_file, "wb") do |fh| int main(int argc, char * argv[]) {
fh.puts <<-EOF return 0;
int main() { }
return 0; EOF
}
EOF
end
else
source_file = "#{@work_dir}/cfgtest.c"
File.open(source_file, "wb") do |fh|
fh.puts <<-EOF
int main(int argc, char * argv[]) {
return 0;
}
EOF
end
end
ld = "${CC}"
%w[d cxx c].each do |language|
if @tested_compilers[language]
ld = @tested_compilers[language].first
break
end
end end
vars = { vars = {
"LD" => ld, "LD" => "${CC}",
"LIBS" => [lib], "LIBS" => [lib],
"_SOURCES" => source_file, "_SOURCES" => "#{@work_dir}/cfgtest.c",
"_TARGET" => "#{@work_dir}/cfgtest.exe", "_TARGET" => "#{@work_dir}/cfgtest.exe",
} }
status = 1 status = 1
@@ -420,8 +378,6 @@ module Rscons
# A define to set (in CPPDEFINES) if the requested item is found. # A define to set (in CPPDEFINES) if the requested item is found.
# @option options [String] :success_message # @option options [String] :success_message
# Message to print on success (default "found"). # Message to print on success (default "found").
# @option options [String] :fail_message
# Message to print on failure (default "not found").
def complete(status, options) def complete(status, options)
success_message = options[:success_message] || "found" success_message = options[:success_message] || "found"
fail_message = options[:fail_message] || "not found" fail_message = options[:fail_message] || "not found"
@@ -445,7 +401,7 @@ module Rscons
options[:on_fail].call options[:on_fail].call
end end
if should_fail if should_fail
raise RsconsError.new("Configuration failed; log file written to #{@log_file_name}") raise RsconsError.new("Configuration failed")
end end
end end
end end
@@ -459,7 +415,7 @@ module Rscons
# #
# @return [Boolean] # @return [Boolean]
# Whether the C compiler tested successfully. # Whether the C compiler tested successfully.
def test_c_compiler(cc, options) def test_c_compiler(cc)
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh| File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
fh.puts <<-EOF fh.puts <<-EOF
int fun(int val) { int fun(int val) {
@@ -471,7 +427,7 @@ module Rscons
merge = {"CC" => cc} merge = {"CC" => cc}
_, _, status = log_and_test_command(command) _, _, status = log_and_test_command(command)
if status == 0 if status == 0
store_merge(merge, options) store_merge(merge)
true true
end end
end end
@@ -483,7 +439,7 @@ module Rscons
# #
# @return [Boolean] # @return [Boolean]
# Whether the C++ compiler tested successfully. # Whether the C++ compiler tested successfully.
def test_cxx_compiler(cc, options) def test_cxx_compiler(cc)
File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh| File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh|
fh.puts <<-EOF fh.puts <<-EOF
template<typename T> template<typename T>
@@ -496,7 +452,7 @@ module Rscons
merge = {"CXX" => cc} merge = {"CXX" => cc}
_, _, status = log_and_test_command(command) _, _, status = log_and_test_command(command)
if status == 0 if status == 0
store_merge(merge, options) store_merge(merge)
true true
end end
end end
@@ -508,7 +464,7 @@ module Rscons
# #
# @return [Boolean] # @return [Boolean]
# Whether the D compiler tested successfully. # Whether the D compiler tested successfully.
def test_d_compiler(dc, options) def test_d_compiler(dc)
File.open("#{@work_dir}/cfgtest.d", "wb") do |fh| File.open("#{@work_dir}/cfgtest.d", "wb") do |fh|
fh.puts <<-EOF fh.puts <<-EOF
import core.math; import core.math;
@@ -529,17 +485,15 @@ module Rscons
env = BasicEnvironment.new env = BasicEnvironment.new
merge = { merge = {
"DC" => dc, "DC" => dc,
"DC:-o" => "-of", "DCCMD" => env["DCCMD"].map {|e| e.sub(/^-o$/, "-of")},
"LD:-o" => "-of", "LDCMD" => env["LDCMD"].map {|e| e.sub(/^-o$/, "-of")},
"LIBDIRPREFIX" => "-L-L",
"LIBLINKPREFIX" => "-L-l",
"DDEPGEN" => ["-deps=${_DEPFILE}"], "DDEPGEN" => ["-deps=${_DEPFILE}"],
} }
merge["OBJSUFFIX"] = [ldc_objsuffix] merge["OBJSUFFIX"] = [ldc_objsuffix]
end end
_, _, status = log_and_test_command(command) _, _, status = log_and_test_command(command)
if status == 0 if status == 0
store_merge(merge, options) store_merge(merge)
true true
end end
end end
+5 -10
View File
@@ -35,9 +35,8 @@ module Rscons
"CXXFLAGS" => [], "CXXFLAGS" => [],
"CXXSUFFIX" => %w[.cc .cpp .cxx .C], "CXXSUFFIX" => %w[.cc .cpp .cxx .C],
"DC" => "gdc", "DC" => "gdc",
"DC:-o" => "-o", "DCCMD" => %w[${DC} -c -o ${_TARGET} ${DDEPGEN} ${INCPREFIX}${D_IMPORT_PATH} ${DFLAGS} ${_SOURCES}],
"DCCMD" => %w[${DC} -c ${DC:-o} ${_TARGET} ${DDEPGEN} ${INCPREFIX}${D_IMPORT_PATH} ${DFLAGS} ${_SOURCES}], "DCCMD:direct" => %w[${DC} -o ${_TARGET} ${DDEPGEN} ${INCPREFIX}${D_IMPORT_PATH} ${DFLAGS} ${LDFLAGS} ${_SOURCES} ${LIBDIRPREFIX}${LIBPATH} ${LIBLINKPREFIX}${LIBS}],
"DCCMD:direct" => %w[${DC} ${DC:-o} ${_TARGET} ${DDEPGEN} ${INCPREFIX}${D_IMPORT_PATH} ${DFLAGS} ${LDFLAGS} ${_SOURCES} ${LIBDIRPREFIX}${LIBPATH} ${LIBLINKPREFIX}${LIBS}],
"DDEPGEN" => %w[-MMD -MF ${_DEPFILE}], "DDEPGEN" => %w[-MMD -MF ${_DEPFILE}],
"DEPFILESUFFIX" => ".mf", "DEPFILESUFFIX" => ".mf",
"DFLAGS" => [], "DFLAGS" => [],
@@ -47,10 +46,10 @@ module Rscons
"D_IMPORT_PATH" => [], "D_IMPORT_PATH" => [],
"INCPREFIX" => "-I", "INCPREFIX" => "-I",
"LD" => nil, "LD" => nil,
"LD:-o" => "-o", "LDCMD" => %w[${LD} -o ${_TARGET} ${LDFLAGS} ${_SOURCES} ${LIBDIRPREFIX}${LIBPATH} ${LIBLINKPREFIX}${LIBS}],
"LDCMD" => %w[${LD} ${LD:-o} ${_TARGET} ${LDFLAGS} ${_SOURCES} ${LIBDIRPREFIX}${LIBPATH} ${LIBLINKPREFIX}${LIBS}],
"LDFLAGS" => [], "LDFLAGS" => [],
"LEX" => "flex", "LEX" => "flex",
"LEXSUFFIX" => %w[.l .ll],
"LEX_CMD" => %w[${LEX} ${LEX_FLAGS} -o ${_TARGET} ${_SOURCES}], "LEX_CMD" => %w[${LEX} ${LEX_FLAGS} -o ${_TARGET} ${_SOURCES}],
"LEX_FLAGS" => [], "LEX_FLAGS" => [],
"LIBDIRPREFIX" => "-L", "LIBDIRPREFIX" => "-L",
@@ -58,11 +57,6 @@ module Rscons
"LIBPATH" => [], "LIBPATH" => [],
"LIBS" => [], "LIBS" => [],
"LIBSUFFIX" => ".a", "LIBSUFFIX" => ".a",
"LLVMAS" => "clang",
"LLVMAS_CMD" => %w[${LLVMAS} -c -o ${_TARGET} ${LLVMAS_FLAGS} ${_SOURCES}],
"LLVMAS_CMD:direct" => %w[${LLVMAS} -o ${_TARGET} ${LLVMAS_FLAGS} ${LDFLAGS} ${_SOURCES} ${LIBDIRPREFIX}${LIBPATH} ${LIBLINKPREFIX}${LIBS}],
"LLVMAS_FLAGS" => [],
"LLVMAS_SUFFIX" => ".ll",
"OBJDUMP" => "objdump", "OBJDUMP" => "objdump",
"OBJSUFFIX" => %w[.o], "OBJSUFFIX" => %w[.o],
"PROGSUFFIX" => on_windows ? ".exe" : "", "PROGSUFFIX" => on_windows ? ".exe" : "",
@@ -90,6 +84,7 @@ module Rscons
"SIZECMD" => %w[${SIZE} ${SIZEFLAGS} ${_SOURCES}], "SIZECMD" => %w[${SIZE} ${SIZEFLAGS} ${_SOURCES}],
"SIZEFLAGS" => [], "SIZEFLAGS" => [],
"YACC" => "bison", "YACC" => "bison",
"YACCSUFFIX" => %w[.y .yy],
"YACC_CMD" => %w[${YACC} ${YACC_FLAGS} -o ${_TARGET} ${_SOURCES}], "YACC_CMD" => %w[${YACC} ${YACC_FLAGS} -o ${_TARGET} ${_SOURCES}],
"YACC_FLAGS" => %w[-d], "YACC_FLAGS" => %w[-d],
} }
+40 -50
View File
@@ -72,21 +72,35 @@ module Rscons
attr_reader :name attr_reader :name
# Create an Environment object. # Create an Environment object.
#
# @overload initialize(name, options)
# @overload initialize(options)
#
# @param name [String]
# Environment name. This determines the folder name used to store all
# environment build files under the top-level build directory.
# @param options [Hash]
# @option options [Symbol] :echo
# :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.
def initialize(*args, &block) def initialize(*args, &block)
@id = self.class.get_id @id = self.class.get_id
if args.first.is_a?(String)
base_name = args.slice!(0)
else
base_name = "e.#{@id}"
end
variant_keys = (Rscons.application.active_variants || []).map do |variant| variant_keys = (Rscons.application.active_variants || []).map do |variant|
variant[:key] variant[:key]
end.compact end.compact
if args.first.is_a?(String) @name = [base_name, *variant_keys].join("-")
base_name = args.slice!(0)
@name = [base_name, *variant_keys].join("-")
@build_root = "#{Rscons.application.build_dir}/#{@name}"
else
if variant_keys.size > 0
raise RsconsError.new("Error: an Environment with active variants must be given a name")
end
@build_root = Rscons.application.build_dir
end
options = args.first || {} options = args.first || {}
unless Cache.instance["configuration_data"]["configured"] unless Cache.instance["configuration_data"]["configured"]
raise "Project must be configured before creating an Environment" raise "Project must be configured before creating an Environment"
@@ -118,8 +132,8 @@ module Rscons
else else
:short :short
end end
@build_root = "#{Rscons.application.build_dir}/#{@name}"
@n_threads = Rscons.application.n_threads @n_threads = Rscons.application.n_threads
@build_steps = 0
self.class.register(self) self.class.register(self)
if block if block
Environment.running_environment = self Environment.running_environment = self
@@ -280,15 +294,7 @@ module Rscons
if extra_path = builder_class.extra_path if extra_path = builder_class.extra_path
extra_path = "/#{extra_path}" extra_path = "/#{extra_path}"
end end
source_fname = source_fname.gsub("\\", "/") "#{@build_root}#{extra_path}/#{Util.make_relative_path("#{source_fname}#{suffix}")}".gsub("\\", "/")
if extra_path.nil? && source_fname.start_with?("#{@build_root}/")
# If the source file is in the environment's build directory and no
# builder "extra path" is present, then just place the output file
# next to the source file.
"#{source_fname}#{suffix}"
else
"#{@build_root}/o#{extra_path}/#{Util.make_relative_path("#{source_fname}#{suffix}")}"
end
end end
# Build all build targets specified in the Environment. # Build all build targets specified in the Environment.
@@ -371,8 +377,7 @@ module Rscons
@builder_sets << build_builder_set @builder_sets << build_builder_set
end end
@builder_sets.last << builder @builder_sets.last << builder
@build_steps += 1 unless builder.is_a?(Rscons::Builders::Barrier) @build_targets[target] = builder
@build_targets[builder.abstarget] = builder
builder builder
else else
super super
@@ -396,14 +401,8 @@ module Rscons
end end
expand(ud) expand(ud)
end end
target = Util.absolute_path(target)
@user_deps[target] ||= [] @user_deps[target] ||= []
user_deps.map! {|ud| Util.absolute_path(ud)} @user_deps[target] = (@user_deps[target] + user_deps).uniq
user_deps.each do |ud|
unless Rscons.phony_target?(ud) || @user_deps[target].include?(ud)
@user_deps[target] << ud
end
end
build_after(target, user_deps) build_after(target, user_deps)
end end
@@ -434,13 +433,13 @@ module Rscons
targets = Array(targets) targets = Array(targets)
prerequisites = Array(prerequisites) prerequisites = Array(prerequisites)
targets.each do |target| targets.each do |target|
target = Util.absolute_path(expand(target)) target = expand(target)
@registered_build_dependencies[target] ||= Set.new @registered_build_dependencies[target] ||= Set.new
prerequisites.each do |prerequisite| prerequisites.each do |prerequisite|
if prerequisite.is_a?(Builder) if prerequisite.is_a?(Builder)
prerequisite = prerequisite.target prerequisite = prerequisite.target
end end
prerequisite = Util.absolute_path(expand(prerequisite)) prerequisite = expand(prerequisite)
@registered_build_dependencies[target] << prerequisite @registered_build_dependencies[target] << prerequisite
end end
end end
@@ -456,9 +455,9 @@ module Rscons
# #
# @return [void] # @return [void]
def produces(target, *side_effects) def produces(target, *side_effects)
abstarget = Util.absolute_path(expand(target)) target = expand(target)
@builder_sets.reverse.each do |builder_set| @builder_sets.reverse.each do |builder_set|
if builders = builder_set[abstarget] if builders = builder_set[target]
builders.last.produces(*side_effects) builders.last.produces(*side_effects)
return return
end end
@@ -475,7 +474,7 @@ module Rscons
# @param side_effect [String] # @param side_effect [String]
# Side effect fiel name. # Side effect fiel name.
def register_side_effect(side_effect) def register_side_effect(side_effect)
@side_effects << Util.absolute_path(side_effect) @side_effects << side_effect
end end
# Return the list of user dependencies for a given target. # Return the list of user dependencies for a given target.
@@ -486,7 +485,6 @@ module Rscons
# List of user-specified dependencies for the target, or nil if none were # List of user-specified dependencies for the target, or nil if none were
# specified. # specified.
def get_user_deps(target) def get_user_deps(target)
target = Util.absolute_path(target)
@user_deps[target] @user_deps[target]
end end
@@ -510,11 +508,10 @@ module Rscons
# @return [String] # @return [String]
# Output file name. # Output file name.
def register_dependency_build(target, source, suffix, vars, builder_class) def register_dependency_build(target, source, suffix, vars, builder_class)
target = Util.absolute_path(target)
output_fname = get_build_fname(source, suffix, builder_class) output_fname = get_build_fname(source, suffix, builder_class)
self.__send__(builder_class.name, output_fname, source, vars) self.__send__(builder_class.name, output_fname, source, vars)
@registered_build_dependencies[target] ||= Set.new @registered_build_dependencies[target] ||= Set.new
@registered_build_dependencies[target] << Util.absolute_path(output_fname) @registered_build_dependencies[target] << output_fname
output_fname output_fname
end end
@@ -591,7 +588,6 @@ module Rscons
# @return [Builder, nil] # @return [Builder, nil]
# The {Builder} for target, or +nil+ if none found. # The {Builder} for target, or +nil+ if none found.
def builder_for(target) def builder_for(target)
target = Util.absolute_path(target)
@build_targets[target] @build_targets[target]
end end
@@ -632,9 +628,7 @@ module Rscons
# #
# @return [void] # @return [void]
def run_builder(builder) def run_builder(builder)
unless builder.is_a?(Rscons::Builders::Barrier) builder.build_step ||= get_next_build_step
builder.build_step ||= get_next_build_step
end
case result = builder.run({}) case result = builder.run({})
when Array when Array
result.each do |waititem| result.each do |waititem|
@@ -660,10 +654,8 @@ module Rscons
Cache.instance.register_build(side_effect, nil, [], self, side_effect: true) Cache.instance.register_build(side_effect, nil, [], self, side_effect: true)
@side_effects.delete(side_effect) @side_effects.delete(side_effect)
end end
unless builder.is_a?(Rscons::Builders::Barrier) @build_hooks[:post].each do |build_hook_block|
@build_hooks[:post].each do |build_hook_block| build_hook_block.call(builder)
build_hook_block.call(builder)
end
end end
process_remove_wait(builder) process_remove_wait(builder)
else else
@@ -735,15 +727,13 @@ module Rscons
# If no builder was found to run yet and there are threads available, try # If no builder was found to run yet and there are threads available, try
# to get a runnable builder from the builder set. # to get a runnable builder from the builder set.
targets_still_building = @threads.reduce([]) do |result, (thread, obj)| targets_still_building = @threads.reduce([]) do |result, (thread, obj)|
result << builder_for_thread(thread).abstarget result << builder_for_thread(thread).target
end end
if @builder_sets.size > 0 if @builder_sets.size > 0
if builder = @builder_sets[0].get_next_builder_to_run(targets_still_building) if builder = @builder_sets[0].get_next_builder_to_run(targets_still_building)
builder.vars = @varset.merge(builder.vars) builder.vars = @varset.merge(builder.vars)
unless builder.is_a?(Rscons::Builders::Barrier) @build_hooks[:pre].each do |build_hook_block|
@build_hooks[:pre].each do |build_hook_block| build_hook_block.call(builder)
build_hook_block.call(builder)
end
end end
return run_builder(builder) return run_builder(builder)
end end
+7 -114
View File
@@ -10,14 +10,6 @@ module Rscons
@script = script @script = script
end 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). # Return a list of paths matching the specified pattern(s).
# #
# A pattern can contain a "/**" component to recurse through directories. # A pattern can contain a "/**" component to recurse through directories.
@@ -102,49 +94,9 @@ module Rscons
end end
# Create an environment. # Create an environment.
#
# @overload env(name, options)
# @param name [String]
# Environment name. This determines the folder name used to store all
# environment build files under the top-level build directory.
# @param options [Hash]
# @option options [Symbol] :echo
# :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.
#
# @overload env(options)
# @param options [Hash]
# @option options [Symbol] :echo
# :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 it is immediately executed and passed the newly
# created Environment object as an argument.
# If the Environment is created in task context, the +process+ method is
# immediately called.
# Otherwise, the Environment is not processed until the task execution
# phase.
#
# @yield [env]
# @yieldparam env [Environment]
# The created environment.
def env(*args, &block) def env(*args, &block)
Rscons.application.check_configure Rscons.application.check_configure
e = Environment.new(*args, &block) Environment.new(*args, &block)
if Rscons.application.task_execution_phase?
e.process
end
e
end end
# Construct a task parameter. # Construct a task parameter.
@@ -289,80 +241,27 @@ module Rscons
end end
# Create or modify a task. # Create or modify a task.
#
# @overload task(name, options = {}, &block)
# @param name [String]
# Task name.
# @param options [Hash]
# Optional task attributes.
# @option options [Boolean] :autoconf
# Whether to automatically configure before running this task
# (default: true).
# @option options [String] :desc
# Description for the task.
# @option options [Array<String>] :depends
# Dependencies of the task.
# @option options [Array<Task::Param>] :params
# Task parameter definitions.
#
# The action block given will be invoked by Rscons when the task
# executes. It will be passed two arguments:
# 1) The Task object.
# 2) A Hash of task parameter names and values.
def task(*args, &block) def task(*args, &block)
Util.task(*args, &block) Util.task(*args, &block)
end end
# Define a variant, or within a with_variants block, query if it is # Define a variant, or within a with_variants block, query if it is
# active. # active.
# def variant(*args)
# @param name [String] Rscons.application.variant(*args)
# Variant name.
# @param options [Hash]
# Optional parameters.
# @option options [String] :default
# Whether the variant is enabled by default (default: true).
# @option options [String] :key
# Variant key, used to name an Environment's build directory. If nil,
# this variant will not contribute to the Environment's build directory
# name.
def variant(name, options = {})
Rscons.application.variant(name, options)
end end
# Check if a variant is enabled. # Check if a variant is enabled.
# def variant_enabled?(*args)
# This can be used, for example, in a configuration block to omit or Rscons.application.variant_enabled?(*args)
# 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)
Rscons.application.variant_enabled?(variant_name)
end end
# Create a variant group. # Create a variant group.
#
# @overload variant_group(name, options = {})
# @param name [String]
# Variant group name (optional).
# @param options [Hash]
# Optional variant group parameters.
# @overload variant_group(options = {})
# @param options [Hash]
# Optional variant group parameters.
def variant_group(*args, &block) def variant_group(*args, &block)
Rscons.application.variant_group(*args, &block) Rscons.application.variant_group(*args, &block)
end end
# Iterate through enabled variants. # Iterate through variants.
#
# The given block is called for each combination of enabled variants
# across the defined variant groups.
def with_variants(&block) def with_variants(&block)
Rscons.application.enable_variants Rscons.application.enable_variants
Rscons.application.with_variants(&block) Rscons.application.with_variants(&block)
@@ -402,17 +301,11 @@ module Rscons
# Top-level DSL available to the Rsconscript. # Top-level DSL available to the Rsconscript.
class TopLevelDsl < GlobalDsl class TopLevelDsl < GlobalDsl
# Set the project name. # Set the project name.
#
# @param project_name [String]
# Project name.
def project_name(project_name) def project_name(project_name)
@script.project_name = project_name @script.project_name = project_name
end end
# Set whether to automatically configure (default true). # Whether to automatically configure (default true).
#
# @param autoconf [Boolean]
# Whether to automatically configure.
def autoconf(autoconf) def autoconf(autoconf)
@script.autoconf = autoconf @script.autoconf = autoconf
end end
-15
View File
@@ -16,21 +16,6 @@ module Rscons
end end
end end
# Return the absolute path for a given target if not a phony target.
#
# @param path [String, Symbol, nil]
# Given target name.
#
# @return [String, Symbol, nil]
# Absolute path of given target.
def absolute_path(path)
if path.is_a?(String)
File.expand_path(path)
else
path
end
end
# Colorize a builder run message. # Colorize a builder run message.
# #
# @param message [String] # @param message [String]
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons module Rscons
# Project version. # Project version.
VERSION = "3.3.0" VERSION = "2.3.0"
end end
+13 -54
View File
@@ -1,10 +1,7 @@
#!/usr/bin/env ruby #!/usr/bin/env ruby
require "base64"
require "digest/md5"
require "fileutils" require "fileutils"
require "stringio" require "digest/md5"
require "zlib"
if File.read("lib/rscons/version.rb") =~ /VERSION = "(.+)"/ if File.read("lib/rscons/version.rb") =~ /VERSION = "(.+)"/
VERSION = $1 VERSION = $1
@@ -25,7 +22,7 @@ combine_files = lambda do |file|
require_name = $1 require_name = $1
if require_name =~ %r{^#{PROG_NAME}(?:/.*)?$} if require_name =~ %r{^#{PROG_NAME}(?:/.*)?$}
path = "#{LIB_DIR}/#{require_name}.rb" path = "#{LIB_DIR}/#{require_name}.rb"
if File.exist?(path) if File.exists?(path)
unless files_processed[path] unless files_processed[path]
files_processed[path] = true files_processed[path] = true
combine_files[path] combine_files[path]
@@ -63,23 +60,13 @@ combined_file.each do |line|
end end
end end
license = File.read("LICENSE.txt").gsub(/^(.*?)$/) do |line| license = File.read("LICENSE.txt").gsub(/^/, "# ")
if line.size > 0
"# #{line}"
else
"#"
end
end
require "zlib"
require "base64"
compressed_script = Zlib::Deflate.deflate(stripped.join) compressed_script = Zlib::Deflate.deflate(stripped.join)
hash = Digest::MD5.hexdigest(compressed_script)
encoded_compressed_script = Base64.encode64(compressed_script).gsub("\n", "") encoded_compressed_script = Base64.encode64(compressed_script).gsub("\n", "")
encoded_compressed_script_io = StringIO.new(encoded_compressed_script) hash = Digest::MD5.hexdigest(encoded_compressed_script)
commented_encoded_compressed_script = ""
until encoded_compressed_script_io.eof?
line = encoded_compressed_script_io.read(64)
commented_encoded_compressed_script += "##{line}\n"
end
FileUtils.rm_rf(DIST) FileUtils.rm_rf(DIST)
FileUtils.mkdir_p(DIST) FileUtils.mkdir_p(DIST)
@@ -89,53 +76,25 @@ File.open("#{DIST}/#{PROG_NAME}", "wb", 0755) do |fh|
#{license} #{license}
BASE64CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
def base64_decode(s)
out = ""
v = 0
bits = 0
s.each_char do |c|
if cv = BASE64CHARS.index(c)
v = (v << 6) | cv
bits += 6
elsif c == "="
break
end
if bits >= 8
out += (v >> (bits - 8)).chr
v &= 0xFFFFFFFF >> (32 - (bits - 8))
bits -= 8
end
end
out
end
script = File.join(File.dirname(__FILE__), ".rscons-#{VERSION}-#{hash}.rb") script = File.join(File.dirname(__FILE__), ".rscons-#{VERSION}-#{hash}.rb")
unless File.exist?(script) unless File.exists?(script)
if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/m if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/
require "zlib" require "zlib"
require "base64"
encoded_compressed = $1 encoded_compressed = $1
compressed = base64_decode(encoded_compressed) unescaped_compressed = Base64.decode64(encoded_compressed)
if ENV["rscons_dist_specs"] inflated = Zlib::Inflate.inflate(unescaped_compressed)
require "digest/md5"
if Digest::MD5.hexdigest(compressed) != "#{hash}"
raise "Hash mismatch when decompressing rscons executable"
end
end
inflated = Zlib::Inflate.inflate(compressed)
File.open(script, "wb") do |fh| File.open(script, "wb") do |fh|
fh.write(inflated) fh.write(inflated)
end end
else else
raise "Error expanding rscons executable" raise "Could not decompress."
end end
end end
load script load script
if __FILE__ == $0 if __FILE__ == $0
Rscons::Cli.new.run(ARGV) Rscons::Cli.new.run(ARGV)
end end
#==> #==>#{encoded_compressed_script}
#{commented_encoded_compressed_script}
EOF EOF
end end
+1 -1
View File
@@ -99,7 +99,7 @@ class Generator
end end
template = File.read("rb/assets/user_guide.html.erb") template = File.read("rb/assets/user_guide.html.erb")
erb = ERB.new(template, trim_mode: "<>") erb = ERB.new(template, nil, "<>")
if multi_page if multi_page
@pages.each_with_index do |page, page_index| @pages.each_with_index do |page, page_index|
File diff suppressed because it is too large. Load diff
+8 -8
View File
@@ -17,14 +17,14 @@ module Rscons
it "makes directories and records any created in the cache" do it "makes directories and records any created in the cache" do
_cache = {} _cache = {}
cache = build_from(_cache) cache = build_from(_cache)
expect(File).to receive(:exist?).with("one").and_return(true) expect(File).to receive(:exists?).with("one").and_return(true)
expect(File).to receive(:exist?).with("one/two").and_return(false) expect(File).to receive(:exists?).with("one/two").and_return(false)
expect(FileUtils).to receive(:mkdir_p).with("one/two") expect(FileUtils).to receive(:mkdir_p).with("one/two")
expect(File).to receive(:exist?).with("one/two/three").and_return(false) expect(File).to receive(:exists?).with("one/two/three").and_return(false)
expect(FileUtils).to receive(:mkdir_p).with("one/two/three") expect(FileUtils).to receive(:mkdir_p).with("one/two/three")
expect(File).to receive(:exist?).with("one").and_return(true) expect(File).to receive(:exists?).with("one").and_return(true)
expect(File).to receive(:exist?).with("one/two").and_return(true) expect(File).to receive(:exists?).with("one/two").and_return(true)
expect(File).to receive(:exist?).with("one/two/four").and_return(false) expect(File).to receive(:exists?).with("one/two/four").and_return(false)
expect(FileUtils).to receive(:mkdir_p).with("one/two/four") expect(FileUtils).to receive(:mkdir_p).with("one/two/four")
cache.mkdir_p("one/two/three") cache.mkdir_p("one/two/three")
cache.mkdir_p("one\\two\\four") cache.mkdir_p("one\\two\\four")
@@ -34,8 +34,8 @@ module Rscons
it "handles absolute paths" do it "handles absolute paths" do
_cache = {} _cache = {}
cache = build_from(_cache) cache = build_from(_cache)
expect(File).to receive(:exist?).with("/one").and_return(true) expect(File).to receive(:exists?).with("/one").and_return(true)
expect(File).to receive(:exist?).with("/one/two").and_return(false) expect(File).to receive(:exists?).with("/one/two").and_return(false)
expect(FileUtils).to receive(:mkdir_p).with("/one/two") expect(FileUtils).to receive(:mkdir_p).with("/one/two")
cache.mkdir_p("/one/two") cache.mkdir_p("/one/two")
expect(cache.directories(false)).to eq ["/one/two"] expect(cache.directories(false)).to eq ["/one/two"]
+21
View File
@@ -138,6 +138,27 @@ module Rscons
end end
end end
describe "#depends" do
it "records the given dependencies in @user_deps" do
env = Environment.new
env.depends("foo", "bar", "baz")
expect(env.instance_variable_get(:@user_deps)).to eq({"foo" => ["bar", "baz"]})
end
it "records user dependencies only once" do
env = Environment.new
env.instance_variable_set(:@user_deps, {"foo" => ["bar"]})
env.depends("foo", "bar", "baz")
expect(env.instance_variable_get(:@user_deps)).to eq({"foo" => ["bar", "baz"]})
end
it "expands arguments for construction variable references" do
env = Environment.new
env["foo"] = "foo.exe"
env["bar"] = "bar.c"
env.depends("${foo}", "${bar}", "a.h")
expect(env.instance_variable_get(:@user_deps)).to eq({"foo.exe" => ["bar.c", "a.h"]})
end
end
describe "#shell" do describe "#shell" do
it "executes the given shell command and returns the results" do it "executes the given shell command and returns the results" do
env = Environment.new env = Environment.new
+8 -8
View File
@@ -1,12 +1,8 @@
if ENV["rscons_dist_specs"] if ENV["dist_specs"]
require_relative "../test_run/rscons" require_relative "../test/rscons"
else else
require "simplecov" require "simplecov"
class MyFormatter
def format(*args)
end
end
SimpleCov.start do SimpleCov.start do
add_filter "/spec/" add_filter "/spec/"
add_filter "/.bundle/" add_filter "/.bundle/"
@@ -15,10 +11,14 @@ else
else else
command_name "RSpec" command_name "RSpec"
end end
add_filter "test_run/rscons.rb" if ENV["dist_specs"]
add_filter "/bin/"
add_filter "/lib/"
else
add_filter "test/rscons.rb"
end
project_name "Rscons" project_name "Rscons"
merge_timeout 3600 merge_timeout 3600
formatter(MyFormatter)
end end
require "rscons" require "rscons"