Compare commits

..
46 Commits
Author SHA1 Message Date
josh b970490de2 v1.19.0 2020-04-16 16:36:44 -04:00
josh 87f1b92e7a update CHANGELOG for v1.19.0 2020-04-16 16:36:16 -04:00
josh a65247db0c fix yard warning 2020-04-16 16:21:06 -04:00
josh b6f0e61052 ruby 2.7 compatibility - #117 2020-04-16 16:12:30 -04:00
josh 93c5561190 stick with simplecov 0.15.x for now 2020-04-16 16:06:18 -04:00
josh 355a8383d0 Revert "update simplecov"
This reverts commit 01ca86de8b.
2020-04-16 16:04:18 -04:00
josh 64a7e56ee1 update bundle 2020-04-16 16:02:44 -04:00
John Lindgren 24ed649982 Check that user dependencies exist before running builders.
IMHO it's more intuitive if env.depends("my-target", "invalid-dep")
causes the build to fail when "invalid-dep" does not exist, rather
than having no effect (i.e. the build still succeeds with no warning).

This will make it more obvious, earlier, if you made a mistake in
an env.depends() call.  Otherwise the mistake could sit latent for
a while until you happen to notice that a build used a out-of-date
copy of some file rather than rebuilding it.

I've only added the check for user dependencies, not for direct
sources.  Listing a source file that doesn't exist will probably
cause the builder to fail anyway, so I don't think the preliminary
check is needed in that case.
2020-03-03 17:32:52 -05:00
josh eff9a10d0c v1.18.0 2019-10-10 16:29:21 -04:00
josh 6ad3fb0785 bundle update 2019-10-10 15:45:00 -04:00
josh 32e8a55b4e CHANGELOG - add v1.18.0 notes 2019-10-04 15:03:50 -04:00
josh 3733cb6114 Backport 2.x VarSet performance improvements to 1.x branch. 2019-10-04 15:01:32 -04:00
josh 5beddd63ac bundle update 2019-07-02 19:46:30 -04:00
josh 4967f89a76 fix inconsistent type handling in VarSet#expand_varref - #73 2019-04-14 14:10:13 -04:00
josh 7cc68b2ce1 Remove Cache dirty-tracking
It really wasn't necessary since Cache#write is not called often.
2019-04-04 15:20:35 -04:00
josh be058dd18b avoid mkdir() race conditions - close #75 2018-12-11 22:13:33 -05:00
josh ad703a5c84 call Bundler.setup in build_tests_spec.rb 2018-12-09 21:20:16 -05:00
John Lindgren fb5d107581 Reduce recursion of VarSet#expand_varref by expanding vars earlier.
It's common for a build command to contain a reference like:

    ${INCPREFIX}${CPPPATH}

Where ${INCPREFIX} is "-I" and ${CPPPATH} is an array of include
paths.  Without this change, Rscons expands ${INCPREFIX} (and then
tries to expand "-I") repeatedly, once for each path.

This change simply makes Rscons intelligent enough to expand
${INCPREFIX} only once, then join the expanded value to each path.

In a mid-sized rake project, ruby-prof shows that the number of
calls to expand_varref is reduced from ~80,000 to ~30,000., and the
total CPU time spent in expand_varref is reduced by about 30%.

In a larger project, the improvement in real build time is small
but consistent enough to be measurable (~0.5 seconds off a total
of ~20 seconds).
2018-12-07 20:06:18 -05:00
josh d5c1a4b41a v1.17.0 2018-11-02 17:01:52 -04:00
josh 3f63bf5d13 update CHANGELOG.md for v1.17.0 2018-11-02 17:01:21 -04:00
josh ce47bd3599 remove makefile target name check when parsing dependencies - close #57 2018-11-02 14:23:47 -04:00
michael.metivier 68cab5e24d Environment.expand_varref has the same allowed parameter types and returns as Varset.expand_varref 2018-11-02 08:52:23 -04:00
michael.metivier f92dd62ebd Allow construction variable expansion on _true_ and _false_ literals 2018-11-02 00:03:54 -04:00
josh 5cf77f1f58 v1.16.0 2018-08-24 09:24:03 -04:00
josh b4cc45c381 update documentation 2018-08-24 09:17:23 -04:00
josh 750506201f add github URL to README.md 2018-08-23 15:37:18 -04:00
josh 284312a21c support command-line variables - close #51 2018-08-23 14:58:08 -04:00
josh d081a4695f add shebang line to bin/rscons 2018-08-23 11:30:18 -04:00
josh c7f66694ab add Rscons.glob - close #50 2018-08-23 11:26:20 -04:00
josh 57de94a3fb improve debuggability of cache.up_to_date? - close #46 2018-08-23 10:10:05 -04:00
josh 53ba7dad41 allow passing a VarSet into cache methods - close #47 2018-08-22 16:32:05 -04:00
josh 8cb02a7e34 generate dependencies for D builds - close #49 2018-08-22 15:23:48 -04:00
josh dbbce9e476 update a few dependencies 2018-05-24 15:32:39 -04:00
josh 0dc3a90660 v1.15.0 2017-09-08 15:13:04 -04:00
josh d48c7587d6 add v1.15.0 release notes 2017-09-08 15:06:55 -04:00
josh 70d4740a69 split CHANGELOG.md out of README.md 2017-09-08 15:06:23 -04:00
josh aaae845d60 allow json 1.x or 2.x 2017-09-08 15:04:40 -04:00
josh e900e74342 v1.14.0 2017-08-03 15:55:54 -04:00
josh 62ae3f162d add v1.14.0 release notes 2017-08-03 15:55:38 -04:00
josh 099d26f33c Add Rscons::VarSet#values_at - close #45 2017-08-03 15:54:24 -04:00
josh 89562c584a Environment#print_builder_run_message should support string commands - close #44 2017-08-03 14:10:53 -04:00
josh a9aeaf2356 v1.13.0 2017-07-14 16:24:27 -04:00
josh 5409b3cb34 add v1.13.0 release notes 2017-07-14 16:24:04 -04:00
josh 6d04500c13 Add ability to record side-effect file production - close #43 2017-07-14 16:17:01 -04:00
josh 45e30662d2 add YARD comment for Rscons::Ansi to get to 100% coverage 2017-07-12 15:51:00 -04:00
josh 8f7e044bfd fix YARD @option tag 2017-07-12 15:50:13 -04:00
47 changed files with 924 additions and 369 deletions

No files matched your search

+233
View File
@@ -0,0 +1,233 @@
## ChangeLog
### v1.19.0
#### New Features
- #117 - ruby 2.7 compatibility
#### Fixes
- #115 - Check that user dependencies exist before running builders.
### v1.18.0
#### Performance Improvements
- Backport 2.x VarSet performance improvements to 1.x branch.
- Remove Cache dirty-tracking
- Reduce recursion of VarSet#expand_varref by expanding vars earlier.
#### Fixes
- #73 - fix inconsistent type handling in VarSet#expand_varref
- #75 - avoid mkdir() race conditions
### v1.17.0
#### New Features
- allow construction variable expansion on `true` and `false` values.
- remove makefile target name check when parsing dependencies
### v1.16.0
#### New Features
- Add `Rscons.glob`
- Support command-line variables
- improve debuggability of cache.up_to_date?
- allow passing a VarSet into cache methods
#### Fixes
- generate dependencies for D builds
### v1.15.0
- allow json 1.x or 2.x
### v1.14.0
#### New Features
- #45 - Add Rscons::VarSet#values_at
#### Fixes
- #44 - Environment#print_builder_run_message should support string commands
### v1.13.0
#### New Features
- #43 - Add ability to record side-effect file production
### v1.12.0
#### New Features
- #40 - env.depends should imply env.build_after
- #41 - be more colorful
#### Fixes
- #39 - wait for in-progress subcommands to complete on build failure
- #42 - cloned Environments should inherit n_threads
### v1.11.1
#### Fixes
- fix the circular build dependency detection logic
### v1.11.0
#### New Features
- Change default Environment :clone option to :all to clone all attributes
- #38 - raise error when circular dependencies are found
- #34 - Allow overriding n_threads on a per-Environment level
#### Fixes
- #35 - env.build_after should expand paths
- #36 - SHCFLAGS and SHCXXFLAGS should inherit non-SH flags by default
- #37 - Fix non-blocking thread-wait if Rscons.n_threads is set to 0
### v1.10.0
#### New Features
- #23 - add parallelization - builds are now parallelized by default
- #31 - add LEXSUFFIX, YACCSUFFIX construction variables
- #30 - place object files for absolute source paths under build_root
- #28 - support redirecting standard output using the Command builder
- Always use a build root and default it to "build"
- Add builder features
- #8 - add SharedObject and SharedLibrary builders
#### Fixes
- expand target and source paths before calling Builder#create_build_target
- #29 - fix PROGSUFFIX handling
- #32 - Pre-build hooks do not respect modified key values
### v1.9.3
- Environment#parse_flags should put -std=XXX flags in CCFLAGS, not CFLAGS
### v1.9.2
- allow phony targets in conjunction with build roots
### v1.9.1
- change *SUFFIX defaults to arrays
- add various C++ file suffixes
- use ${INCPREFIX} instead of hard-coded "-I" in Preprocess builder
### v1.9.0
#### New Features
- #6 - add Install and Copy builders
- #22 - allow overriding Command builder short description with CMD_DESC variable
- #24 - add "rscons" executable
- #25 - add support for phony targets given as Symbols instead of Strings
- #26 - support registering multiple build targets with the same target name
- #27 - add Directory builder
#### Fixes
- #20 - fix variable references that expand to arrays in build target sources
- #21 - rework Preprocess builder to consider deep dependencies
- fix Rscons.set_suffix to append the given suffix if the filename has none
- remove ${CFLAGS} from default CPP_CMD
### v1.8.1
- fix Environment#dump when construction variables are symbols
### v1.8.0
- new Command builder to execute arbitrary user commands
- new SimpleBuilder class
- create new builders quickly by passing a block to Environment#add_builder
- improved YARD documentation
- add Environment#dump to debug Environment construction variables
### v1.7.0
- allow build hooks to register new build targets
- add post-build hooks (register with Environment#add_post_build_hook)
- clear all build targets after processing an Environment
- allow trailing slashes in arguments to Environment#build_dir
### v1.6.1
- add DEPFILESUFFIX construction variable to override dependency file suffix
- fix Environment#depends to expand its arguments for construction variables
### v1.6.0
- support lambdas as construction variable values
### v1.5.0
- add "json" as a runtime dependency
- update construction variables to match SCons more closely
- add CPPDEFPREFIX, INCPREFIX, CPPDEFINES, CCFLAGS, LIBDIRPREFIX, and LIBLINKPREFIX
- add Environment#shell
- add Environment#parse_flags, #parse_flags!, #merge_flags
- unbuffer $stdout by default
- add PROGSUFFIX construction variable (defaults to .exe on MinGW/Cygwin)
- add Rscons::BuildTarget and Builder#create_build_target
- update specs to RSpec 3.x and fix to run on MinGW/Cygwin/Linux
- add YARD documentation to get to 100% coverage
### v1.4.3
- fix builders properly using construction variable overrides
- expand nil construction variables to empty strings
### v1.4.2
- add Environment#expand_path
- expand construction variable references in builder targets and sources before invoking builder
### v1.4.1
- fix invoking a builder with no sources while a build root defined
### v1.4.0
- add CFile builder
- add Disassemble builder
- add Preprocess builder
- pass the Environment object to build hooks in the :env key of the build_op parameter
- expand target/source paths beginning with "^/" to be relative to the Environment's build root
- many performance improvements, including:
- use JSON instead of YAML for the cache to improve loading speed (Issue #7)
- store a hash of the build command instead of the full command contents in the cache
- implement copy-on-write semantics for construction variables when cloning Environments
- only load the cache once instead of on each Environment#process
- only write the cache when something has changed
- fix Cache#mkdir_p to handle relative paths (Issue #5)
- flush the cache to disk if a builder raises an exception (Issue #4)
### v1.3.0
- change Environment#execute() options parameter to accept the following options keys:
- :env to pass an environment Hash to Kernel#system
- :options to pass an options Hash to Kernel#system
### v1.2.0
- add :clone option to Environment#clone to control exactly which Environment attributes are cloned
- allow nil to be passed in to Environment#build_root=
### v1.1.0
- Change Cache#up_to_date?() and #register_build() to accept a single target
file or an array of target file names
+23 -26
View File
@@ -1,38 +1,36 @@
PATH PATH
remote: . remote: .
specs: specs:
rscons (1.12.0) rscons (1.19.0)
json (~> 1.0) json (>= 1.8, < 3.0)
GEM GEM
remote: https://rubygems.org/ remote: https://rubygems.org/
specs: specs:
diff-lcs (1.2.5) diff-lcs (1.3)
docile (1.1.5) docile (1.1.5)
json (1.8.3) json (2.3.0)
rake (10.4.2) rake (13.0.1)
rdoc (4.2.0) rdoc (6.2.1)
json (~> 1.4) rspec (3.9.0)
redcarpet (3.3.3) rspec-core (~> 3.9.0)
rspec (3.4.0) rspec-expectations (~> 3.9.0)
rspec-core (~> 3.4.0) rspec-mocks (~> 3.9.0)
rspec-expectations (~> 3.4.0) rspec-core (3.9.1)
rspec-mocks (~> 3.4.0) rspec-support (~> 3.9.1)
rspec-core (3.4.1) rspec-expectations (3.9.1)
rspec-support (~> 3.4.0)
rspec-expectations (3.4.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0) rspec-support (~> 3.9.0)
rspec-mocks (3.4.0) rspec-mocks (3.9.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.4.0) rspec-support (~> 3.9.0)
rspec-support (3.4.1) rspec-support (3.9.2)
simplecov (0.11.1) simplecov (0.15.1)
docile (~> 1.1.0) docile (~> 1.1.0)
json (~> 1.8) json (>= 1.8, < 3)
simplecov-html (~> 0.10.0) simplecov-html (~> 0.10.0)
simplecov-html (0.10.0) simplecov-html (0.10.2)
yard (0.8.7.6) yard (0.9.24)
PLATFORMS PLATFORMS
ruby ruby
@@ -41,11 +39,10 @@ PLATFORMS
DEPENDENCIES DEPENDENCIES
rake rake
rdoc rdoc
redcarpet
rscons! rscons!
rspec rspec
simplecov simplecov (~> 0.15.0)
yard yard
BUNDLED WITH BUNDLED WITH
1.10.6 2.1.4
+37 -188
View File
@@ -1,7 +1,7 @@
# Rscons # Rscons
Rscons is a software construction framework inspired by SCons and implemented Rscons (https://github.com/holtrop/rscons) is a software construction framework
in Ruby. inspired by SCons and implemented in Ruby.
[![Gem Version](https://badge.fury.io/rb/rscons.png)](http://badge.fury.io/rb/rscons) [![Gem Version](https://badge.fury.io/rb/rscons.png)](http://badge.fury.io/rb/rscons)
@@ -30,7 +30,7 @@ task block or split among multiple tasks.
```ruby ```ruby
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env["CFLAGS"] << "-Wall" env["CFLAGS"] << "-Wall"
env.Program("program", Dir["src/**/*.c"]) env.Program("program", Rscons.glob("src/**/*.c"))
end end
``` ```
@@ -39,7 +39,7 @@ end
```ruby ```ruby
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env["DFLAGS"] << "-Wall" env["DFLAGS"] << "-Wall"
env.Program("program", Dir["src/**/*.d"]) env.Program("program", Rscons.glob("src/**/*.d"))
end end
``` ```
@@ -51,7 +51,7 @@ main_env = Rscons::Environment.new do |env|
env.build_dir("src", "build/main") env.build_dir("src", "build/main")
env["CFLAGS"] = ["-DSOME_DEFINE", "-O3"] env["CFLAGS"] = ["-DSOME_DEFINE", "-O3"]
env["LIBS"] = ["SDL"] env["LIBS"] = ["SDL"]
env.Program("program", Dir["src/**/*.cc"]) env.Program("program", Rscons.glob("src/**/*.cc"))
end end
debug_env = main_env.clone do |env| debug_env = main_env.clone do |env|
@@ -59,7 +59,7 @@ debug_env = main_env.clone do |env|
env.build_dir("src", "build/debug") env.build_dir("src", "build/debug")
env["CFLAGS"] -= ["-O3"] env["CFLAGS"] -= ["-O3"]
env["CFLAGS"] += ["-g", "-O0"] env["CFLAGS"] += ["-g", "-O0"]
env.Program("program-debug", Dir["src/**/*.cc"]) env.Program("program-debug", Rscons.glob("src/**/*.cc"))
end end
``` ```
@@ -86,7 +86,7 @@ end
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env.add_builder(GenerateFoo.new) env.add_builder(GenerateFoo.new)
env.GenerateFoo("foo.h", []) env.GenerateFoo("foo.h", [])
env.Program("a.out", Dir["*.c"]) env.Program("a.out", Rscons.glob("*.c"))
end end
``` ```
@@ -111,6 +111,15 @@ Rscons::Environment.new do |env|
end end
``` ```
The `Cache#up_to_date?` method accepts an optional 5th parameter which is an
options Hash. The `:debug` option can be specified in this Hash with a value
of `true` to aid in debugging builders while developing them. For example:
```ruby
unless cache.up_to_date?(target, cmd, sources, env, debug: true)
end
```
### Example: Custom Builder That Generates Multiple Output Files ### Example: Custom Builder That Generates Multiple Output Files
```ruby ```ruby
@@ -191,7 +200,7 @@ Rscons::Environment.new do |env|
end end
end end
env.build_dir("src", "build") env.build_dir("src", "build")
env.Program("program", Dir["**/*.cc"]) env.Program("program", Rscons.glob("**/*.cc"))
end end
``` ```
@@ -199,7 +208,7 @@ end
```ruby ```ruby
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env.Library("mylib.a", Dir["src/**/*.c"]) env.Library("mylib.a", Rscons.glob("src/**/*.c"))
end end
``` ```
@@ -327,7 +336,7 @@ env.Install("dist/share", "share")
```ruby ```ruby
env.Library(target, sources) env.Library(target, sources)
# Example # Example
env.Library("lib.a", Dir["src/**/*.c"]) env.Library("lib.a", Rscons.glob("src/**/*.c"))
``` ```
The Library builder creates a static library archive from the given source The Library builder creates a static library archive from the given source
@@ -362,7 +371,7 @@ preprocessed output to the target file.
```ruby ```ruby
env.Program(target, sources) env.Program(target, sources)
# Example # Example
env.Program("myprog", Dir["src/**/*.cc"]) env.Program("myprog", Rscons.glob("src/**/*.cc"))
``` ```
The Program builder compiles and links the given sources to an executable file. The Program builder compiles and links the given sources to an executable file.
@@ -375,7 +384,7 @@ This can be controlled with the `PROGSUFFIX` construction variable.
```ruby ```ruby
env.SharedLibrary(target, sources) env.SharedLibrary(target, sources)
# Example # Example
env.SharedLibrary("mydll", Dir["src/**/*.cc"]) env.SharedLibrary("mydll", Rscons.glob("src/**/*.cc"))
``` ```
The SharedLibrary builder compiles and links the given sources to a dynamically The SharedLibrary builder compiles and links the given sources to a dynamically
@@ -411,14 +420,14 @@ construction variables can be overridden by the user.
| ARFLAGS | Array | Static library archiver flags | ["rcs"] | | ARFLAGS | Array | Static library archiver flags | ["rcs"] |
| AS | String | Assembler executable | "${CC}" | | AS | String | Assembler executable | "${CC}" |
| ASCMD | Array | Assembler command line | ["${AS}", "-c", "-o", "${_TARGET}", "${ASDEPGEN}", "${INCPREFIX}${ASPPPATH}", "${ASPPFLAGS}", "${ASFLAGS}", "${_SOURCES}"] | | ASCMD | Array | Assembler command line | ["${AS}", "-c", "-o", "${_TARGET}", "${ASDEPGEN}", "${INCPREFIX}${ASPPPATH}", "${ASPPFLAGS}", "${ASFLAGS}", "${_SOURCES}"] |
| ASDEPGEN | Array | Assembly dependency generation flags | ["-MMD", "-MF", "${_DEPFILE}"] | | ASDEPGEN | Array | Assembly dependency generation flags | ["-MMD", "-MF", "${_DEPFILE}", "-MT", "TARGET"] |
| ASFLAGS | Array | Assembler flags | [] | | ASFLAGS | Array | Assembler flags | [] |
| ASPPFLAGS | Array | Assembler preprocessor flags | ["${CPPFLAGS}"] | | ASPPFLAGS | Array | Assembler preprocessor flags | ["${CPPFLAGS}"] |
| ASPPPATH | Array | Assembler preprocessor path | ["${CPPPATH}"] | | ASPPPATH | Array | Assembler preprocessor path | ["${CPPPATH}"] |
| ASSUFFIX | Array | Assembly file suffixes | [".S"] | | ASSUFFIX | Array | Assembly file suffixes | [".S"] |
| CC | String | C compiler executable | "gcc" | | CC | String | C compiler executable | "gcc" |
| CCCMD | Array | C compiler command line | ["${CC}", "-c", "-o", "${_TARGET}", "${CCDEPGEN}", "${INCPREFIX}${CPPPATH}", "${CPPFLAGS}", "${CFLAGS}", "${CCFLAGS}", "${_SOURCES}"] | | CCCMD | Array | C compiler command line | ["${CC}", "-c", "-o", "${_TARGET}", "${CCDEPGEN}", "${INCPREFIX}${CPPPATH}", "${CPPFLAGS}", "${CFLAGS}", "${CCFLAGS}", "${_SOURCES}"] |
| CCDEPGEN | Array | C compiler dependency generation flags | ["-MMD", "-MF", "${_DEPFILE}"] | | CCDEPGEN | Array | C compiler dependency generation flags | ["-MMD", "-MF", "${_DEPFILE}", "-MT", "TARGET"] |
| CCFLAGS | Array | Common flags for both C and C++ compiler | [] | | CCFLAGS | Array | Common flags for both C and C++ compiler | [] |
| CFLAGS | Array | C compiler flags | [] | | CFLAGS | Array | C compiler flags | [] |
| CPP_CMD | Array | Preprocess command line | ["${_PREPROCESS_CC}", "-E", "${_PREPROCESS_DEPGEN}", "-o", "${_TARGET}", "${INCPREFIX}${CPPPATH}", "${CPPFLAGS}", "${_SOURCES}"] | | CPP_CMD | Array | Preprocess command line | ["${_PREPROCESS_CC}", "-E", "${_PREPROCESS_DEPGEN}", "-o", "${_TARGET}", "${INCPREFIX}${CPPPATH}", "${CPPFLAGS}", "${_SOURCES}"] |
@@ -430,12 +439,13 @@ construction variables can be overridden by the user.
| CSUFFIX | Array | C source file suffixes | [".c"] | | CSUFFIX | Array | C source file suffixes | [".c"] |
| CXX | String | C++ compiler executable | "g++" | | CXX | String | C++ compiler executable | "g++" |
| CXXCMD | Array | C++ compiler command line | ["${CXX}", "-c", "-o", "${_TARGET}", "${CXXDEPGEN}", "${INCPREFIX}${CPPPATH}", "${CPPFLAGS}", "${CXXFLAGS}", "${CCFLAGS}", "${_SOURCES}"] | | CXXCMD | Array | C++ compiler command line | ["${CXX}", "-c", "-o", "${_TARGET}", "${CXXDEPGEN}", "${INCPREFIX}${CPPPATH}", "${CPPFLAGS}", "${CXXFLAGS}", "${CCFLAGS}", "${_SOURCES}"] |
| CXXDEPGEN | Array | C++ compiler dependency generation flags | ["-MMD", "-MF", "${_DEPFILE}"] | | CXXDEPGEN | Array | C++ compiler dependency generation flags | ["-MMD", "-MF", "${_DEPFILE}", "-MT", "TARGET"] |
| CXXFLAGS | Array | C++ compiler flags | [] | | CXXFLAGS | Array | C++ compiler flags | [] |
| CXXSUFFIX | Array | C++ source file suffixes | [".cc", ".cpp", ".cxx", ".C"] | | CXXSUFFIX | Array | C++ source file suffixes | [".cc", ".cpp", ".cxx", ".C"] |
| D_IMPORT_PATH | Array | D compiler import path | [] | | D_IMPORT_PATH | Array | D compiler import path | [] |
| DC | String | D compiler executable | "gdc" | | DC | String | D compiler executable | "gdc" |
| DCCMD | Array | D compiler command line | ["${DC}", "-c", "-o", "${_TARGET}", "${INCPREFIX}${D_IMPORT_PATH}", "${DFLAGS}", "${_SOURCES}"] | | DCCMD | Array | D compiler command line | ["${DC}", "-c", "-o", "${_TARGET}", "${DDEPGEN}", "${INCPREFIX}${D_IMPORT_PATH}", "${DFLAGS}", "${_SOURCES}"] |
| DDEPGEN | Array | D compiler dependency generation flags | ["-MMD", "-MF", "${_DEPFILE}", "-MT", "TARGET"] |
| DEPFILESUFFIX | String | Dependency file suffix for Makefile-style dependency rules emitted by the compiler (used internally for temporary dependency files used to determine a source file's dependencies) | ".mf" | | DEPFILESUFFIX | String | Dependency file suffix for Makefile-style dependency rules emitted by the compiler (used internally for temporary dependency files used to determine a source file's dependencies) | ".mf" |
| DFLAGS | Array | D compiler flags | [] | | DFLAGS | Array | D compiler flags | [] |
| DISASM_CMD | Array | Disassemble command line | ["${OBJDUMP}", "${DISASM_FLAGS}", "${_SOURCES}"] | | DISASM_CMD | Array | Disassemble command line | ["${OBJDUMP}", "${DISASM_FLAGS}", "${_SOURCES}"] |
@@ -548,9 +558,19 @@ end
You can pass multiple dependency files to `Environment#depends`: You can pass multiple dependency files to `Environment#depends`:
```ruby ```ruby
env.depends("my_app", "config/link.ld", "README.txt", *Dir.glob("assets/**/*")) env.depends("my_app", "config/link.ld", "README.txt", *Rscons.glob("assets/**/*"))
``` ```
### Command-Line Variables
Variables can be specified on the rscons command line. For example:
```
rscons VAR=val
```
These variables are accessible in a global VarSet called `Rscons.vars`.
### Construction Variable Naming ### Construction Variable Naming
* uppercase strings - the default construction variables that Rscons uses * uppercase strings - the default construction variables that Rscons uses
@@ -562,177 +582,6 @@ env.depends("my_app", "config/link.ld", "README.txt", *Dir.glob("assets/**/*"))
Documentation for the complete Rscons API can be found at Documentation for the complete Rscons API can be found at
http://www.rubydoc.info/github/holtrop/rscons/master. http://www.rubydoc.info/github/holtrop/rscons/master.
## Release Notes
### v1.12.0
#### New Features
- #40 - env.depends should imply env.build_after
- #41 - be more colorful
#### Fixes
- #39 - wait for in-progress subcommands to complete on build failure
- #42 - cloned Environments should inherit n_threads
### v1.11.1
#### Fixes
- fix the circular build dependency detection logic
### v1.11.0
#### New Features
- Change default Environment :clone option to :all to clone all attributes
- #38 - raise error when circular dependencies are found
- #34 - Allow overriding n_threads on a per-Environment level
#### Fixes
- #35 - env.build_after should expand paths
- #36 - SHCFLAGS and SHCXXFLAGS should inherit non-SH flags by default
- #37 - Fix non-blocking thread-wait if Rscons.n_threads is set to 0
### v1.10.0
#### New Features
- #23 - add parallelization - builds are now parallelized by default
- #31 - add LEXSUFFIX, YACCSUFFIX construction variables
- #30 - place object files for absolute source paths under build_root
- #28 - support redirecting standard output using the Command builder
- Always use a build root and default it to "build"
- Add builder features
- #8 - add SharedObject and SharedLibrary builders
#### Fixes
- expand target and source paths before calling Builder#create_build_target
- #29 - fix PROGSUFFIX handling
- #32 - Pre-build hooks do not respect modified key values
### v1.9.3
- Environment#parse_flags should put -std=XXX flags in CCFLAGS, not CFLAGS
### v1.9.2
- allow phony targets in conjunction with build roots
### v1.9.1
- change *SUFFIX defaults to arrays
- add various C++ file suffixes
- use ${INCPREFIX} instead of hard-coded "-I" in Preprocess builder
### v1.9.0
#### New Features
- #6 - add Install and Copy builders
- #22 - allow overriding Command builder short description with CMD_DESC variable
- #24 - add "rscons" executable
- #25 - add support for phony targets given as Symbols instead of Strings
- #26 - support registering multiple build targets with the same target name
- #27 - add Directory builder
#### Fixes
- #20 - fix variable references that expand to arrays in build target sources
- #21 - rework Preprocess builder to consider deep dependencies
- fix Rscons.set_suffix to append the given suffix if the filename has none
- remove ${CFLAGS} from default CPP_CMD
### v1.8.1
- fix Environment#dump when construction variables are symbols
### v1.8.0
- new Command builder to execute arbitrary user commands
- new SimpleBuilder class
- create new builders quickly by passing a block to Environment#add_builder
- improved YARD documentation
- add Environment#dump to debug Environment construction variables
### v1.7.0
- allow build hooks to register new build targets
- add post-build hooks (register with Environment#add_post_build_hook)
- clear all build targets after processing an Environment
- allow trailing slashes in arguments to Environment#build_dir
### v1.6.1
- add DEPFILESUFFIX construction variable to override dependency file suffix
- fix Environment#depends to expand its arguments for construction variables
### v1.6.0
- support lambdas as construction variable values
### v1.5.0
- add "json" as a runtime dependency
- update construction variables to match SCons more closely
- add CPPDEFPREFIX, INCPREFIX, CPPDEFINES, CCFLAGS, LIBDIRPREFIX, and LIBLINKPREFIX
- add Environment#shell
- add Environment#parse_flags, #parse_flags!, #merge_flags
- unbuffer $stdout by default
- add PROGSUFFIX construction variable (defaults to .exe on MinGW/Cygwin)
- add Rscons::BuildTarget and Builder#create_build_target
- update specs to RSpec 3.x and fix to run on MinGW/Cygwin/Linux
- add YARD documentation to get to 100% coverage
### v1.4.3
- fix builders properly using construction variable overrides
- expand nil construction variables to empty strings
### v1.4.2
- add Environment#expand_path
- expand construction variable references in builder targets and sources before invoking builder
### v1.4.1
- fix invoking a builder with no sources while a build root defined
### v1.4.0
- add CFile builder
- add Disassemble builder
- add Preprocess builder
- pass the Environment object to build hooks in the :env key of the build_op parameter
- expand target/source paths beginning with "^/" to be relative to the Environment's build root
- many performance improvements, including:
- use JSON instead of YAML for the cache to improve loading speed (Issue #7)
- store a hash of the build command instead of the full command contents in the cache
- implement copy-on-write semantics for construction variables when cloning Environments
- only load the cache once instead of on each Environment#process
- only write the cache when something has changed
- fix Cache#mkdir_p to handle relative paths (Issue #5)
- flush the cache to disk if a builder raises an exception (Issue #4)
### v1.3.0
- change Environment#execute() options parameter to accept the following options keys:
- :env to pass an environment Hash to Kernel#system
- :options to pass an options Hash to Kernel#system
### v1.2.0
- add :clone option to Environment#clone to control exactly which Environment attributes are cloned
- allow nil to be passed in to Environment#build_root=
### v1.1.0
- Change Cache#up_to_date?() and #register_build() to accept a single target
file or an array of target file names
## Contributing ## Contributing
1. Fork it 1. Fork it
+2
View File
@@ -1,3 +1,5 @@
#!/usr/bin/env ruby
require "rscons/cli" require "rscons/cli"
Rscons::Cli.run(ARGV) Rscons::Cli.run(ARGV)
+2 -2
View File
@@ -1,5 +1,5 @@
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env.append('CPPPATH' => Dir['src/**/*/'].sort) env.append('CPPPATH' => Rscons.glob('src/**'))
env.build_dir(%r{^src/([^/]+)/}, 'build_\\1/') env.build_dir(%r{^src/([^/]+)/}, 'build_\\1/')
env.Program('build_dir.exe', Dir['src/**/*.c']) env.Program('build_dir.exe', Rscons.glob('src/**/*.c'))
end end
@@ -6,7 +6,7 @@ end
Rscons::Environment.new(echo: :command) do |env| Rscons::Environment.new(echo: :command) do |env|
env.add_builder(MyObject.new) env.add_builder(MyObject.new)
env.append('CPPPATH' => Dir['src/**/*/'].sort) env.append('CPPPATH' => Rscons.glob('src/**'))
env.add_build_hook do |build_op| env.add_build_hook do |build_op|
if build_op[:builder].name == "MyObject" && build_op[:sources].first =~ %r{one\.c} if build_op[:builder].name == "MyObject" && build_op[:sources].first =~ %r{one\.c}
build_op[:vars]["CFLAGS"] << "-O1" build_op[:vars]["CFLAGS"] << "-O1"
+2 -2
View File
@@ -1,6 +1,6 @@
env = Rscons::Environment.new do |env| env = Rscons::Environment.new do |env|
env.append('CPPPATH' => Dir['src/**/*/'].sort) env.append('CPPPATH' => Rscons.glob('src/**/*/'))
env.build_dir("src", "build") env.build_dir("src", "build")
env.build_root = "build_root" env.build_root = "build_root"
env.Program('build_dir.exe', Dir['src/**/*.c']) env.Program('build_dir.exe', Rscons.glob('src/**/*.c'))
end end
+2 -2
View File
@@ -1,5 +1,5 @@
Rscons::Environment.new(echo: :command) do |env| Rscons::Environment.new(echo: :command) do |env|
env.append('CPPPATH' => Dir['src/**/*/'].sort) env.append('CPPPATH' => Rscons.glob('src/**/*/'))
env.build_dir(%r{^src/([^/]+)/}, 'build_\\1/') env.build_dir(%r{^src/([^/]+)/}, 'build_\\1/')
env.add_build_hook do |build_op| env.add_build_hook do |build_op|
if build_op[:target] =~ %r{build_one/.*\.o} if build_op[:target] =~ %r{build_one/.*\.o}
@@ -8,5 +8,5 @@ Rscons::Environment.new(echo: :command) do |env|
build_op[:vars]["CFLAGS"] << "-O2" build_op[:vars]["CFLAGS"] << "-O2"
end end
end end
env.Program('build_hook.exe', Dir['src/**/*.c'].sort) env.Program('build_hook.exe', Rscons.glob('src/**/*.c'))
end end
+2 -2
View File
@@ -1,8 +1,8 @@
Rscons::Environment.new(echo: :command) do |env| Rscons::Environment.new(echo: :command) do |env|
env.append('CPPPATH' => Dir['src/**/*/'].sort) env.append('CPPPATH' => Rscons.glob('src/**').sort)
env.build_root = "build_root" env.build_root = "build_root"
FileUtils.mkdir_p(env.build_root) FileUtils.mkdir_p(env.build_root)
FileUtils.mv("src/one/one.c", "build_root") FileUtils.mv("src/one/one.c", "build_root")
env.Object("^/one.o", "^/one.c") env.Object("^/one.o", "^/one.c")
env.Program("build_dir.exe", Dir['src/**/*.c'] + ["^/one.o"]) env.Program("build_dir.exe", Rscons.glob('src/**/*.c') + ["^/one.o"])
end end
+2 -2
View File
@@ -1,6 +1,6 @@
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env["CSUFFIX"] = %w[.yargh .c] env["CSUFFIX"] = %w[.yargh .c]
env["CFLAGS"] += %w[-x c] env["CFLAGS"] += %w[-x c]
env["CPPPATH"] += Dir["src/**/"] env["CPPPATH"] += Rscons.glob("src/**")
env.Program("build_dir.exe", Dir["src/**/*.{c,yargh}"]) env.Program("build_dir.exe", Rscons.glob("src/**/*.{c,yargh}"))
end end
@@ -0,0 +1,5 @@
Rscons::Environment.new do |env|
env.echo = :command
env.Install("inst.exe", "install.rb")
end
@@ -0,0 +1,13 @@
class MyBuilder < Rscons::Builder
def run(options)
env, target = options.values_at(:env, :target)
env.print_builder_run_message("MyBuilder #{target}", "MyBuilder #{target} command")
target
end
end
Rscons::Environment.new do |env|
env.echo = :command
env.add_builder(MyBuilder.new)
env.MyBuilder("foo")
end
+2 -2
View File
@@ -1,6 +1,6 @@
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env.append('CPPPATH' => Dir['src/**/*/'].sort) env.append('CPPPATH' => Rscons.glob('src/**'))
env.build_dir("src2", "build") env.build_dir("src2", "build")
env.build_root = "build_root" env.build_root = "build_root"
env.Program('build_dir.exe', Dir['src/**/*.c']) env.Program('build_dir.exe', Rscons.glob('src/**/*.c'))
end end
+2 -2
View File
@@ -1,6 +1,6 @@
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env.append("CPPPATH" => Dir["src/**/*/"].sort) env.append("CPPPATH" => Rscons.glob("src/**"))
env.build_dir("src/one/", "build_one/") env.build_dir("src/one/", "build_one/")
env.build_dir("src/two", "build_two") env.build_dir("src/two", "build_two")
env.Program("build_dir.exe", Dir["src/**/*.c"]) env.Program("build_dir.exe", Rscons.glob("src/**/*.c"))
end end
+14
View File
@@ -0,0 +1,14 @@
Rscons::Environment.new do |env|
env["build_root"] = env.build_root
env["inc_h"] = "inc.h"
env.Copy("copy_inc.h", "${inc_h}")
env.depends("${build_root}/program.o", "${inc_h}")
env.Program("program.exe", ["program.c", "inc.c"])
env.Command("inc.c",
[],
"CMD" => %w[ruby gen.rb ${_TARGET}],
"CMD_DESC" => "Generating")
env.produces("inc.c", "inc.h")
end
+1 -1
View File
@@ -1,3 +1,3 @@
Rscons::Environment.new(echo: :command) do |env| Rscons::Environment.new(echo: :command) do |env|
env.Program("hello-d.exe", Dir["*.d"]) env.Program("hello-d.exe", Rscons.glob("*.d"))
end end
+2 -1
View File
@@ -1,6 +1,7 @@
import std.stdio; import std.stdio;
import mod;
void main() void main()
{ {
writeln("Hello from D!"); writeln("Hello from D, value is ", Klass.VAL, "!");
} }
+4
View File
@@ -0,0 +1,4 @@
class Klass
{
enum VAL = 42;
};
+1 -1
View File
@@ -1,4 +1,4 @@
Rscons::Environment.new(echo: :command) do |env| Rscons::Environment.new(echo: :command) do |env|
env["ARCMD"] = %w[ar rcf ${_TARGET} ${_SOURCES}] env["ARCMD"] = %w[ar rcf ${_TARGET} ${_SOURCES}]
env.Library("lib.a", Dir["*.c"].sort) env.Library("lib.a", Rscons.glob("*.c"))
end end
+3 -3
View File
@@ -1,11 +1,11 @@
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env["CPPPATH"] << "src/lib" env["CPPPATH"] << "src/lib"
libmine = env.SharedLibrary("mine", Dir["src/lib/*.c"]) libmine = env.SharedLibrary("mine", Rscons.glob("src/lib/*.c"))
env.Program("test-shared.exe", env.Program("test-shared.exe",
Dir["src/*.c"], Rscons.glob("src/*.c"),
"LIBPATH" => %w[.], "LIBPATH" => %w[.],
"LIBS" => %w[mine]) "LIBS" => %w[mine])
env.build_after("test-shared.exe", libmine.to_s) env.build_after("test-shared.exe", libmine.to_s)
env.Program("test-static.exe", env.Program("test-static.exe",
Dir["src/**/*.c"]) Rscons.glob("src/**/*.c"))
end end
@@ -1,11 +1,11 @@
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env["CPPPATH"] << "src/lib" env["CPPPATH"] << "src/lib"
libmine = env.SharedLibrary("mine", Dir["src/lib/*.cc"]) libmine = env.SharedLibrary("mine", Rscons.glob("src/lib/*.cc"))
env.Program("test-shared.exe", env.Program("test-shared.exe",
Dir["src/*.cc"], Rscons.glob("src/*.cc"),
"LIBPATH" => %w[.], "LIBPATH" => %w[.],
"LIBS" => %w[mine]) "LIBS" => %w[mine])
env.build_after("test-shared.exe", libmine.to_s) env.build_after("test-shared.exe", libmine.to_s)
env.Program("test-static.exe", env.Program("test-static.exe",
Dir["src/**/*.cc"]) Rscons.glob("src/**/*.cc"))
end end
@@ -1,8 +1,8 @@
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env["CPPPATH"] << "src/lib" env["CPPPATH"] << "src/lib"
libmine = env.SharedLibrary("mine", Dir["src/lib/*.d"]) libmine = env.SharedLibrary("mine", Rscons.glob("src/lib/*.d"))
env.Program("test-shared.exe", env.Program("test-shared.exe",
Dir["src/*.c"], Rscons.glob("src/*.c"),
"LIBPATH" => %w[.], "LIBPATH" => %w[.],
"LIBS" => %w[mine]) "LIBS" => %w[mine])
env.build_after("test-shared.exe", libmine.to_s) env.build_after("test-shared.exe", libmine.to_s)
@@ -1,12 +1,12 @@
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env["CPPPATH"] << "src/lib" env["CPPPATH"] << "src/lib"
env["SHLD"] = "gcc" env["SHLD"] = "gcc"
libmine = env.SharedLibrary("mine", Dir["src/lib/*.c"]) libmine = env.SharedLibrary("mine", Rscons.glob("src/lib/*.c"))
env.Program("test-shared.exe", env.Program("test-shared.exe",
Dir["src/*.c"], Rscons.glob("src/*.c"),
"LIBPATH" => %w[.], "LIBPATH" => %w[.],
"LIBS" => %w[mine]) "LIBS" => %w[mine])
env.build_after("test-shared.exe", libmine.to_s) env.build_after("test-shared.exe", libmine.to_s)
env.Program("test-static.exe", env.Program("test-static.exe",
Dir["src/**/*.c"]) Rscons.glob("src/**/*.c"))
end end
+34
View File
@@ -0,0 +1,34 @@
class DebugBuilder < Rscons::Builder
def run(options)
target, sources, cache, env, vars = options.values_at(:target, :sources, :cache, :env, :vars)
command = %W[gcc -c -o #{target} #{sources.first}]
if Rscons.vars["command_change"]
command += %w[-Wall]
end
if Rscons.vars["new_dep"]
sources += ["extra"]
end
if Rscons.vars["strict_deps1"]
sources += ["extra"]
strict_deps = true
end
if Rscons.vars["strict_deps2"]
sources = ["extra"] + sources
strict_deps = true
end
unless cache.up_to_date?(target, command, sources, env, debug: true, strict_deps: strict_deps)
desc = "#{self.class.name} #{target}"
return false unless env.execute(desc, command)
cache.register_build(target, command, sources, env)
end
target
end
end
Rscons::Environment.new do |env|
env.add_builder(DebugBuilder.new)
if Rscons.vars["new_user_dep"]
env.depends("foo.o", "new_dep")
end
env.DebugBuilder("foo.o", "simple.c")
end
+20
View File
@@ -0,0 +1,20 @@
class TestBuilder < Rscons::Builder
def run(options)
target, sources, cache, env = options.values_at(:target, :sources, :cache, :env)
command = Rscons::VarSet.new("A" => "a", "B" => "b")
unless cache.up_to_date?(target, command, sources, env)
File.open(target, "w") do |fh|
fh.puts("hi")
end
msg = "#{self.class.name} #{target}"
env.print_builder_run_message(msg, msg)
cache.register_build(target, command, sources, env)
end
target
end
end
Rscons::Environment.new do |env|
env.add_builder(TestBuilder.new)
env.TestBuilder("foo")
end
+1 -1
View File
@@ -1,4 +1,4 @@
Rscons::Environment.new do |env| Rscons::Environment.new do |env|
env["sources"] = Dir["*.c"].sort env["sources"] = Rscons.glob("*.c")
env.Program("simple.exe", "${sources}") env.Program("simple.exe", "${sources}")
end end
+1
View File
@@ -0,0 +1 @@
# This file is referenced by cache_debugging.rb
+36
View File
@@ -5,6 +5,7 @@ require_relative "rscons/cache"
require_relative "rscons/environment" require_relative "rscons/environment"
require_relative "rscons/job_set" require_relative "rscons/job_set"
require_relative "rscons/threaded_command" require_relative "rscons/threaded_command"
require_relative "rscons/util"
require_relative "rscons/varset" require_relative "rscons/varset"
require_relative "rscons/version" require_relative "rscons/version"
@@ -55,6 +56,11 @@ module Rscons
# Whether to output ANSI color escape sequences. # Whether to output ANSI color escape sequences.
attr_accessor :do_ansi_color attr_accessor :do_ansi_color
# @since 1.16.0
# @return [VarSet]
# Access any variables set on the rscons command-line.
attr_reader :vars
# Remove all generated files. # Remove all generated files.
# #
# @return [void] # @return [void]
@@ -165,6 +171,35 @@ module Rscons
@command_executer = val @command_executer = val
end end
# Return a list of paths matching the specified pattern(s).
#
# @since 1.16.0
#
# A pattern can contain a "/**" component to recurse through directories.
# If the pattern ends with "/**" then only the recursive list of
# directories will be returned.
#
# Examples:
# - "src/**": return all directories under "src", recursively (including
# "src" itself).
# - "src/**/*": return all files and directories recursively under the src
# directory.
# - "src/**/*.c": return all .c files recursively under the src directory.
# - "dir/*/": return all directories in dir, but no files.
#
# @return [Array<String>] Paths matching the specified pattern(s).
def glob(*patterns)
require "pathname"
patterns.reduce([]) do |result, pattern|
if pattern.end_with?("/**")
pattern += "/"
end
result += Dir.glob(pattern).map do |path|
Pathname.new(path.gsub("\\", "/")).cleanpath.to_s
end
end.sort
end
private private
# Determine the number of threads to use by default. # Determine the number of threads to use by default.
@@ -203,6 +238,7 @@ module Rscons
end end
@n_threads = determine_n_threads @n_threads = determine_n_threads
@vars = VarSet.new
end end
+1
View File
@@ -1,4 +1,5 @@
module Rscons module Rscons
# Module to contain logic to write ANSI color escape codes.
module Ansi module Ansi
class << self class << self
+1 -1
View File
@@ -231,7 +231,7 @@ module Rscons
# @param env [Environment] The Environment executing the builder. # @param env [Environment] The Environment executing the builder.
# @param cache [Cache] The Cache object. # @param cache [Cache] The Cache object.
# @param options [Hash] Options. # @param options [Hash] Options.
# @options options [String] :stdout # @option options [String] :stdout
# File name to redirect standard output to. # File name to redirect standard output to.
# #
# @return [String,ThreadedCommand] # @return [String,ThreadedCommand]
+1 -2
View File
@@ -20,8 +20,7 @@ module Rscons
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
desc = "Directory #{target}" env.print_builder_run_message("Directory #{target}", nil)
env.print_builder_run_message(desc, desc)
cache.mkdir_p(target) cache.mkdir_p(target)
target target
end end
+1 -2
View File
@@ -43,8 +43,7 @@ module Rscons
# Check the cache and copy if necessary # Check the cache and copy if necessary
unless cache.up_to_date?(dest, :Copy, [src], env) unless cache.up_to_date?(dest, :Copy, [src], env)
unless printed_message unless printed_message
desc = "#{name} #{target}" env.print_builder_run_message("#{name} #{target}", nil)
env.print_builder_run_message(desc, desc)
printed_message = true printed_message = true
end end
cache.mkdir_p(File.dirname(dest)) cache.mkdir_p(File.dirname(dest))
+3 -2
View File
@@ -54,8 +54,9 @@ module Rscons
'DC' => 'gdc', 'DC' => 'gdc',
'DFLAGS' => [], 'DFLAGS' => [],
'DSUFFIX' => ['.d'], 'DSUFFIX' => ['.d'],
'DDEPGEN' => ['-MMD', '-MF', '${_DEPFILE}'],
'D_IMPORT_PATH' => [], 'D_IMPORT_PATH' => [],
'DCCMD' => ['${DC}', '-c', '-o', '${_TARGET}', '${INCPREFIX}${D_IMPORT_PATH}', '${DFLAGS}', '${_SOURCES}'], 'DCCMD' => ['${DC}', '-c', '-o', '${_TARGET}', '${DDEPGEN}', '${INCPREFIX}${D_IMPORT_PATH}', '${DFLAGS}', '${_SOURCES}'],
} }
end end
@@ -114,7 +115,7 @@ module Rscons
if options[:command_status] if options[:command_status]
target, deps, cache, env, vars = options.values_at(:target, :sources, :cache, :env, :vars) target, deps, cache, env, vars = options.values_at(:target, :sources, :cache, :env, :vars)
if File.exists?(vars['_DEPFILE']) if File.exists?(vars['_DEPFILE'])
deps += Environment.parse_makefile_deps(vars['_DEPFILE'], target) deps += Environment.parse_makefile_deps(vars['_DEPFILE'])
FileUtils.rm_f(vars['_DEPFILE']) FileUtils.rm_f(vars['_DEPFILE'])
end end
cache.register_build(target, options[:tc].command, deps.uniq, env) cache.register_build(target, options[:tc].command, deps.uniq, env)
+1 -1
View File
@@ -55,7 +55,7 @@ module Rscons
if options[:command_status] if options[:command_status]
target, deps, cache, env, vars = options.values_at(:target, :sources, :cache, :env, :vars) target, deps, cache, env, vars = options.values_at(:target, :sources, :cache, :env, :vars)
if File.exists?(vars['_DEPFILE']) if File.exists?(vars['_DEPFILE'])
deps += Environment.parse_makefile_deps(vars['_DEPFILE'], nil) deps += Environment.parse_makefile_deps(vars['_DEPFILE'])
FileUtils.rm_f(vars['_DEPFILE']) FileUtils.rm_f(vars['_DEPFILE'])
end end
cache.register_build(target, options[:tc].command, deps.uniq, env) cache.register_build(target, options[:tc].command, deps.uniq, env)
+1 -1
View File
@@ -100,7 +100,7 @@ module Rscons
if options[:command_status] if options[:command_status]
target, deps, cache, env, vars = options.values_at(:target, :sources, :cache, :env, :vars) target, deps, cache, env, vars = options.values_at(:target, :sources, :cache, :env, :vars)
if File.exists?(vars['_DEPFILE']) if File.exists?(vars['_DEPFILE'])
deps += Environment.parse_makefile_deps(vars['_DEPFILE'], target) deps += Environment.parse_makefile_deps(vars['_DEPFILE'])
FileUtils.rm_f(vars['_DEPFILE']) FileUtils.rm_f(vars['_DEPFILE'])
end end
cache.register_build(target, options[:tc].command, deps.uniq, env) cache.register_build(target, options[:tc].command, deps.uniq, env)
+64 -26
View File
@@ -85,18 +85,15 @@ module Rscons
@lookup_checksums = {} @lookup_checksums = {}
end end
# Write the cache to disk to be loaded next time. # Write the cache to disk.
# #
# @return [void] # @return [void]
def write def write
if @dirty || (@cache["version"] != VERSION)
@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))
end end
end end
@dirty = false
end
# Check if target(s) are up to date. # Check if target(s) are up to date.
# #
@@ -111,6 +108,10 @@ module Rscons
# @param deps [Array<String>] List of the target's dependency files. # @param deps [Array<String>] List of the target's dependency files.
# @param env [Environment] The Rscons::Environment. # @param env [Environment] The Rscons::Environment.
# @param options [Hash] Optional options. # @param options [Hash] Optional options.
# @option options [Boolean] :debug
# If turned on, this causes the Cache to print messages explaining why
# a build target is out of date. This could aid a builder author in
# debugging the operation of their builder.
# @option options [Boolean] :strict_deps # @option options [Boolean] :strict_deps
# Only consider a target up to date if its list of dependencies is # Only consider a target up to date if its list of dependencies is
# exactly equal (including order) to the cached list of dependencies # exactly equal (including order) to the cached list of dependencies
@@ -133,39 +134,79 @@ module Rscons
unless Rscons.phony_target?(target) unless Rscons.phony_target?(target)
# target file must exist on disk # target file must exist on disk
return false unless File.exists?(target) unless File.exists?(target)
if options[:debug]
puts "Target #{target} needs rebuilding because it does not exist on disk"
end
return false
end
end end
# target must be registered in the cache # target must be registered in the cache
return false unless @cache["targets"].has_key?(cache_key) unless @cache["targets"].has_key?(cache_key)
if options[:debug]
puts "Target #{target} needs rebuilding because there is no cached build information for it"
end
return false
end
unless Rscons.phony_target?(target) 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
return false unless @cache["targets"][cache_key]["checksum"] == lookup_checksum(target) unless @cache["targets"][cache_key]["checksum"] == lookup_checksum(target)
if options[:debug]
puts "Target #{target} needs rebuilding because it has been changed on disk since being built last"
end
return false
end
end end
# command used to build target must be identical # command used to build target must be identical
return false unless @cache["targets"][cache_key]["command"] == Digest::MD5.hexdigest(command.inspect) unless @cache["targets"][cache_key]["command"] == Digest::MD5.hexdigest(command.inspect)
if options[:debug]
puts "Target #{target} needs rebuilding because the command used to build it has changed"
end
return false
end
cached_deps = @cache["targets"][cache_key]["deps"] || [] cached_deps = @cache["targets"][cache_key]["deps"] || []
cached_deps_fnames = cached_deps.map { |dc| dc["fname"] } cached_deps_fnames = cached_deps.map { |dc| dc["fname"] }
if options[:strict_deps] if options[:strict_deps]
# depedencies passed in must exactly equal those in the cache # depedencies passed in must exactly equal those in the cache
return false unless deps == cached_deps_fnames unless deps == cached_deps_fnames
if options[:debug]
puts "Target #{target} needs rebuilding because the :strict_deps option is given and the set of dependencies does not match the previous set of dependencies"
end
return false
end
else else
# all dependencies passed in must exist in cache (but cache may have more) # all dependencies passed in must exist in cache (but cache may have more)
return false unless (Set.new(deps) - Set.new(cached_deps_fnames)).empty? unless (Set.new(deps) - Set.new(cached_deps_fnames)).empty?
if options[:debug]
puts "Target #{target} needs rebuilding because there are new dependencies"
end
return false
end
end end
# set of user dependencies must match # set of user dependencies must match
user_deps = env.get_user_deps(target) || [] 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"] }
return false unless user_deps == cached_user_deps_fnames unless user_deps == cached_user_deps_fnames
if options[:debug]
puts "Target #{target} needs rebuilding because the set of user-specified dependency files has changed"
end
return false
end
# all cached dependencies must have their checksums match # all cached dependencies must have their checksums match
(cached_deps + cached_user_deps).each do |dep_cache| (cached_deps + cached_user_deps).each do |dep_cache|
return false unless dep_cache["checksum"] == lookup_checksum(dep_cache["fname"]) unless dep_cache["checksum"] == lookup_checksum(dep_cache["fname"])
if options[:debug]
puts "Target #{target} needs rebuilding because dependency file #{dep_cache["fname"]} has changed"
end
return false
end
end end
end end
@@ -205,7 +246,6 @@ module Rscons
} }
end, end,
} }
@dirty = true
end end
end end
@@ -228,9 +268,8 @@ module Rscons
next if parts[i] == "" next if parts[i] == ""
subpath = File.join(*parts[0, i + 1]) subpath = File.join(*parts[0, i + 1])
unless File.exists?(subpath) unless File.exists?(subpath)
FileUtils.mkdir(subpath) FileUtils.mkdir_p(subpath)
@cache["directories"][subpath] = true @cache["directories"][subpath] = true
@dirty = true
end end
end end
end end
@@ -243,6 +282,16 @@ module Rscons
@cache["directories"].keys @cache["directories"].keys
end end
# Return a file's checksum, or the previously calculated checksum for
# the same file.
#
# @param file [String] The file name.
#
# @return [String] The file's checksum.
def lookup_checksum(file)
@lookup_checksums[file] || calculate_checksum(file)
end
private private
# Return a String key based on the target name to use in the on-disk cache. # Return a String key based on the target name to use in the on-disk cache.
@@ -271,17 +320,6 @@ module Rscons
@cache["targets"] ||= {} @cache["targets"] ||= {}
@cache["directories"] ||= {} @cache["directories"] ||= {}
@lookup_checksums = {} @lookup_checksums = {}
@dirty = false
end
# Return a file's checksum, or the previously calculated checksum for
# the same file.
#
# @param file [String] The file name.
#
# @return [String] The file's checksum.
def lookup_checksum(file)
@lookup_checksums[file] || calculate_checksum(file)
end end
# Calculate and return a file's checksum. # Calculate and return a file's checksum.
+6
View File
@@ -60,6 +60,12 @@ module Rscons
end.parse!(argv) end.parse!(argv)
argv.each do |arg|
if arg =~ /^([^=]+)=(.*)$/
Rscons.vars[$1] = $2
end
end
if rsconsfile if rsconsfile
unless File.exists?(rsconsfile) unless File.exists?(rsconsfile)
$stderr.puts "Cannot read #{rsconsfile}" $stderr.puts "Cannot read #{rsconsfile}"
+80 -22
View File
@@ -1,7 +1,6 @@
require "fileutils" require "fileutils"
require "set" require "set"
require "shellwords" require "shellwords"
require "thwait"
module Rscons module Rscons
# The Environment class is the main programmatic interface to Rscons. It # The Environment class is the main programmatic interface to Rscons. It
@@ -45,8 +44,9 @@ module Rscons
def initialize(options = {}) def initialize(options = {})
@threaded_commands = Set.new @threaded_commands = Set.new
@registered_build_dependencies = {} @registered_build_dependencies = {}
@side_effects = {}
@varset = VarSet.new @varset = VarSet.new
@job_set = JobSet.new(@registered_build_dependencies) @job_set = JobSet.new(@registered_build_dependencies, @side_effects)
@user_deps = {} @user_deps = {}
@builders = {} @builders = {}
@build_dirs = [] @build_dirs = []
@@ -279,6 +279,24 @@ module Rscons
@varset.send(:[], *args) @varset.send(:[], *args)
end end
# Access the value of a construction variable.
#
# @since 1.18.0
#
# This method is similar to #[] but does not make a copy-on-access copy of
# the variable accessed. This means that the returned value is NOT safe to
# be modified by the caller. Thus the caller must guarantee that it does
# not modify the returned value.
#
# @param key [String, Symbol]
# The construction variable name.
#
# @return [Object]
# The construction variable's value.
def get_var(key)
@varset.get_var(key)
end
# Set a construction variable's value. # Set a construction variable's value.
# #
# @see VarSet#[]= # @see VarSet#[]=
@@ -322,6 +340,7 @@ module Rscons
cache.clear_checksum_cache! cache.clear_checksum_cache!
if job if job
validate_user_deps(job[:target], @user_deps[job[:target]], cache)
result = run_builder(job[:builder], result = run_builder(job[:builder],
job[:target], job[:target],
job[:sources], job[:sources],
@@ -384,12 +403,12 @@ module Rscons
# Expand a construction variable reference. # Expand a construction variable reference.
# #
# @param varref [Array, String] Variable reference to expand. # @param varref [nil, String, Array, Proc, Symbol, TrueClass, FalseClass] Variable reference to expand.
# @param extra_vars [Hash, VarSet] # @param extra_vars [Hash, VarSet]
# Extra variables to use in addition to (or replace) the Environment's # Extra variables to use in addition to (or replace) the Environment's
# construction variables when expanding the variable reference. # construction variables when expanding the variable reference.
# #
# @return [Array, String] Expansion of the variable reference. # @return [nil, String, Array, Symbol, TrueClass, FalseClass] Expansion of the variable reference.
def expand_varref(varref, extra_vars = nil) def expand_varref(varref, extra_vars = nil)
vars = if extra_vars.nil? vars = if extra_vars.nil?
@varset @varset
@@ -502,6 +521,26 @@ module Rscons
end end
end end
# Manually record the given side effect file(s) as being produced when the
# named target is produced.
#
# @since 1.13.0
#
# @param target [String]
# Target of a build operation.
# @param side_effects [Array<String>]
# File(s) produced when the target file is produced.
#
# @return [void]
def produces(target, *side_effects)
target = expand_path(expand_varref(target))
side_effects = Array(side_effects).map do |side_effect|
expand_path(expand_varref(side_effect))
end.flatten
@side_effects[target] ||= []
@side_effects[target] += side_effects
end
# Return the list of user dependencies for a given target. # Return the list of user dependencies for a given target.
# #
# @param target [String] Target file name. # @param target [String] Target file name.
@@ -761,7 +800,7 @@ module Rscons
append["LDFLAGS", ["-arch", val]] append["LDFLAGS", ["-arch", val]]
end end
skip = true skip = true
elsif word =~ /^#{self["CPPDEFPREFIX"]}(.*)$/ elsif word =~ /^#{get_var("CPPDEFPREFIX")}(.*)$/
handle["CPPDEFINES", $1] handle["CPPDEFINES", $1]
elsif word == "-include" elsif word == "-include"
if val = words[i + 1] if val = words[i + 1]
@@ -774,11 +813,11 @@ module Rscons
append["LDFLAGS", ["-isysroot", val]] append["LDFLAGS", ["-isysroot", val]]
end end
skip = true skip = true
elsif word =~ /^#{self["INCPREFIX"]}(.*)$/ elsif word =~ /^#{get_var("INCPREFIX")}(.*)$/
handle["CPPPATH", $1] handle["CPPPATH", $1]
elsif word =~ /^#{self["LIBLINKPREFIX"]}(.*)$/ elsif word =~ /^#{get_var("LIBLINKPREFIX")}(.*)$/
handle["LIBS", $1] handle["LIBS", $1]
elsif word =~ /^#{self["LIBDIRPREFIX"]}(.*)$/ elsif word =~ /^#{get_var("LIBDIRPREFIX")}(.*)$/
handle["LIBPATH", $1] handle["LIBPATH", $1]
elsif word == "-mno-cygwin" elsif word == "-mno-cygwin"
append["CCFLAGS", [word]] append["CCFLAGS", [word]]
@@ -827,7 +866,7 @@ module Rscons
# @return [void] # @return [void]
def merge_flags(flags) def merge_flags(flags)
flags.each_pair do |key, val| flags.each_pair do |key, val|
if self[key].is_a?(Array) and val.is_a?(Array) if self.get_var(key).is_a?(Array) and val.is_a?(Array)
self[key] += val self[key] += val
else else
self[key] = val self[key] = val
@@ -864,7 +903,13 @@ module Rscons
def print_builder_run_message(short_description, command) def print_builder_run_message(short_description, command)
case @echo case @echo
when :command when :command
message = command_to_s(command) if command if command.is_a?(Array)
message = command_to_s(command)
elsif command.is_a?(String)
message = command
elsif short_description.is_a?(String)
message = short_description
end
when :short when :short
message = short_description if short_description message = short_description if short_description
end end
@@ -966,10 +1011,7 @@ module Rscons
!thread.alive? !thread.alive?
end end
else else
if threads.empty? Util.wait_for_thread(*threads)
raise "No threads to wait for"
end
ThreadsWait.new(*threads).next_wait
end end
end end
@@ -1038,16 +1080,14 @@ module Rscons
end end
end end
# Parse dependencies for a given target from a Makefile. # Parse dependencies from a Makefile.
# #
# This method is used internally by Rscons builders. # This method is used internally by Rscons builders.
# #
# @param mf_fname [String] File name of the Makefile to read. # @param mf_fname [String] File name of the Makefile to read.
# @param target [String, nil]
# Name of the target to gather dependencies for, nil for any/all.
# #
# @return [Array<String>] Paths of dependency files. # @return [Array<String>] Paths of dependency files.
def self.parse_makefile_deps(mf_fname, target) def self.parse_makefile_deps(mf_fname)
deps = [] deps = []
buildup = '' buildup = ''
File.read(mf_fname).each_line do |line| File.read(mf_fname).each_line do |line|
@@ -1055,16 +1095,34 @@ module Rscons
buildup += ' ' + $1 buildup += ' ' + $1
else else
buildup += ' ' + line buildup += ' ' + line
if buildup =~ /^(.*): (.*)$/ if buildup =~ /^.*: (.*)$/
mf_target, mf_deps = $1.strip, $2 mf_deps = $1
if target.nil? or mf_target == target
deps += mf_deps.split(' ').map(&:strip) deps += mf_deps.split(' ').map(&:strip)
end end
end
buildup = '' buildup = ''
end end
end end
deps deps
end end
# Ensures that user dependencies exist with valid checksums in the
# cache. Raises an exception if any dependency is invalid.
#
# @param target [String]
# Target to be built
# @param user_deps [Array<String>, nil]
# User dependencies of the target
# @param cache [Cache]
# Rscons cache instance
#
# @return [void]
def validate_user_deps(target, user_deps, cache)
return if user_deps.nil?
user_deps.each do |dep|
if cache.lookup_checksum(dep) == ""
raise "User dependency #{dep} of target #{target} is invalid"
end
end
end
end end
end end
+13 -10
View File
@@ -9,9 +9,14 @@ module Rscons
# @param build_dependencies [Hash] # @param build_dependencies [Hash]
# Hash mapping targets to a set of build dependencies. A job will not be # Hash mapping targets to a set of build dependencies. A job will not be
# returned as ready to run if any of its dependencies are still building. # returned as ready to run if any of its dependencies are still building.
def initialize(build_dependencies) # @param side_effects [Hash]
# Hash mapping targets to a set of side-effect files. A job will not be
# returned as ready to run if any of its dependencies is a side-effect
# of another target that has not yet been built.
def initialize(build_dependencies, side_effects)
@jobs = {} @jobs = {}
@build_dependencies = build_dependencies @build_dependencies = build_dependencies
@side_effects = side_effects
end end
# Add a job to the JobSet. # Add a job to the JobSet.
@@ -47,18 +52,16 @@ module Rscons
# @return [nil, Hash] # @return [nil, Hash]
# The next job to run. # The next job to run.
def get_next_job_to_run(targets_still_building) def get_next_job_to_run(targets_still_building)
targets_not_built_yet = targets_still_building + @jobs.keys
side_effects = targets_not_built_yet.map do |target|
@side_effects[target] || []
end.flatten
targets_not_built_yet += side_effects
@jobs.keys.each do |target| @jobs.keys.each do |target|
skip = false skip = false
(@jobs[target][0][:sources] + (@build_dependencies[target] || []).to_a).each do |src| (@jobs[target][0][:sources] + (@build_dependencies[target] || []).to_a).each do |src|
if @jobs.include?(src) if targets_not_built_yet.include?(src)
# Skip this target because it depends on another target not yet
# built.
skip = true
break
end
if targets_still_building.include?(src)
# Skip this target because it depends on another target that is
# currently being built.
skip = true skip = true
break break
end end
+34
View File
@@ -0,0 +1,34 @@
module Rscons
# A collection of stand-alone utility methods.
module Util
class << self
# Wait for any of a number of threads to complete.
#
# @param threads [Array<Thread>]
# Threads to wait for.
#
# @return [Thread]
# The Thread that completed.
def wait_for_thread(*threads)
if threads.empty?
raise "No threads to wait for"
end
queue = Queue.new
threads.each do |thread|
# Create a wait thread for each thread we're waiting for.
Thread.new do
begin
thread.join
ensure
queue.push(thread)
end
end
end
# Wait for any thread to complete.
queue.pop
end
end
end
end
+86 -27
View File
@@ -11,11 +11,13 @@ module Rscons
append(vars) append(vars)
end end
# Access the value of variable. # Access the value of a variable.
# #
# @param key [String, Symbol] The variable name. # @param key [String, Symbol]
# The variable name.
# #
# @return [Object] The variable's value. # @return [Object]
# The variable's value.
def [](key) def [](key)
if @my_vars.include?(key) if @my_vars.include?(key)
@my_vars[key] @my_vars[key]
@@ -30,11 +32,38 @@ module Rscons
end end
end end
# Access the value of a variable.
#
# This method is similar to #[] but does not make a copy-on-access copy of
# the variable accessed. This means that the returned value is NOT safe to
# be modified by the caller. Thus the caller must guarantee that it does
# not modify the returned value.
#
# @param key [String, Symbol]
# The variable name.
#
# @return [Object]
# The variable's value.
def get_var(key)
if @my_vars.include?(key)
@my_vars[key]
else
@coa_vars.each do |coa_vars|
if coa_vars.include?(key)
return coa_vars[key]
end
end
nil
end
end
# Assign a value to a variable. # Assign a value to a variable.
# #
# @param key [String, Symbol] The variable name. # @param key [String, Symbol]
# The variable name.
# #
# @param val [Object] The value to set. # @param val [Object]
# The value to set.
def []=(key, val) def []=(key, val)
@my_vars[key] = val @my_vars[key] = val
end end
@@ -86,40 +115,50 @@ module Rscons
# Replace "$!{var}" variable references in varref with the expanded # Replace "$!{var}" variable references in varref with the expanded
# variables' values, recursively. # variables' values, recursively.
# #
# @param varref [nil, String, Array, Proc] # @param varref [nil, String, Array, Proc, Symbol, TrueClass, FalseClass]
# Value containing references to variables. # Value containing references to variables.
# @param lambda_args [Array] # @param lambda_args [Array]
# Arguments to pass to any lambda variable values to be expanded. # Arguments to pass to any lambda variable values to be expanded.
# #
# @return [nil, String, Array] # @return [nil, String, Array, Symbol, TrueClass, FalseClass]
# Expanded value with "$!{var}" variable references replaced. # Expanded value with "$!{var}" variable references replaced.
def expand_varref(varref, lambda_args) def expand_varref(varref, lambda_args)
if varref.is_a?(String) case varref
when String
if varref =~ /^(.*)\$\{([^}]+)\}(.*)$/ if varref =~ /^(.*)\$\{([^}]+)\}(.*)$/
prefix, varname, suffix = $1, $2, $3 prefix, varname, suffix = $1, $2, $3
varval = expand_varref(self[varname], lambda_args) prefix = expand_varref(prefix, lambda_args) unless prefix.empty?
if varval.is_a?(String) or varval.nil? varval = expand_varref(get_var(varname), lambda_args)
expand_varref("#{prefix}#{varval}#{suffix}", lambda_args) # suffix needs no expansion since the regex matches the last occurence
elsif varval.is_a?(Array) case varval
varval.map {|vv| expand_varref("#{prefix}#{vv}#{suffix}", lambda_args)}.flatten when Array
if prefix.is_a?(Array)
varval.map {|vv| prefix.map {|p| "#{p}#{vv}#{suffix}"}}.flatten
else else
raise "I do not know how to expand a variable reference to a #{varval.class.name} (from #{varname.inspect} => #{self[varname].inspect})" varval.map {|vv| "#{prefix}#{vv}#{suffix}"}
end
when String, Symbol, true, false, nil
if prefix.is_a?(Array)
prefix.map {|p| "#{p}#{varval}#{suffix}"}
else
"#{prefix}#{varval}#{suffix}"
end
else
raise "Unknown construction variable type: #{varval.class} (from #{varname.inspect} => #{get_var(varname).inspect})"
end end
else else
varref varref
end end
elsif varref.is_a?(Array) when Array
varref.map do |ent| varref.map do |ent|
expand_varref(ent, lambda_args) expand_varref(ent, lambda_args)
end.flatten end.flatten
elsif varref.is_a?(Proc) when Symbol, true, false, nil
expand_varref(varref[*lambda_args], lambda_args)
elsif varref.nil?
nil
elsif varref.is_a?(Symbol)
varref varref
when Proc
expand_varref(varref[*lambda_args], lambda_args)
else else
raise "Unknown varref type: #{varref.class} (#{varref.inspect})" raise "Unknown construction variable type: #{varref.class} (#{varref.inspect})"
end end
end end
@@ -143,6 +182,26 @@ module Rscons
end end
end end
# Return a String representing the VarSet.
#
# @return [String] Representation of the VarSet.
def inspect
to_h.inspect
end
# Return an array containing the values associated with the given keys.
#
# @param keys [Array<String, Symbol>]
# Keys to look up in the VarSet.
#
# @return [Array]
# An array containing the values associated with the given keys.
def values_at(*keys)
keys.map do |key|
self[key]
end
end
private private
# Move all VarSet variables into the copy-on-access list. # Move all VarSet variables into the copy-on-access list.
@@ -164,16 +223,16 @@ module Rscons
# #
# @return [Object] Deep copied value. # @return [Object] Deep copied value.
def deep_dup(obj) def deep_dup(obj)
obj_class = obj.class case obj
if obj_class == Hash when String
obj.dup
when Array
obj.map { |v| deep_dup(v) }
when Hash
obj.reduce({}) do |result, (k, v)| obj.reduce({}) do |result, (k, v)|
result[k] = deep_dup(v) result[k] = deep_dup(v)
result result
end end
elsif obj_class == Array
obj.map { |v| deep_dup(v) }
elsif obj_class == String
obj.dup
else else
obj obj
end end
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons module Rscons
# gem version # gem version
VERSION = "1.12.0" VERSION = "1.19.0"
end end
+2 -3
View File
@@ -18,12 +18,11 @@ Gem::Specification.new do |gem|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.require_paths = ["lib"] gem.require_paths = ["lib"]
gem.add_dependency "json", "~> 1.0" gem.add_dependency "json", ">= 1.8", "< 3.0"
gem.add_development_dependency "rspec" gem.add_development_dependency "rspec"
gem.add_development_dependency "rake" gem.add_development_dependency "rake"
gem.add_development_dependency "simplecov" gem.add_development_dependency "simplecov", "~> 0.15.0"
gem.add_development_dependency "yard" gem.add_development_dependency "yard"
gem.add_development_dependency "rdoc" gem.add_development_dependency "rdoc"
gem.add_development_dependency "redcarpet"
end end
+138 -17
View File
@@ -74,6 +74,8 @@ describe Rscons do
command_name = "#{command_prefix}#{@statics[:build_test_id]}" command_name = "#{command_prefix}#{@statics[:build_test_id]}"
File.open("_simplecov_setup.rb", "w") do |fh| File.open("_simplecov_setup.rb", "w") do |fh|
fh.puts <<EOF fh.puts <<EOF
require "bundler"
Bundler.setup
require "simplecov" require "simplecov"
class MyFormatter class MyFormatter
def format(*args) def format(*args)
@@ -91,10 +93,11 @@ end
$LOAD_PATH.unshift(#{@owd.inspect} + "/lib") $LOAD_PATH.unshift(#{@owd.inspect} + "/lib")
# force color off # force color off
ENV["TERM"] = nil ENV["TERM"] = nil
#{options[:ruby_setup_code]}
EOF EOF
end end
stdout, stderr, status = nil, nil, nil stdout, stderr, status = nil, nil, nil
Bundler.with_clean_env do Bundler.with_unbundled_env do
stdout, stderr, status = Open3.capture3(*command) stdout, stderr, status = Open3.capture3(*command)
end end
# Remove output lines generated as a result of the test environment # Remove output lines generated as a result of the test environment
@@ -249,8 +252,8 @@ EOF
result = run_test(rsconsfile: "carat.rb") result = run_test(rsconsfile: "carat.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(Set[*lines(result.stdout)]).to eq Set[ expect(Set[*lines(result.stdout)]).to eq Set[
%q{gcc -c -o build_root/one.o -MMD -MF build_root/one.mf -Isrc/one/ -Isrc/two/ build_root/one.c}, %q{gcc -c -o build_root/one.o -MMD -MF build_root/one.mf -Isrc -Isrc/one -Isrc/two build_root/one.c},
%q{gcc -c -o build_root/src/two/two.o -MMD -MF build_root/src/two/two.mf -Isrc/one/ -Isrc/two/ src/two/two.c}, %q{gcc -c -o build_root/src/two/two.o -MMD -MF build_root/src/two/two.mf -Isrc -Isrc/one -Isrc/two src/two/two.c},
%Q{gcc -o build_dir.exe build_root/src/two/two.o build_root/one.o}, %Q{gcc -o build_dir.exe build_root/src/two/two.o build_root/one.o},
] ]
end end
@@ -392,8 +395,8 @@ EOF
result = run_test(rsconsfile: "build_hooks.rb") result = run_test(rsconsfile: "build_hooks.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(Set[*lines(result.stdout)]).to eq Set[ expect(Set[*lines(result.stdout)]).to eq Set[
'gcc -c -o build_one/one.o -MMD -MF build_one/one.mf -Isrc/one/ -Isrc/two/ -O1 src/one/one.c', 'gcc -c -o build_one/one.o -MMD -MF build_one/one.mf -Isrc/one -Isrc/two -O1 src/one/one.c',
'gcc -c -o build_two/two.o -MMD -MF build_two/two.mf -Isrc/one/ -Isrc/two/ -O2 src/two/two.c', 'gcc -c -o build_two/two.o -MMD -MF build_two/two.mf -Isrc/one -Isrc/two -O2 src/two/two.c',
'gcc -o build_hook.exe build_one/one.o build_two/two.o', 'gcc -o build_hook.exe build_one/one.o build_two/two.o',
] ]
expect(`./build_hook.exe`).to eq "Hello from two()\n" expect(`./build_hook.exe`).to eq "Hello from two()\n"
@@ -414,14 +417,14 @@ EOF
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(lines(result.stdout)).to eq(["LD simple.exe"]) expect(lines(result.stdout)).to eq(["LD simple.exe"])
File.unlink("program.ld")
result = run_test(rsconsfile: "user_dependencies.rb")
expect(result.stderr).to eq ""
expect(lines(result.stdout)).to eq ["LD simple.exe"]
result = run_test(rsconsfile: "user_dependencies.rb") result = run_test(rsconsfile: "user_dependencies.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(result.stdout).to eq "" expect(result.stdout).to eq ""
File.unlink("program.ld")
result = run_test(rsconsfile: "user_dependencies.rb")
expect(result.stderr).to match /User dependency program\.ld of target simple\.exe is invalid/
expect(result.status).to_not eq 0
end end
unless ENV["omit_gdc_tests"] unless ENV["omit_gdc_tests"]
@@ -429,11 +432,31 @@ EOF
test_dir("d") test_dir("d")
result = run_test result = run_test
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(lines(result.stdout)).to eq [ slines = lines(result.stdout)
"gdc -c -o build/main.o main.d", expect(slines).to include("gdc -c -o build/main.o -MMD -MF build/main.mf main.d")
"gdc -o hello-d.exe build/main.o", expect(slines).to include("gdc -c -o build/mod.o -MMD -MF build/mod.mf mod.d")
] expect(slines.last).to eq("gdc -o hello-d.exe build/main.o build/mod.o")
expect(`./hello-d.exe`.rstrip).to eq "Hello from D!" expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
end
it "does dependency generation for D sources" do
test_dir("d")
result = run_test
expect(result.stderr).to eq ""
slines = lines(result.stdout)
expect(slines).to include("gdc -c -o build/main.o -MMD -MF build/main.mf main.d")
expect(slines).to include("gdc -c -o build/mod.o -MMD -MF build/mod.mf mod.d")
expect(slines.last).to eq("gdc -o hello-d.exe build/main.o build/mod.o")
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 42!"
fcontents = File.read("mod.d", mode: "rb").sub("42", "33")
File.open("mod.d", "wb") {|fh| fh.write(fcontents)}
result = run_test
expect(result.stderr).to eq ""
slines = lines(result.stdout)
expect(slines).to include("gdc -c -o build/main.o -MMD -MF build/main.mf main.d")
expect(slines).to include("gdc -c -o build/mod.o -MMD -MF build/mod.mf mod.d")
expect(slines.last).to eq("gdc -o hello-d.exe build/main.o build/mod.o")
expect(`./hello-d.exe`.rstrip).to eq "Hello from D, value is 33!"
end end
it "creates shared libraries using D" do it "creates shared libraries using D" do
@@ -769,6 +792,22 @@ EOF
expect(lines(result.stdout)).to eq ["165"] expect(lines(result.stdout)).to eq ["165"]
end end
it "prints a builder's short description with 'command' echo mode if there is no command" do
test_dir("build_dir")
result = run_test(rsconsfile: "echo_command_ruby_builder.rb")
expect(result.stderr).to eq ""
expect(lines(result.stdout)).to eq ["Install inst.exe"]
end
it "supports a string for a builder's echoed 'command' with Environment#print_builder_run_message" do
test_dir("build_dir")
result = run_test(rsconsfile: "echo_command_string.rb")
expect(result.stderr).to eq ""
expect(lines(result.stdout)).to eq ["MyBuilder foo command"]
end
context "colored output" do context "colored output" do
it "does not output in color with --color=off" do it "does not output in color with --color=off" do
test_dir("simple") test_dir("simple")
@@ -857,8 +896,8 @@ EOF
result = run_test(rsconsfile: "backward_compatible_build_hooks.rb") result = run_test(rsconsfile: "backward_compatible_build_hooks.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
expect(Set[*lines(result.stdout)]).to eq Set[ expect(Set[*lines(result.stdout)]).to eq Set[
'gcc -c -o one.o -MMD -MF one.mf -Isrc/one/ -Isrc/two/ -O1 src/two/two.c', 'gcc -c -o one.o -MMD -MF one.mf -Isrc -Isrc/one -Isrc/two -O1 src/two/two.c',
'gcc -c -o two.o -MMD -MF two.mf -Isrc/one/ -Isrc/two/ -O2 src/two/two.c' 'gcc -c -o two.o -MMD -MF two.mf -Isrc -Isrc/one -Isrc/two -O2 src/two/two.c'
] ]
expect(File.exists?('one.o')).to be_truthy expect(File.exists?('one.o')).to be_truthy
expect(File.exists?('two.o')).to be_truthy expect(File.exists?('two.o')).to be_truthy
@@ -1225,6 +1264,81 @@ EOF
"LD simple.exe", "LD simple.exe",
] ]
end end
it "allows a VarSet to be passed in as the command parameter" do
test_dir("simple")
result = run_test(rsconsfile: "cache_varset.rb")
expect(result.stderr).to eq ""
expect(lines(result.stdout)).to eq [
"TestBuilder foo",
]
result = run_test(rsconsfile: "cache_varset.rb")
expect(result.stderr).to eq ""
expect(result.stdout).to eq ""
end
context "debugging" do
it "prints a message when the target does not exist" do
test_dir("simple")
result = run_test(rsconsfile: "cache_debugging.rb")
expect(result.stdout).to match /Target foo\.o needs rebuilding because it does not exist on disk/
end
it "prints a message when there is no cached build information for the target" do
test_dir("simple")
FileUtils.touch("foo.o")
result = run_test(rsconsfile: "cache_debugging.rb")
expect(result.stdout).to match /Target foo\.o needs rebuilding because there is no cached build information for it/
end
it "prints a message when the target file has changed on disk" do
test_dir("simple")
result = run_test(rsconsfile: "cache_debugging.rb")
File.open("foo.o", "wb") {|fh| fh.puts "hi"}
result = run_test(rsconsfile: "cache_debugging.rb")
expect(result.stdout).to match /Target foo\.o needs rebuilding because it has been changed on disk since being built last/
end
it "prints a message when the command has changed" do
test_dir("simple")
result = run_test(rsconsfile: "cache_debugging.rb")
result = run_test(rsconsfile: "cache_debugging.rb", rscons_args: %w[command_change=yes])
expect(result.stdout).to match /Target foo\.o needs rebuilding because the command used to build it has changed/
end
it "prints a message when strict_deps is in use and the set of dependencies does not match" do
test_dir("simple")
result = run_test(rsconsfile: "cache_debugging.rb", rscons_args: %w[strict_deps1=yes])
result = run_test(rsconsfile: "cache_debugging.rb", rscons_args: %w[strict_deps2=yes])
expect(result.stdout).to match /Target foo\.o needs rebuilding because the :strict_deps option is given and the set of dependencies does not match the previous set of dependencies/
end
it "prints a message when there is a new dependency" do
test_dir("simple")
result = run_test(rsconsfile: "cache_debugging.rb")
result = run_test(rsconsfile: "cache_debugging.rb", rscons_args: %w[new_dep=yes])
expect(result.stdout).to match /Target foo\.o needs rebuilding because there are new dependencies/
end
it "prints a message when there is a new user-specified dependency" do
test_dir("simple")
result = run_test(rsconsfile: "cache_debugging.rb")
result = run_test(rsconsfile: "cache_debugging.rb", rscons_args: %w[new_user_dep=yes])
expect(result.stdout).to match /Target foo\.o needs rebuilding because the set of user-specified dependency files has changed/
end
it "prints a message when a dependency file has changed" do
test_dir("simple")
result = run_test(rsconsfile: "cache_debugging.rb")
f = File.read("simple.c", mode: "rb")
f += "\n"
File.open("simple.c", "wb") do |fh|
fh.write(f)
end
result = run_test(rsconsfile: "cache_debugging.rb")
expect(result.stdout).to match /Target foo\.o needs rebuilding because dependency file simple\.c has changed/
end
end
end end
context "Object builder" do context "Object builder" do
@@ -1306,6 +1420,13 @@ EOF
result = run_test(rsconsfile: "build_after.rb", rscons_args: %w[-j 4]) result = run_test(rsconsfile: "build_after.rb", rscons_args: %w[-j 4])
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
end end
it "allows the user to specify side-effect files produced by another builder" do
test_dir("custom_builder")
result = run_test(rsconsfile: "produces.rb", rscons_args: %w[-j 4])
expect(result.stderr).to eq ""
expect(File.exists?("copy_inc.h")).to be_truthy
end
end end
context "CLI" do context "CLI" do
+4 -4
View File
@@ -26,13 +26,13 @@ module Rscons
cache = build_from(_cache) cache = build_from(_cache)
expect(File).to receive(:exists?).with("one").and_return(true) expect(File).to receive(:exists?).with("one").and_return(true)
expect(File).to receive(:exists?).with("one/two").and_return(false) expect(File).to receive(:exists?).with("one/two").and_return(false)
expect(FileUtils).to receive(:mkdir).with("one/two") expect(FileUtils).to receive(:mkdir_p).with("one/two")
expect(File).to receive(:exists?).with("one/two/three").and_return(false) expect(File).to receive(:exists?).with("one/two/three").and_return(false)
expect(FileUtils).to receive(:mkdir).with("one/two/three") expect(FileUtils).to receive(:mkdir_p).with("one/two/three")
expect(File).to receive(:exists?).with("one").and_return(true) expect(File).to receive(:exists?).with("one").and_return(true)
expect(File).to receive(:exists?).with("one/two").and_return(true) expect(File).to receive(:exists?).with("one/two").and_return(true)
expect(File).to receive(:exists?).with("one/two/four").and_return(false) expect(File).to receive(:exists?).with("one/two/four").and_return(false)
expect(FileUtils).to receive(:mkdir).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")
expect(cache.directories).to eq ["one/two", "one/two/three", "one/two/four"] expect(cache.directories).to eq ["one/two", "one/two/three", "one/two/four"]
@@ -43,7 +43,7 @@ module Rscons
cache = build_from(_cache) cache = build_from(_cache)
expect(File).to receive(:exists?).with("/one").and_return(true) expect(File).to receive(:exists?).with("/one").and_return(true)
expect(File).to receive(:exists?).with("/one/two").and_return(false) expect(File).to receive(:exists?).with("/one/two").and_return(false)
expect(FileUtils).to receive(:mkdir).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).to eq ["/one/two"] expect(cache.directories).to eq ["/one/two"]
end end
+3 -3
View File
@@ -184,7 +184,7 @@ module Rscons
env["foo"] = {} env["foo"] = {}
expect(env.expand_varref(["-p${path}", "${flags}"])).to eq ["-pdir1", "-pdir2", "-x", "-y", "-z"] expect(env.expand_varref(["-p${path}", "${flags}"])).to eq ["-pdir1", "-pdir2", "-x", "-y", "-z"]
expect(env.expand_varref("foo")).to eq "foo" expect(env.expand_varref("foo")).to eq "foo"
expect {env.expand_varref("${foo}")}.to raise_error /Unknown.varref.type/ expect {env.expand_varref("${foo}")}.to raise_error /Unknown.construction.variable.type/
expect(env.expand_varref("${specialflag}")).to eq "-z" expect(env.expand_varref("${specialflag}")).to eq "-z"
expect(env.expand_varref("${path}")).to eq ["dir1", "dir2"] expect(env.expand_varref("${path}")).to eq ["dir1", "dir2"]
end end
@@ -329,7 +329,7 @@ module Rscons
expect(File).to receive(:read).with('makefile').and_return(<<EOS) expect(File).to receive(:read).with('makefile').and_return(<<EOS)
module.o: source.cc module.o: source.cc
EOS EOS
expect(Environment.parse_makefile_deps('makefile', 'module.o')).to eq ['source.cc'] expect(Environment.parse_makefile_deps('makefile')).to eq ['source.cc']
end end
it 'handles dependencies split across many lines' do it 'handles dependencies split across many lines' do
@@ -338,7 +338,7 @@ module.o: module.c \\
module.h \\ module.h \\
other.h other.h
EOS EOS
expect(Environment.parse_makefile_deps('makefile', 'module.o')).to eq [ expect(Environment.parse_makefile_deps('makefile')).to eq [
'module.c', 'module.h', 'other.h'] 'module.c', 'module.h', 'other.h']
end end
end end
+31 -3
View File
@@ -159,14 +159,33 @@ module Rscons
it "calls a lambda with the given lambda arguments" do it "calls a lambda with the given lambda arguments" do
expect(v.expand_varref("${lambda}", [v: "fez"])).to eq("fez--12") expect(v.expand_varref("${lambda}", [v: "fez"])).to eq("fez--12")
end end
it "returns true when passed true" do
expect(v.expand_varref(true, :lambda_args)).to eq(true)
end
it "returns false when passed false" do
expect(v.expand_varref(false, :lambda_args)).to eq(false)
end
it "raises an error when given an invalid argument" do it "raises an error when given an invalid argument" do
expect { v.expand_varref({a: :b}, :lambda_args) }.to raise_error /Unknown varref type: Hash/ expect { v.expand_varref({a: :b}, :lambda_args) }.to raise_error /Unknown construction variable type: Hash/
end end
it "raises an error when an expanded variable is an unexpected type" do it "raises an error when an expanded variable is an unexpected type" do
expect(v).to receive(:[]).at_least(1).times.with("bad").and_return("bad_val") expect(v).to receive(:get_var).at_least(1).times.with("bad").and_return("bad_val")
expect(v).to receive(:expand_varref).with("bad_val", :lambda_args).and_return({a: :b}) expect(v).to receive(:expand_varref).with("bad_val", :lambda_args).and_return({a: :b})
expect(v).to receive(:expand_varref).and_call_original expect(v).to receive(:expand_varref).and_call_original
expect { v.expand_varref("${bad}", :lambda_args) }.to raise_error /I do not know how to expand a variable reference to a Hash/ expect { v.expand_varref("${bad}", :lambda_args) }.to raise_error /Unknown construction variable type: Hash/
end
it "expands symbols within a string outside an array" do
v['var'] = :a_symbol
expect(v.expand_varref("this is ${var}", :lambda_args)).to eq "this is a_symbol"
end
it "expands booleans within a string outside an array" do
v['var'] = false
expect(v.expand_varref("this is ${var}", :lambda_args)).to eq "this is false"
end
it "expands symbols and booleans in an array" do
v['var'] = [:a_symbol, false]
expanded = v.expand_varref("this is ${var}", :lambda_args)
expect(expanded).to eq(["this is a_symbol", "this is false"])
end end
end end
@@ -199,5 +218,14 @@ module Rscons
end end
end end
describe "#values_at" do
it "returns an array of the values associated with the given keys" do
v = VarSet.new({"fuz" => "a string", "foo" => 42, "bar" => :baz,
"qax" => [3, 6], "qux" => {a: :b}})
expect(v.values_at).to eq []
expect(v.values_at(*%w[fuz qux qax])).to eq ["a string", {a: :b}, [3, 6]]
end
end
end end
end end