Compare commits
124
Commits
784bbd471b
...
v2.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d08e2f6b5c | ||
|
|
90365dd197 | ||
|
|
8d05516c40 | ||
|
|
db49d86866 | ||
|
|
e667455c32 | ||
|
|
e62332aba6 | ||
|
|
397639849d | ||
|
|
96d5caf2e5 | ||
|
|
f4b10a30af | ||
|
|
cf52c3fc6f | ||
|
|
5378afb6f8 | ||
|
|
48d9273cc6 | ||
|
|
efea4f1181 | ||
|
|
b4fa6d965c | ||
|
|
ac56ad34f6 | ||
|
|
2fd533a32a | ||
|
|
c938ed71d1 | ||
|
|
ec60201a62 | ||
|
|
0cafb8c5b3 | ||
|
|
004810386b | ||
|
|
a15ee9daaf | ||
|
|
cd33405697 | ||
|
|
efa77066bf | ||
|
|
a1bb9d81fd | ||
|
|
0344f02cb1 | ||
|
|
d52bdaf046 | ||
|
|
ad7ccb771d | ||
|
|
044ee343ca | ||
|
|
eb54a2b64a | ||
|
|
2dd1ce9f1d | ||
|
|
1e157c79ec | ||
|
|
2385874f4a | ||
|
|
c5c42e6f4a | ||
|
|
9a627b2150 | ||
|
|
937c964c3e | ||
|
|
a2a1d08014 | ||
|
|
0ccf43cc96 | ||
|
|
d202e15a57 | ||
|
|
94f808b1fc | ||
|
|
51a9dd365f | ||
|
|
6e590b62a6 | ||
|
|
3aac190ad6 | ||
|
|
d6b9487e65 | ||
|
|
66d2700faa | ||
|
|
ba98af1424 | ||
|
|
cf4f80c9ae | ||
|
|
d74785d579 | ||
|
|
4fd822fb95 | ||
|
|
b39fd80d8c | ||
|
|
ef00c9f377 | ||
|
|
1ddc03b285 | ||
|
|
f686f9e787 | ||
|
|
b6c19b7f00 | ||
|
|
4fc18e5d91 | ||
|
|
cda5584448 | ||
|
|
30d82392b5 | ||
|
|
63d875da7f | ||
|
|
8593c5e219 | ||
|
|
9bf0abbece | ||
|
|
02ef6e0846 | ||
|
|
f97ee94e44 | ||
|
|
55a2ea6d6b | ||
|
|
59ab8e46c4 | ||
|
|
baebe744b3 | ||
|
|
0ad3f815c5 | ||
|
|
ca6336c715 | ||
|
|
a11c27f415 | ||
|
|
5c77cac354 | ||
|
|
46294b2a88 | ||
|
|
5f71781806 | ||
|
|
22b007df3a | ||
|
|
4339d358f8 | ||
|
|
e4b96b759f | ||
|
|
fcb686ac5f | ||
|
|
e5c690a4fc | ||
|
|
5b67ad88d3 | ||
|
|
dbd8b47bf9 | ||
|
|
8222958ae3 | ||
|
|
56f104b5e7 | ||
|
|
947a65a75e | ||
|
|
66fac4f982 | ||
|
|
254584435f | ||
|
|
f74d8202d3 | ||
|
|
bbf2319da6 | ||
|
|
812b1cbe9c | ||
|
|
0d2fe02b9c | ||
|
|
c7fb88db82 | ||
|
|
49f4367125 | ||
|
|
c4d062752d | ||
|
|
5b3e10ff72 | ||
|
|
5f1ad78e8c | ||
|
|
9f00063d57 | ||
|
|
6328ceee2a | ||
|
|
26a9307533 | ||
|
|
90886ed9ad | ||
|
|
8df73e9d11 | ||
|
|
f239b27c3a | ||
|
|
5e6b83411b | ||
|
|
54e77f235f | ||
|
|
4e08de9043 | ||
|
|
0d8aec0eb1 | ||
|
|
573a542f84 | ||
|
|
ef2a9084db | ||
|
|
bfcd0c0b05 | ||
|
|
215f5e4b51 | ||
|
|
6229cef015 | ||
|
|
6be0d322a7 | ||
|
|
89e43fea1c | ||
|
|
458eeaf93d | ||
|
|
dd3d468a28 | ||
|
|
a4dec210ae | ||
|
|
fa14102ba5 | ||
|
|
87a15b3868 | ||
|
|
f23fbcd6d1 | ||
|
|
b02a7573b9 | ||
|
|
d0cd0a14a4 | ||
|
|
5cb895f465 | ||
|
|
cd1c54bf0d | ||
|
|
a4fead7dc6 | ||
|
|
59a3cb4e10 | ||
|
|
785e8e18fd | ||
|
|
49e1bca1f7 | ||
|
|
fd054a07c4 | ||
|
|
328babe1f4 |
No files matched your search
+3
-1
@@ -4,6 +4,8 @@
|
||||
/build_test_run/
|
||||
/coverage/
|
||||
/dist/
|
||||
/doc/
|
||||
/gen/
|
||||
/large_project/
|
||||
/pkg/
|
||||
/test/
|
||||
/yard/
|
||||
+116
-70
@@ -1,80 +1,126 @@
|
||||
## ChangeLog
|
||||
## v2.0.2
|
||||
|
||||
### v1.17.0
|
||||
### Fixes
|
||||
|
||||
#### New Features
|
||||
- #113 - distinguish object files built from multiple sources with the same base name but different extensions
|
||||
|
||||
## v2.0.1
|
||||
|
||||
### Fixes
|
||||
|
||||
- #112 - Install builder cannot replace a currently executing binary on Linux
|
||||
|
||||
## v2.0.0
|
||||
|
||||
- convert rscons from a Ruby gem to a standalone script
|
||||
- compress rscons distributable script
|
||||
- add configure operation to detect compilers, check for headers/libraries, etc... (invoked automatically if needed)
|
||||
- Environments store builder classes instead of instances of builder classes
|
||||
- use a separate Builder instance for each build operation
|
||||
- load Rsconscript from Rsconscript/Rsconscript.rb instead of Rsconsfile
|
||||
- drop support for builder run methods using the old 5 parameter signature
|
||||
- remove Environment#build_dir
|
||||
- set Environment build root in configure step
|
||||
- remove Builder#finalize (now #run called repeatedly until builder completes)
|
||||
- remove Builder#setup
|
||||
- remove Builder#features and Builder#produces?
|
||||
- add functionality to allow builders to wait on Ruby threads or other builders
|
||||
- add install/uninstall/distclean command-line operations
|
||||
- preserve makefile dependency files under build directory
|
||||
- remove a few deprecated methods
|
||||
- pass a Builder instance to build hooks instead of a build operation Hash
|
||||
- support a basic markup syntax in builder run messages to colorize target/source files
|
||||
- hide (but store) failed compilation command by default so the user doesn't have to scroll back as much to see compiler output
|
||||
- refactor to remove some redundancy among built-in builders
|
||||
- track object file source language (correctly determine linker when only passed object files previously built by particular toolchains)
|
||||
- add barriers
|
||||
- add InstallDirectory builder
|
||||
- change Install builder to copy files on 'install' operation
|
||||
- add "prefix" construction variable based on configured installation prefix
|
||||
- allow passing builder objects as sources to build targets
|
||||
- differentiate 'build' targets from 'install' targets in cache contents
|
||||
- add verbose mode
|
||||
- show build progress as a percentage in builder output messages
|
||||
- various performance improvements
|
||||
- wrote a new user guide
|
||||
- added new website ([https://holtrop.github.io/rscons/](https://holtrop.github.io/rscons/))
|
||||
- added new logo
|
||||
|
||||
## v1.17.0
|
||||
|
||||
### New Features
|
||||
|
||||
- allow construction variable expansion on `true` and `false` values.
|
||||
- remove makefile target name check when parsing dependencies
|
||||
|
||||
### v1.16.0
|
||||
## v1.16.0
|
||||
|
||||
#### New Features
|
||||
### New Features
|
||||
|
||||
- Add `Rscons.glob`
|
||||
- Support command-line variables
|
||||
- improve debuggability of cache.up_to_date?
|
||||
- improve debuggability of `cache.up_to_date?`
|
||||
- allow passing a VarSet into cache methods
|
||||
|
||||
#### Fixes
|
||||
### Fixes
|
||||
|
||||
- generate dependencies for D builds
|
||||
|
||||
### v1.15.0
|
||||
## v1.15.0
|
||||
|
||||
- allow json 1.x or 2.x
|
||||
|
||||
### v1.14.0
|
||||
## v1.14.0
|
||||
|
||||
#### New Features
|
||||
### New Features
|
||||
|
||||
- #45 - Add Rscons::VarSet#values_at
|
||||
- #45 - Add `Rscons::VarSet#values_at`
|
||||
|
||||
#### Fixes
|
||||
### Fixes
|
||||
|
||||
- #44 - Environment#print_builder_run_message should support string commands
|
||||
- #44 - `Environment#print_builder_run_message` should support string commands
|
||||
|
||||
### v1.13.0
|
||||
## v1.13.0
|
||||
|
||||
#### New Features
|
||||
### New Features
|
||||
|
||||
- #43 - Add ability to record side-effect file production
|
||||
|
||||
### v1.12.0
|
||||
## v1.12.0
|
||||
|
||||
#### New Features
|
||||
### New Features
|
||||
|
||||
- #40 - env.depends should imply env.build_after
|
||||
- #40 - env.depends should imply `env.build_after`
|
||||
- #41 - be more colorful
|
||||
|
||||
#### Fixes
|
||||
### Fixes
|
||||
|
||||
- #39 - wait for in-progress subcommands to complete on build failure
|
||||
- #42 - cloned Environments should inherit n_threads
|
||||
- #42 - cloned Environments should inherit `n_threads`
|
||||
|
||||
### v1.11.1
|
||||
## v1.11.1
|
||||
|
||||
#### Fixes
|
||||
### Fixes
|
||||
|
||||
- fix the circular build dependency detection logic
|
||||
|
||||
### v1.11.0
|
||||
## v1.11.0
|
||||
|
||||
#### New Features
|
||||
### 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
|
||||
- #34 - Allow overriding `n_threads` on a per-Environment level
|
||||
|
||||
#### Fixes
|
||||
### 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
|
||||
- #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
|
||||
## v1.10.0
|
||||
|
||||
#### New Features
|
||||
### New Features
|
||||
|
||||
- #23 - add parallelization - builds are now parallelized by default
|
||||
- #31 - add LEXSUFFIX, YACCSUFFIX construction variables
|
||||
@@ -84,100 +130,100 @@
|
||||
- Add builder features
|
||||
- #8 - add SharedObject and SharedLibrary builders
|
||||
|
||||
#### Fixes
|
||||
### Fixes
|
||||
|
||||
- expand target and source paths before calling Builder#create_build_target
|
||||
- #29 - fix PROGSUFFIX handling
|
||||
- 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
|
||||
## v1.9.3
|
||||
|
||||
- Environment#parse_flags should put -std=XXX flags in CCFLAGS, not CFLAGS
|
||||
- `Environment#parse_flags` should put -std=XXX flags in CCFLAGS, not CFLAGS
|
||||
|
||||
### v1.9.2
|
||||
## v1.9.2
|
||||
|
||||
- allow phony targets in conjunction with build roots
|
||||
|
||||
### v1.9.1
|
||||
## 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
|
||||
## v1.9.0
|
||||
|
||||
#### New Features
|
||||
### New Features
|
||||
|
||||
- #6 - add Install and Copy builders
|
||||
- #22 - allow overriding Command builder short description with CMD_DESC variable
|
||||
- #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
|
||||
### 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
|
||||
- fix `Rscons.set_suffix` to append the given suffix if the filename has none
|
||||
- remove ${CFLAGS} from default `CPP_CMD`
|
||||
|
||||
### v1.8.1
|
||||
## v1.8.1
|
||||
|
||||
- fix Environment#dump when construction variables are symbols
|
||||
|
||||
### v1.8.0
|
||||
## 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
|
||||
- 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
|
||||
## v1.7.0
|
||||
|
||||
- allow build hooks to register new build targets
|
||||
- add post-build hooks (register with Environment#add_post_build_hook)
|
||||
- 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
|
||||
- allow trailing slashes in arguments to `Environment#build_dir`
|
||||
|
||||
### v1.6.1
|
||||
## 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
|
||||
## v1.6.0
|
||||
|
||||
- support lambdas as construction variable values
|
||||
|
||||
### v1.5.0
|
||||
## 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
|
||||
- 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
|
||||
## v1.4.3
|
||||
|
||||
- fix builders properly using construction variable overrides
|
||||
- expand nil construction variables to empty strings
|
||||
|
||||
### v1.4.2
|
||||
## v1.4.2
|
||||
|
||||
- add Environment#expand_path
|
||||
- add `Environment#expand_path`
|
||||
- expand construction variable references in builder targets and sources before invoking builder
|
||||
|
||||
### v1.4.1
|
||||
## v1.4.1
|
||||
|
||||
- fix invoking a builder with no sources while a build root defined
|
||||
|
||||
### v1.4.0
|
||||
## v1.4.0
|
||||
|
||||
- add CFile builder
|
||||
- add Disassemble builder
|
||||
@@ -190,21 +236,21 @@
|
||||
- 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)
|
||||
- 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
|
||||
## 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
|
||||
## 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=
|
||||
- allow nil to be passed in to `Environment#build_root=`
|
||||
|
||||
### v1.1.0
|
||||
## v1.1.0
|
||||
|
||||
- Change Cache#up_to_date?() and #register_build() to accept a single target
|
||||
- Change `Cache#up_to_date?` and `#register_build` to accept a single target
|
||||
file or an array of target file names
|
||||
@@ -6,3 +6,5 @@ gem "rake"
|
||||
gem "simplecov"
|
||||
gem "yard"
|
||||
gem "rdoc"
|
||||
gem "redcarpet"
|
||||
gem "syntax"
|
||||
+21
-17
@@ -2,29 +2,31 @@ GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
diff-lcs (1.3)
|
||||
docile (1.1.5)
|
||||
json (2.1.0)
|
||||
rake (12.3.1)
|
||||
rdoc (5.1.0)
|
||||
rspec (3.6.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
rspec-mocks (~> 3.6.0)
|
||||
rspec-core (3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-expectations (3.6.0)
|
||||
docile (1.3.2)
|
||||
json (2.2.0)
|
||||
rake (12.3.2)
|
||||
rdoc (6.1.1)
|
||||
redcarpet (3.4.0)
|
||||
rspec (3.8.0)
|
||||
rspec-core (~> 3.8.0)
|
||||
rspec-expectations (~> 3.8.0)
|
||||
rspec-mocks (~> 3.8.0)
|
||||
rspec-core (3.8.2)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-expectations (3.8.4)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-mocks (3.6.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-mocks (3.8.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-support (3.6.0)
|
||||
rspec-support (~> 3.8.0)
|
||||
rspec-support (3.8.2)
|
||||
simplecov (0.15.0)
|
||||
docile (~> 1.1.0)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.2)
|
||||
yard (0.9.12)
|
||||
syntax (1.2.2)
|
||||
yard (0.9.20)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
@@ -34,9 +36,11 @@ DEPENDENCIES
|
||||
json
|
||||
rake
|
||||
rdoc
|
||||
redcarpet
|
||||
rspec
|
||||
simplecov
|
||||
syntax
|
||||
yard
|
||||
|
||||
BUNDLED WITH
|
||||
1.16.2
|
||||
1.17.3
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2013 Josh Holtrop
|
||||
Copyright (c) 2013-2019 Josh Holtrop
|
||||
|
||||
MIT License
|
||||
|
||||
@@ -19,4 +19,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,591 +1,10 @@
|
||||
# Rscons
|
||||
|
||||
Rscons (https://github.com/holtrop/rscons) is a software construction framework
|
||||
inspired by SCons and waf but implemented in Ruby.
|
||||

|
||||
|
||||
## Installation
|
||||
Rscons (https://github.com/holtrop/rscons) is an open-source build system
|
||||
for developers.
|
||||
|
||||
Rscons is distributed as a standalone Ruby script named ``rscons``. This file
|
||||
(containing the complete rscons build system) can be checked into your project
|
||||
and versioned along with your project.
|
||||
Rscons is written in Ruby, and is inspired by [SCons](https://scons.org/) and [waf](https://waf.io/).
|
||||
|
||||
## Usage
|
||||
|
||||
### Standalone
|
||||
|
||||
Rscons provides a standalone executable ("rscons") with a command-line
|
||||
interface. The rscons executable will read a build script (by default named
|
||||
Rsconsfile or Rsconsfile.rb) and execute its contents.
|
||||
|
||||
### With Rake
|
||||
|
||||
Rscons can be used with rake as well. The same content that would be written
|
||||
in Rsconsfile can be placed in a Rakefile. It could be placed within a rake
|
||||
task block or split among multiple tasks.
|
||||
|
||||
## Example Build Scripts
|
||||
|
||||
### Example: Building a C Program
|
||||
|
||||
```ruby
|
||||
Rscons::Environment.new do |env|
|
||||
env["CFLAGS"] << "-Wall"
|
||||
env.Program("program", Rscons.glob("src/**/*.c"))
|
||||
end
|
||||
```
|
||||
|
||||
### Example: Building a D Program
|
||||
|
||||
```ruby
|
||||
Rscons::Environment.new do |env|
|
||||
env["DFLAGS"] << "-Wall"
|
||||
env.Program("program", Rscons.glob("src/**/*.d"))
|
||||
end
|
||||
```
|
||||
|
||||
### Example: Cloning an Environment
|
||||
|
||||
```ruby
|
||||
main_env = Rscons::Environment.new do |env|
|
||||
# Store object files from sources under "src" in "build/main"
|
||||
env.build_dir("src", "build/main")
|
||||
env["CFLAGS"] = ["-DSOME_DEFINE", "-O3"]
|
||||
env["LIBS"] = ["SDL"]
|
||||
env.Program("program", Rscons.glob("src/**/*.cc"))
|
||||
end
|
||||
|
||||
debug_env = main_env.clone do |env|
|
||||
# Store object files from sources under "src" in "build/debug"
|
||||
env.build_dir("src", "build/debug")
|
||||
env["CFLAGS"] -= ["-O3"]
|
||||
env["CFLAGS"] += ["-g", "-O0"]
|
||||
env.Program("program-debug", Rscons.glob("src/**/*.cc"))
|
||||
end
|
||||
```
|
||||
|
||||
### Example: Custom Builder
|
||||
|
||||
Custom builders are implemented as classes which extend from `Rscons::Builder`.
|
||||
The builder must have a `run` method which is called to invoke the builder.
|
||||
The `run` method should return the name of the target built on success, and
|
||||
`false` on failure.
|
||||
|
||||
```ruby
|
||||
class GenerateFoo < Rscons::Builder
|
||||
def run(target, sources, cache, env, vars)
|
||||
cache.mkdir_p(File.dirname(target))
|
||||
File.open(target, "w") do |fh|
|
||||
fh.puts <<EOF
|
||||
#define GENERATED 42
|
||||
EOF
|
||||
end
|
||||
target
|
||||
end
|
||||
end
|
||||
|
||||
Rscons::Environment.new do |env|
|
||||
env.add_builder(GenerateFoo.new)
|
||||
env.GenerateFoo("foo.h", [])
|
||||
env.Program("a.out", Rscons.glob("*.c"))
|
||||
end
|
||||
```
|
||||
|
||||
### Example: Custom Builder That Only Regenerates When Necessary
|
||||
|
||||
```ruby
|
||||
class CmdBuilder < Rscons::Builder
|
||||
def run(target, sources, cache, env, vars)
|
||||
cmd = ["cmd", "-i", sources.first, "-o", target]
|
||||
unless cache.up_to_date?(target, cmd, sources, env)
|
||||
cache.mkdir_p(File.dirname(target))
|
||||
system(cmd)
|
||||
cache.register_build(target, cmd, sources, env)
|
||||
end
|
||||
target
|
||||
end
|
||||
end
|
||||
|
||||
Rscons::Environment.new do |env|
|
||||
env.add_builder(CmdBuilder.new)
|
||||
env.CmdBuilder("foo.gen", "foo_gen.cfg")
|
||||
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
|
||||
|
||||
```ruby
|
||||
class CModuleGenerator < Rscons::Builder
|
||||
def run(target, sources, cache, env, vars)
|
||||
c_fname = target
|
||||
h_fname = target.sub(/\.c$/, ".h")
|
||||
cmd = ["generate_c_and_h", sources.first, c_fname, h_fname]
|
||||
unless cache.up_to_date?([c_fname, h_fname], cmd, sources, env)
|
||||
cache.mkdir_p(File.dirname(target))
|
||||
system(cmd)
|
||||
cache.register_build([c_fname, h_fname], cmd, sources, env)
|
||||
end
|
||||
target
|
||||
end
|
||||
end
|
||||
|
||||
Rscons::Environment.new do |env|
|
||||
env.add_builder(CModuleGenerator.new)
|
||||
env.CModuleGenerator("build/foo.c", "foo_gen.cfg")
|
||||
end
|
||||
```
|
||||
|
||||
### Example: Custom Builder Using Builder#standard_build()
|
||||
|
||||
The `standard_build` method from the `Rscons::Builder` base class can be used
|
||||
when the builder needs to execute a system command to produce the target file.
|
||||
The `standard_build` method will return the correct value so its return value
|
||||
can be used as the return value from the `run` method.
|
||||
|
||||
```ruby
|
||||
class CmdBuilder < Rscons::Builder
|
||||
def run(target, sources, cache, env, vars)
|
||||
cmd = ["cmd", "-i", sources.first, "-o", target]
|
||||
standard_build("CmdBld #{target}", target, cmd, sources, env, cache)
|
||||
end
|
||||
end
|
||||
|
||||
Rscons::Environment.new do |env|
|
||||
env.add_builder(CmdBuilder.new)
|
||||
env.CmdBuilder("foo.gen", "foo_gen.cfg")
|
||||
end
|
||||
```
|
||||
|
||||
### Example: Custom Builder Using Environment#add_builder()
|
||||
|
||||
The `add_builder` method of the `Rscons::Environment` class optionally allows
|
||||
you to define and register a builder by providing a name and action block. This
|
||||
can be useful if the builder you are trying to define is easily expressed as a
|
||||
short ruby procedure. When `add_builder` is called in this manner a new builder
|
||||
will be registered with the environment with the given name. When this builder
|
||||
is used it will call the provided block in order to build the target.
|
||||
|
||||
```ruby
|
||||
Rscons::Environment.new do |env|
|
||||
env.add_builder(:JsonToYaml) do |target, sources, cache, env, vars|
|
||||
unless cache.up_to_date?(target, :JsonToYaml, sources, env)
|
||||
cache.mkdir_p(File.dirname(target))
|
||||
File.open(target, 'w') do |f|
|
||||
f.write(YAML.dump(JSON.load(IO.read(sources.first))))
|
||||
end
|
||||
cache.register_build(target, :JsonToYaml, sources, env)
|
||||
end
|
||||
target
|
||||
end
|
||||
env.JsonToYaml('foo.yml','foo.json')
|
||||
end
|
||||
```
|
||||
|
||||
### Example: Using different compilation flags for some sources
|
||||
|
||||
```ruby
|
||||
Rscons::Environment.new do |env|
|
||||
env["CFLAGS"] = ["-O3", "-Wall", "-DDEFINE"]
|
||||
env.add_build_hook do |build_op|
|
||||
if build_op[:target] =~ %r{build/third-party}
|
||||
build_op[:vars]["CFLAGS"] -= ["-Wall"]
|
||||
end
|
||||
end
|
||||
env.build_dir("src", "build")
|
||||
env.Program("program", Rscons.glob("**/*.cc"))
|
||||
end
|
||||
```
|
||||
|
||||
### Example: Creating a static library
|
||||
|
||||
```ruby
|
||||
Rscons::Environment.new do |env|
|
||||
env.Library("mylib.a", Rscons.glob("src/**/*.c"))
|
||||
end
|
||||
```
|
||||
|
||||
### Example: Creating a C++ parser source from a Yacc/Bison input file
|
||||
|
||||
```ruby
|
||||
Rscons::Environment.new do |env|
|
||||
env.CFile("#{env.build_root}/parser.tab.cc", "parser.yy")
|
||||
end
|
||||
```
|
||||
|
||||
## Details
|
||||
|
||||
### Environments
|
||||
|
||||
The Environment is the main top-level object that Rscons operates with. An
|
||||
Environment must be created by the user in order to build anything. All build
|
||||
targets are registered within an Environment. In many cases only a single
|
||||
Environment will be needed, but more than one can be created (either from
|
||||
scratch or by cloning another existing Environment) if needed.
|
||||
|
||||
An Environment consists of:
|
||||
|
||||
* a collection of builders
|
||||
* a collection of construction variables used by those builders
|
||||
* a mapping of build directories from source directories
|
||||
* a default build root to apply if no specific build directories are matched
|
||||
* a collection of user-defined build targets
|
||||
* a collection of user-defined build hooks
|
||||
|
||||
When cloning an environment, by default the construction variables, builders,
|
||||
build hooks, build directories, and build root are cloned, but the new
|
||||
environment does not inherit any of the registered build targets.
|
||||
|
||||
The set of environment attributes that are cloned is controllable via the
|
||||
`:clone` option to the `#clone` method.
|
||||
For example, `env.clone(clone: [:variables, :builders])` will include
|
||||
construction variables, and builders but not build hooks, build directories, or
|
||||
the build root.
|
||||
|
||||
The set of pending targets is never cloned.
|
||||
|
||||
Cloned environments contain "deep copies" of construction variables.
|
||||
For example, in:
|
||||
|
||||
```ruby
|
||||
base_env = Rscons::Environment.new
|
||||
base_env["CPPPATH"] = ["one", "two"]
|
||||
cloned_env = base_env.clone
|
||||
cloned_env["CPPPATH"] << "three"
|
||||
```
|
||||
|
||||
`base_env["CPPPATH"]` will not include "three".
|
||||
|
||||
### Builders
|
||||
|
||||
Builders are the workhorses that Rscons uses to execute build operations.
|
||||
Each builder is specialized to perform a particular operation.
|
||||
|
||||
Rscons ships with a number of builders:
|
||||
|
||||
* Command, which executes a user-defined command to produce the target.
|
||||
* Copy, which is identical to Install.
|
||||
* CFile, which builds a C or C++ source file from a lex or yacc input file.
|
||||
* Disassemble, which disassembles an object file to a disassembly listing.
|
||||
* Install, which installs files or directories to a specified destination.
|
||||
* Library, which collects object files into a static library archive file.
|
||||
* Object, which compiles source files to produce an object file.
|
||||
* Preprocess, which invokes the C/C++ preprocessor on a source file.
|
||||
* Program, which links object files to produce an executable.
|
||||
* SharedLibrary, which links object files to produce a dynamically loadable
|
||||
library.
|
||||
* SharedObject, which compiles source files to produce an object file, in a way
|
||||
that is able to be used to create a shared library.
|
||||
|
||||
If you want to create an Environment that does not contain any builders,
|
||||
you can use the `:exclude_builders` key to the Environment constructor.
|
||||
|
||||
#### Command
|
||||
|
||||
```ruby
|
||||
env.Command(target, sources, "CMD" => command)
|
||||
# Example
|
||||
env.Command("docs.html", "docs.md",
|
||||
"CMD" => ["pandoc", "-fmarkdown", "-thtml", "-o${_TARGET}", "${_SOURCES}"],
|
||||
"CMD_DESC" => "PANDOC")
|
||||
```
|
||||
|
||||
The command builder executes a user-defined command in order to produce the
|
||||
desired target file based on the provided source files.
|
||||
|
||||
#### CFile
|
||||
|
||||
```ruby
|
||||
env.CFile(target, source)
|
||||
# Example
|
||||
env.CFile("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.
|
||||
|
||||
#### Disassemble
|
||||
|
||||
```ruby
|
||||
env.Disassemble(target, source)
|
||||
# Example
|
||||
env.Disassemble("module.dis", "module.o")
|
||||
```
|
||||
|
||||
The Disassemble builder generates a disassembly listing using objdump from
|
||||
and object file.
|
||||
|
||||
#### Install
|
||||
|
||||
```ruby
|
||||
env.Install(destination, sources)
|
||||
# Example
|
||||
env.Install("dist/bin", "app.exe")
|
||||
env.Install("dist/share", "share")
|
||||
```
|
||||
|
||||
#### Library
|
||||
|
||||
```ruby
|
||||
env.Library(target, sources)
|
||||
# Example
|
||||
env.Library("lib.a", Rscons.glob("src/**/*.c"))
|
||||
```
|
||||
|
||||
The Library builder creates a static library archive from the given source
|
||||
files.
|
||||
|
||||
#### Object
|
||||
|
||||
```ruby
|
||||
env.Object(target, sources)
|
||||
# Example
|
||||
env.Object("module.o", "module.c")
|
||||
```
|
||||
|
||||
The Object builder compiles the given sources to an object file. Although it
|
||||
can be called explicitly, it is more commonly implicitly called by the Program
|
||||
builder.
|
||||
|
||||
#### Preprocess
|
||||
|
||||
```ruby
|
||||
env.Preprocess(target, source)
|
||||
# Example
|
||||
env.Preprocess("module-preprocessed.cc", "module.cc")
|
||||
```
|
||||
|
||||
The Preprocess builder invokes either `${CC}` or `${CXX}` (depending on if the
|
||||
source contains an extension in `${CXXSUFFIX}` or not) and writes the
|
||||
preprocessed output to the target file.
|
||||
|
||||
#### Program
|
||||
|
||||
```ruby
|
||||
env.Program(target, sources)
|
||||
# Example
|
||||
env.Program("myprog", Rscons.glob("src/**/*.cc"))
|
||||
```
|
||||
|
||||
The Program builder compiles and links the given sources to an executable file.
|
||||
Object files or source files can be given as `sources`. A platform-dependent
|
||||
program suffix will be appended to the target name if one is not specified.
|
||||
This can be controlled with the `PROGSUFFIX` construction variable.
|
||||
|
||||
#### SharedLibrary
|
||||
|
||||
```ruby
|
||||
env.SharedLibrary(target, sources)
|
||||
# Example
|
||||
env.SharedLibrary("mydll", Rscons.glob("src/**/*.cc"))
|
||||
```
|
||||
|
||||
The SharedLibrary builder compiles and links the given sources to a dynamically
|
||||
loadable library. Object files or source files can be given as `sources`.
|
||||
A platform-dependent prefix and suffix will be appended to the target name if
|
||||
they are not specified by the user. These values can be controlled by
|
||||
overriding the `SHLIBPREFIX` and `SHLIBSUFFIX` construction variables.
|
||||
|
||||
#### SharedObject
|
||||
|
||||
```ruby
|
||||
env.SharedObject(target, sources)
|
||||
# Example
|
||||
env.SharedObject("lib_module.o", "lib_module.c")
|
||||
```
|
||||
|
||||
The SharedObject builder compiles the given sources to an object file. Any
|
||||
compilation flags necessary to build the object file in a manner that allows it
|
||||
to be used to create a shared library are added. Although it can be called
|
||||
explicitly, it is more commonly implicitly called by the SharedLibrary builder.
|
||||
|
||||
### Construction Variables
|
||||
|
||||
Construction variables are used to define the toolset and any build options
|
||||
that Rscons will use to build a project. The default construction variable
|
||||
values are configured to build applications using gcc. However, all
|
||||
construction variables can be overridden by the user.
|
||||
|
||||
| Name | Type | Description | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| AR | String | Static library archiver executable | "ar" |
|
||||
| ARCMD | Array | Static library archiver command line | ["${AR}", "${ARFLAGS}", "${_TARGET}", "${_SOURCES}"] |
|
||||
| ARFLAGS | Array | Static library archiver flags | ["rcs"] |
|
||||
| AS | String | Assembler executable | "${CC}" |
|
||||
| ASCMD | Array | Assembler command line | ["${AS}", "-c", "-o", "${_TARGET}", "${ASDEPGEN}", "${INCPREFIX}${ASPPPATH}", "${ASPPFLAGS}", "${ASFLAGS}", "${_SOURCES}"] |
|
||||
| ASDEPGEN | Array | Assembly dependency generation flags | ["-MMD", "-MF", "${_DEPFILE}", "-MT", "TARGET"] |
|
||||
| ASFLAGS | Array | Assembler flags | [] |
|
||||
| ASPPFLAGS | Array | Assembler preprocessor flags | ["${CPPFLAGS}"] |
|
||||
| ASPPPATH | Array | Assembler preprocessor path | ["${CPPPATH}"] |
|
||||
| ASSUFFIX | Array | Assembly file suffixes | [".S"] |
|
||||
| CC | String | C compiler executable | "gcc" |
|
||||
| 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}", "-MT", "TARGET"] |
|
||||
| CCFLAGS | Array | Common flags for both C and C++ compiler | [] |
|
||||
| CFLAGS | Array | C compiler flags | [] |
|
||||
| CPP_CMD | Array | Preprocess command line | ["${_PREPROCESS_CC}", "-E", "${_PREPROCESS_DEPGEN}", "-o", "${_TARGET}", "${INCPREFIX}${CPPPATH}", "${CPPFLAGS}", "${_SOURCES}"] |
|
||||
| CPP_TARGET_SUFFIX | String | Suffix used for crt:preprocess target filename. | ".c" |
|
||||
| CPPDEFINES | Array | C preprocessor defines | [] |
|
||||
| CPPDEFPREFIX | String | Prefix used for C preprocessor to introduce a define | "-D" |
|
||||
| CPPFLAGS | Array | C preprocessor flags | ["${CPPDEFPREFIX}${CPPDEFINES}"] |
|
||||
| CPPPATH | Array | C preprocessor path | [] |
|
||||
| CSUFFIX | Array | C source file suffixes | [".c"] |
|
||||
| CXX | String | C++ compiler executable | "g++" |
|
||||
| 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}", "-MT", "TARGET"] |
|
||||
| CXXFLAGS | Array | C++ compiler flags | [] |
|
||||
| CXXSUFFIX | Array | C++ source file suffixes | [".cc", ".cpp", ".cxx", ".C"] |
|
||||
| D_IMPORT_PATH | Array | D compiler import path | [] |
|
||||
| DC | String | D compiler executable | "gdc" |
|
||||
| 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" |
|
||||
| DFLAGS | Array | D compiler flags | [] |
|
||||
| DISASM_CMD | Array | Disassemble command line | ["${OBJDUMP}", "${DISASM_FLAGS}", "${_SOURCES}"] |
|
||||
| DISASM_FLAGS | Array | Disassemble flags | ["--disassemble", "--source"] |
|
||||
| DSUFFIX | String/Array | Default D source file suffix | ".d" |
|
||||
| INCPREFIX | String | Prefix used for C preprocessor to add an include path | "-I" |
|
||||
| LD | String | nil | Linker executable (automatically determined when nil) | nil (if nil, ${CC}, ${CXX}, or ${DC} is used depending on the sources being linked) |
|
||||
| LDCMD | Array | Link command line | ["${LD}", "-o", "${_TARGET}", "${LDFLAGS}", "${_SOURCES}", "${LIBDIRPREFIX}${LIBPATH}", "${LIBLINKPREFIX}${LIBS}"] |
|
||||
| LDFLAGS | Array | Linker flags | [] |
|
||||
| LEX | String | Lex executable | "flex" |
|
||||
| LEX_CMD | Array | Lex command line | ["${LEX}", "${LEX_FLAGS}", "-o", "${_TARGET}", "${_SOURCES}"] |
|
||||
| LEX_FLAGS | Array | Lex flags | [] |
|
||||
| LEXSUFFIX | Array | Lex input file suffixes | [".l", ".ll"] |
|
||||
| LIBDIRPREFIX | String | Prefix given to linker to add a library search path | "-L" |
|
||||
| LIBLINKPREFIX | String | Prefix given to linker to add a library to link with | "-l" |
|
||||
| LIBPATH | Array | Library load path | [] |
|
||||
| LIBS | Array | Libraries to link with | [] |
|
||||
| LIBSUFFIX | String/Array | Default static library file suffix | ".a" |
|
||||
| OBJDUMP | String | Objdump executable | "objdump" |
|
||||
| OBJSUFFIX | String/Array | Default object file suffix | ".o" |
|
||||
| PROGSUFFIX | String | Default program suffix. | Windows: ".exe", POSIX: "" |
|
||||
| SHCC | String | Shared object C compiler | "${CC}" |
|
||||
| SHCCCMD | Array | Shared object C compiler command line | ["${SHCC}", "-c", "-o", "${_TARGET}", "${CCDEPGEN}", "${INCPREFIX}${CPPPATH}", "${CPPFLAGS}", "${SHCFLAGS}", "${SHCCFLAGS}", "${_SOURCES}"] |
|
||||
| SHCCFLAGS | Array | Shared object C and C++ compiler flags | Windows: ["${CCFLAGS}"], POSIX: ["${CCFLAGS}", -fPIC"] |
|
||||
| SHCFLAGS | Array | Shared object C compiler flags | ["${CFLAGS}"] |
|
||||
| SHCXX | String | Shared object C++ compiler | "${CXX}" |
|
||||
| SHCXXCMD | Array | Shared object C++ compiler command line | ["${SHCXX}", "-c", "-o", "${_TARGET}", "${CXXDEPGEN}", "${INCPREFIX}${CPPPATH}", "${CPPFLAGS}", "${SHCXXFLAGS}", "${SHCCFLAGS}", "${_SOURCES}"] |
|
||||
| SHCXXFLAGS | Array | Shared object C++ compiler flags | ["${CXXFLAGS}"] |
|
||||
| SHDC | String | Shared object D compiler | "gdc" |
|
||||
| SHDCCMD | Array | Shared object D compiler command line | ["${SHDC}", "-c", "-o", "${_TARGET}", "${INCPREFIX}${D_IMPORT_PATH}", "${SHDFLAGS}", "${_SOURCES}"] |
|
||||
| SHDFLAGS | Array | Shared object D compiler flags | Windows: ["${DFLAGS}"], POSIX: ["${DFLAGS}", "-fPIC"] |
|
||||
| SHLD | String | Shared library linker | nil (if nil, ${SHCC}, ${SHCXX}, or ${SHDC} is used depending on the sources being linked) |
|
||||
| SHLDCMD | Array | Shared library linker command line | ["${SHLD}", "-o", "${_TARGET}", "${SHLDFLAGS}", "${_SOURCES}", "${SHLIBDIRPREFIX}${LIBPATH}", "${SHLIBLINKPREFIX}${LIBS}"] |
|
||||
| SHLDFLAGS | Array | Shared library linker flags | ["${LDFLAGS}", "-shared"] |
|
||||
| SHLIBDIRPREFIX | String | Prefix given to shared library linker to add a library search path | "-L" |
|
||||
| SHLIBLINKPREFIX | String | Prefix given to shared library linker to add a library to link with | "-l" |
|
||||
| SHLIBPREFIX | String | Shared library file name prefix | Windows: "", POSIX: "lib" |
|
||||
| SHLIBSUFFIX | String | Shared library file name suffix | Windows: ".dll", POSIX: ".so" |
|
||||
| SIZE | String | Size executable. | "size" |
|
||||
| YACC | String | Yacc executable | "bison" |
|
||||
| YACC_CMD | Array | Yacc command line | ["${YACC}", "${YACC_FLAGS}", "-o", "${_TARGET}", "${_SOURCES}"] |
|
||||
| YACC_FLAGS | Array | Yacc flags | ["-d"] |
|
||||
| YACCSUFFIX | Array | Yacc input file suffixes | [".y", ".yy"] |
|
||||
|
||||
### Build Hooks
|
||||
|
||||
Environments can have build hooks which are added with `env.add_build_hook()`.
|
||||
Build hooks are invoked immediately before a builder executes.
|
||||
Build hooks can modify the construction variables in use for the build
|
||||
operation.
|
||||
They can also register new build targets.
|
||||
|
||||
Environments can also have post-build hooks added with `env.add_post_build_hook()`.
|
||||
Post-build hooks are only invoked if the build operation was a success.
|
||||
Post-build hooks can invoke commands using the newly-built files, or register
|
||||
new build targets.
|
||||
|
||||
Each build hook block will be invoked for every build operation, so the block
|
||||
should test the target or sources if its action should only apply to some
|
||||
subset of build targets or source files.
|
||||
|
||||
Example build hook:
|
||||
|
||||
```ruby
|
||||
Rscons::Environment.new do |env|
|
||||
# Build third party sources without -Wall
|
||||
env.add_build_hook do |build_op|
|
||||
if build_op[:builder].name == "Object" and
|
||||
build_op[:sources].first =~ %r{src/third-party}
|
||||
build_op[:vars]["CFLAGS"] -= ["-Wall"]
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The `build_op` parameter to the build hook block is a Hash describing the
|
||||
build operation with the following keys:
|
||||
* `:builder` - `Builder` instance in use
|
||||
* `:env` - `Environment` calling the build hook; note that this may be
|
||||
different from the Environment that the build hook was added to in the case
|
||||
that the original Environment was cloned with build hooks!
|
||||
* `:target` - `String` name of the target file
|
||||
* `:sources` - `Array` of the source files
|
||||
* `:vars` - `Rscons::VarSet` containing the construction variables to use.
|
||||
The build hook can overwrite entries in `build_op[:vars]` to alter the
|
||||
construction variables in use for this specific build operation.
|
||||
|
||||
### Phony Targets
|
||||
|
||||
A build target name given as a Symbol instead of a String is interpreted as a
|
||||
"phony" target.
|
||||
Phony targets operate similarly to normal build targets, except that a file is
|
||||
not expected to be produced by the builder.
|
||||
Phony targets will still be "rebuilt" if any source or the command is out of
|
||||
date.
|
||||
|
||||
### Explicit Dependencies
|
||||
|
||||
A target can be marked as depending on another file that Rscons would not
|
||||
otherwise know about via the `Environment#depends` function. For example,
|
||||
to force the linker to re-link a Program output when a linker script changes:
|
||||
|
||||
```ruby
|
||||
Rscons::Environment.new do |env|
|
||||
env.Program("a.out", "foo.c", "LDFLAGS" => %w[-T linker_script.ld])
|
||||
env.depends("a.out", "linker_script.ld")
|
||||
end
|
||||
```
|
||||
|
||||
You can pass multiple dependency files to `Environment#depends`:
|
||||
|
||||
```ruby
|
||||
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
|
||||
|
||||
* uppercase strings - the default construction variables that Rscons uses
|
||||
* strings beginning with "_" - set and used internally by builders
|
||||
* symbols, lowercase strings - reserved as user-defined construction variables
|
||||
|
||||
### API documentation
|
||||
|
||||
Documentation for the complete Rscons API can be found at
|
||||
http://www.rubydoc.info/github/holtrop/rscons/master.
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork it
|
||||
2. Create your feature branch (`git checkout -b my-new-feature`)
|
||||
3. Commit your changes (`git commit -am 'Add some feature'`)
|
||||
4. Push to the branch (`git push origin my-new-feature`)
|
||||
5. Create new Pull Request
|
||||
See [https://holtrop.github.io/rscons/index.html](https://holtrop.github.io/rscons/index.html) for User Guide and Installation instructions.
|
||||
+60
-2
@@ -10,8 +10,8 @@ require "yard"
|
||||
require "rake/clean"
|
||||
require "fileutils"
|
||||
|
||||
CLEAN.include %w[build_test_run .yardoc doc coverage test]
|
||||
CLOBBER.include %w[dist pkg]
|
||||
CLEAN.include %w[build_test_run .yardoc yard coverage test]
|
||||
CLOBBER.include %w[dist gen large_project pkg]
|
||||
|
||||
task :build_dist do
|
||||
sh "ruby rb/build_dist.rb"
|
||||
@@ -39,6 +39,64 @@ end
|
||||
|
||||
YARD::Rake::YardocTask.new do |yard|
|
||||
yard.files = ['lib/**/*.rb']
|
||||
yard.options = ["-ogen/yard"]
|
||||
end
|
||||
|
||||
task :gen_large_project, [:size] => :build_dist do |task, args|
|
||||
size = (args.size || 10000).to_i
|
||||
FileUtils.rm_rf("large_project")
|
||||
FileUtils.mkdir_p("large_project/src")
|
||||
size.times do |i|
|
||||
File.open("large_project/src/fn#{i}.c", "w") do |fh|
|
||||
fh.puts(<<-EOF)
|
||||
int fn#{i}(void)
|
||||
{
|
||||
return #{i};
|
||||
}
|
||||
EOF
|
||||
end
|
||||
File.open("large_project/src/fn#{i}.h", "w") do |fh|
|
||||
fh.puts %[int fn#{i}(void);]
|
||||
end
|
||||
end
|
||||
File.open("large_project/src/main.c", "w") do |fh|
|
||||
size.times do |i|
|
||||
fh.puts %[#include "fn#{i}.h"]
|
||||
end
|
||||
fh.puts <<-EOF
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
int result = 0;
|
||||
EOF
|
||||
size.times do |i|
|
||||
fh.puts %[result += fn#{i}();]
|
||||
end
|
||||
fh.puts <<-EOF
|
||||
return result;
|
||||
}
|
||||
EOF
|
||||
end
|
||||
File.open("large_project/Rsconscript", "w") do |fh|
|
||||
fh.puts <<EOF
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env.Program("project", glob("src/*.c"))
|
||||
end
|
||||
end
|
||||
EOF
|
||||
end
|
||||
FileUtils.cp("dist/rscons", "large_project")
|
||||
end
|
||||
|
||||
task :user_guide do
|
||||
system("ruby", "-Ilib", "rb/gen_user_guide.rb")
|
||||
end
|
||||
|
||||
task :default => :spec
|
||||
|
||||
task :all => [
|
||||
:build_dist,
|
||||
:spec,
|
||||
:yard,
|
||||
:user_guide,
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
configure do
|
||||
check_c_header "string.h", check_cpppath: ["./usr1"]
|
||||
check_c_header "frobulous.h", check_cpppath: ["./usr2"]
|
||||
end
|
||||
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env.Object("test.o", "test.c")
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
configure do
|
||||
check_c_header "not___found.h", fail: false, set_define: "HAVE_NOT___FOUND_H"
|
||||
check_c_header "not___found.h", set_define: "HAVE_NOT___FOUND_H"
|
||||
end
|
||||
|
||||
build do
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
configure do
|
||||
check_cxx_header "string.h", check_cpppath: ["./usr1"]
|
||||
check_cxx_header "frobulous.h", check_cpppath: ["./usr2"]
|
||||
end
|
||||
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env.Object("test.o", "test.cc")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
configure do
|
||||
check_d_import "std.stdio", check_d_import_path: ["./usr1"]
|
||||
check_d_import "frobulous", check_d_import_path: ["./usr2"]
|
||||
end
|
||||
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env.Object("test.o", "test.d")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
build do
|
||||
Environment.new(echo: :command) do |env|
|
||||
env.Library("usr2/libfrobulous.a", "two.c")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
configure do
|
||||
check_lib "m", check_libpath: ["./usr1"]
|
||||
check_lib "frobulous", check_libpath: ["./usr2"]
|
||||
end
|
||||
|
||||
build do
|
||||
Environment.new(echo: :command) do |env|
|
||||
env.Program("simple.exe", "simple.c")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,30 @@
|
||||
configure do
|
||||
custom_check("Checking 'grep' version") do |op|
|
||||
stdout, stderr, status = op.log_and_test_command(%w[grep --version])
|
||||
should_fail = true
|
||||
if status != 0
|
||||
fail_message = "error executing grep"
|
||||
elsif stdout =~ /^grep \(GNU grep\) 1\./
|
||||
fail_message = "too old!"
|
||||
status = 1
|
||||
elsif stdout =~ /^grep \(GNU grep\) 2\./
|
||||
fail_message = "we'll work with it but you should upgrade"
|
||||
status = 1
|
||||
should_fail = false
|
||||
op.store_merge("CPPDEFINES" => "GREP_WORKAROUND")
|
||||
else
|
||||
op.store_append("CPPDEFINES" => "GREP_FULL")
|
||||
end
|
||||
op.complete(status, success_message: "good!", fail_message: fail_message, fail: should_fail)
|
||||
end
|
||||
custom_check("Checking sed -E flag") do |op|
|
||||
stdout, stderr, status = op.log_and_test_command(%w[sed -E -e s/ab+/rep/], stdin: "abbbc")
|
||||
op.complete(stdout =~ /repc/ ? 0 : 1, success_message: "good", fail_message: "fail")
|
||||
end
|
||||
end
|
||||
|
||||
build do
|
||||
Environment.new do |env|
|
||||
puts env["CPPDEFINES"]
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,4 @@
|
||||
int two(void)
|
||||
{
|
||||
return 42;
|
||||
}
|
||||
@@ -6,7 +6,8 @@ build do
|
||||
"CMD_DESC" => "Generating")
|
||||
env["build_root"] = env.build_root
|
||||
env["inc_c"] = "inc.c"
|
||||
env.build_after("${build_root}/program.o", "${inc_c}")
|
||||
env.Program("program.exe", ["program.c", "inc.c"])
|
||||
env.Object("program.o", "program.c")
|
||||
env.build_after("program.o", "${inc_c}")
|
||||
env.Program("program.exe", ["program.o", "inc.c"])
|
||||
end
|
||||
end
|
||||
@@ -4,13 +4,14 @@ build do
|
||||
env["inc_h"] = "inc.h"
|
||||
|
||||
env.Copy("copy_inc.h", "${inc_h}")
|
||||
env.depends("${build_root}/program.o", "${inc_h}")
|
||||
env.Program("program.exe", ["program.c", "inc.c"])
|
||||
env.depends("program.o", "${inc_h}")
|
||||
env.Object("program.o", "program.c")
|
||||
env.Program("program.exe", ["program.o", "inc.c"])
|
||||
|
||||
env.Command("inc.c",
|
||||
[],
|
||||
"CMD" => %w[ruby gen.rb ${_TARGET}],
|
||||
"CMD_DESC" => "Generating")
|
||||
env.produces("inc.c", "inc.h")
|
||||
inc_c = env.Command("inc.c",
|
||||
[],
|
||||
"CMD" => %w[ruby gen.rb ${_TARGET}],
|
||||
"CMD_DESC" => "Generating")
|
||||
inc_c.produces("inc.h")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,17 @@
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env["build_root"] = env.build_root
|
||||
env["inc_h"] = "inc.h"
|
||||
|
||||
env.Copy("copy_inc.h", "${inc_h}")
|
||||
env.depends("program.o", "${inc_h}")
|
||||
env.Object("program.o", "program.c")
|
||||
env.Program("program.exe", ["program.o", "inc.c"])
|
||||
|
||||
env.Command("inc.c",
|
||||
[],
|
||||
"CMD" => %w[ruby gen.rb ${_TARGET}],
|
||||
"CMD_DESC" => "Generating")
|
||||
env.produces("inc.c", "inc.h")
|
||||
end
|
||||
end
|
||||
@@ -1,3 +1,7 @@
|
||||
configure do
|
||||
check_d_compiler "gdc"
|
||||
end
|
||||
|
||||
build do
|
||||
Environment.new(echo: :command) do |env|
|
||||
env.Program("hello-d.exe", glob("*.d"))
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
configure do
|
||||
check_d_compiler "ldc2"
|
||||
end
|
||||
|
||||
build do
|
||||
Environment.new(echo: :command) do |env|
|
||||
env.Program("hello-d.exe", glob("*.d"))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env.Program("program.exe", "program.c")
|
||||
env.Install("install/program.exe", "program.exe")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
sleep(2);
|
||||
return 0;
|
||||
}
|
||||
@@ -4,6 +4,7 @@ build do
|
||||
require 'yaml'
|
||||
env.add_builder(:JsonToYaml) do |params|
|
||||
unless @cache.up_to_date?(@target, :JsonToYaml, @sources, @env)
|
||||
print_run_message("JsonToYaml #{@target}", nil)
|
||||
@cache.mkdir_p(File.dirname(@target))
|
||||
File.open(@target, 'w') do |f|
|
||||
f.write(YAML.dump(JSON.load(IO.read(@sources.first))))
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env.Program("foo.exe", glob("*.cc", "*.c"))
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
|
||||
void foo(void)
|
||||
{
|
||||
printf("foo\n");
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
#include <iostream>
|
||||
|
||||
extern "C" {
|
||||
void foo(void);
|
||||
}
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
foo();
|
||||
std::cout << "main" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env["LIBS"] += ["c"]
|
||||
env["LIBS"] += ["m"]
|
||||
env.Program('simple.exe', Dir['*.c'])
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,5 @@
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env.produces("foo", "bar")
|
||||
end
|
||||
end
|
||||
@@ -2,9 +2,9 @@ build do
|
||||
Environment.new(echo: :command) do |env|
|
||||
env.append('CPPPATH' => glob('src/**/*/'))
|
||||
env.add_build_hook do |builder|
|
||||
if File.basename(builder.target) == "one.o"
|
||||
if File.basename(builder.sources.first) == "one.c"
|
||||
builder.vars["CFLAGS"] << "-O1"
|
||||
elsif File.basename(builder.target) == "two.o"
|
||||
elsif File.basename(builder.sources.first) == "two.c"
|
||||
builder.vars["CFLAGS"] << "-O2"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
project_name "install_test"
|
||||
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env.Install("inst.exe", "install.rb")
|
||||
env["CPPPATH"] += glob("src/**")
|
||||
env.Program("^/program.exe", glob("src/**/*.c"))
|
||||
env.InstallDirectory("${prefix}/bin")
|
||||
env.Install("${prefix}/bin", "^/program.exe")
|
||||
env.InstallDirectory("${prefix}/share")
|
||||
env.Install("${prefix}/share/proj/install.rb", "install.rb")
|
||||
env.Install("${prefix}/mult", ["install.rb", "copy.rb"])
|
||||
env.Install("${prefix}/src", "src")
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,12 @@
|
||||
build do
|
||||
Environment.new do |env|
|
||||
env["CPPPATH"] += glob("src/**")
|
||||
env.Program("^/prog.exe", glob("src/**/*.c"))
|
||||
end
|
||||
|
||||
Environment.new do |env|
|
||||
env["CPPPATH"] += glob("src/**")
|
||||
env["CCFLAGS"] += %w[-Wall -g]
|
||||
env.Program("^/prog-debug.exe", glob("src/**/*.c"))
|
||||
end
|
||||
end
|
||||
+1327
File diff suppressed because it is too large.
Load diff
Binary file not shown.
|
After Width: | Height: | Size: 242 KiB |
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 67 KiB |
+2
-11
@@ -26,6 +26,7 @@ module Rscons
|
||||
:Directory,
|
||||
:Disassemble,
|
||||
:Install,
|
||||
:InstallDirectory,
|
||||
:Library,
|
||||
:Object,
|
||||
:Preprocess,
|
||||
@@ -63,16 +64,6 @@ module Rscons
|
||||
target.is_a?(Symbol)
|
||||
end
|
||||
|
||||
# Return a new path by changing the suffix in path to suffix.
|
||||
#
|
||||
# @param path [String] The path to alter.
|
||||
# @param suffix [String] The new filename suffix, e.g. ".exe".
|
||||
#
|
||||
# @return [String] New path.
|
||||
def set_suffix(path, suffix)
|
||||
path.sub(/\.[^.]*$/, "") + suffix
|
||||
end
|
||||
|
||||
# Return the system shell and arguments for executing a shell command.
|
||||
#
|
||||
# @return [Array<String>] The shell and flag.
|
||||
@@ -145,9 +136,9 @@ require_relative "rscons/builders/mixins/program"
|
||||
# default builders
|
||||
require_relative "rscons/builders/cfile"
|
||||
require_relative "rscons/builders/command"
|
||||
require_relative "rscons/builders/copy"
|
||||
require_relative "rscons/builders/directory"
|
||||
require_relative "rscons/builders/disassemble"
|
||||
require_relative "rscons/builders/install"
|
||||
require_relative "rscons/builders/library"
|
||||
require_relative "rscons/builders/object"
|
||||
require_relative "rscons/builders/preprocess"
|
||||
|
||||
@@ -3,6 +3,7 @@ module Rscons
|
||||
module Ansi
|
||||
class << self
|
||||
|
||||
# ANSI terminal reset sequence.
|
||||
RESET = "\e[0m"
|
||||
|
||||
# Write a message to an IO with ANSI escape codes.
|
||||
|
||||
+114
-45
@@ -1,3 +1,5 @@
|
||||
require "set"
|
||||
|
||||
module Rscons
|
||||
|
||||
# Functionality for an instance of the rscons application invocation.
|
||||
@@ -23,6 +25,19 @@ module Rscons
|
||||
def initialize
|
||||
@n_threads = Util.determine_n_threads
|
||||
@vars = VarSet.new
|
||||
@operations = Set.new
|
||||
@build_step = 0
|
||||
end
|
||||
|
||||
# Check whether a requested operation is active.
|
||||
#
|
||||
# @param op [String]
|
||||
# Operation name.
|
||||
#
|
||||
# @return [Boolean]
|
||||
# Whether the requested operation is active.
|
||||
def operation(op)
|
||||
@operations.include?(op)
|
||||
end
|
||||
|
||||
# Run the specified operation.
|
||||
@@ -33,34 +48,74 @@ module Rscons
|
||||
# The script.
|
||||
# @param operation_options [Hash]
|
||||
# Option values from the CLI for the operation.
|
||||
# @param options [Hash]
|
||||
# Optional parameters.
|
||||
# @option sub_op [Boolean]
|
||||
# Whether this operation is not the top-level operation.
|
||||
#
|
||||
# @return [Integer]
|
||||
# Process exit code (0 on success).
|
||||
def run(operation, script, operation_options)
|
||||
def run(operation, script, operation_options, options = {})
|
||||
@start_time = Time.new
|
||||
@script = script
|
||||
case operation
|
||||
when "build"
|
||||
unless Cache.instance["configuration_data"]["configured"]
|
||||
if @script.autoconf
|
||||
rv = run("configure", script, operation_options)
|
||||
if rv != 0
|
||||
return rv
|
||||
end
|
||||
else
|
||||
$stderr.puts "Project must be configured first, and autoconf is disabled"
|
||||
return 1
|
||||
@operations << operation
|
||||
puts "Starting '#{operation}' at #{Time.new}" if verbose
|
||||
rv =
|
||||
case operation
|
||||
when "build"
|
||||
rv = 0
|
||||
unless Cache.instance["configuration_data"]["configured"]
|
||||
rv =
|
||||
if @script.autoconf
|
||||
run("configure", script, operation_options, sub_op: false)
|
||||
else
|
||||
$stderr.puts "Project must be configured first, and autoconf is disabled"
|
||||
1
|
||||
end
|
||||
end
|
||||
if rv == 0
|
||||
build(operation_options)
|
||||
else
|
||||
rv
|
||||
end
|
||||
when "clean"
|
||||
clean
|
||||
when "configure"
|
||||
configure(operation_options)
|
||||
when "distclean"
|
||||
distclean
|
||||
when "install"
|
||||
run("build", script, operation_options, sub_op: false)
|
||||
when "uninstall"
|
||||
uninstall
|
||||
else
|
||||
$stderr.puts "Unknown operation: #{operation}"
|
||||
1
|
||||
end
|
||||
build(operation_options)
|
||||
when "clean"
|
||||
clean
|
||||
when "configure"
|
||||
configure(operation_options)
|
||||
when "distclean"
|
||||
distclean
|
||||
else
|
||||
$stderr.puts "Unknown operation: #{operation}"
|
||||
1
|
||||
if verbose and options[:sub_op].nil?
|
||||
time = Time.new
|
||||
elapsed = time - @start_time
|
||||
puts "'#{operation}' complete at #{time} (#{Util.format_elapsed_time(elapsed)})"
|
||||
end
|
||||
rv
|
||||
end
|
||||
|
||||
# Get the next build step number.
|
||||
#
|
||||
# This is used internally by the {Environment} class.
|
||||
#
|
||||
# @api private
|
||||
def get_next_build_step
|
||||
@build_step += 1
|
||||
end
|
||||
|
||||
# Get the total number of build steps.
|
||||
#
|
||||
# @return [Integer]
|
||||
# The total number of build steps.
|
||||
def get_total_build_steps
|
||||
Environment.environments.reduce(@build_step) do |result, env|
|
||||
result + env.build_steps_remaining
|
||||
end
|
||||
end
|
||||
|
||||
@@ -94,17 +149,19 @@ module Rscons
|
||||
def clean
|
||||
cache = Cache.instance
|
||||
# remove all built files
|
||||
cache.targets.each do |target|
|
||||
cache.targets(false).each do |target|
|
||||
cache.remove_target(target)
|
||||
FileUtils.rm_f(target)
|
||||
end
|
||||
# remove all created directories if they are empty
|
||||
cache.directories.sort {|a, b| b.size <=> a.size}.each do |directory|
|
||||
cache.directories(false).sort {|a, b| b.size <=> a.size}.each do |directory|
|
||||
cache.remove_directory(directory)
|
||||
next unless File.directory?(directory)
|
||||
if (Dir.entries(directory) - ['.', '..']).empty?
|
||||
Dir.rmdir(directory) rescue nil
|
||||
end
|
||||
end
|
||||
cache.clear
|
||||
cache.write
|
||||
0
|
||||
end
|
||||
|
||||
@@ -116,7 +173,9 @@ module Rscons
|
||||
cache = Cache.instance
|
||||
build_dir = cache["configuration_data"]["build_dir"]
|
||||
clean
|
||||
FileUtils.rm_rf(build_dir)
|
||||
if build_dir
|
||||
FileUtils.rm_rf(build_dir)
|
||||
end
|
||||
cache.clear
|
||||
0
|
||||
end
|
||||
@@ -129,34 +188,44 @@ module Rscons
|
||||
# @return [Integer]
|
||||
# Exit code.
|
||||
def configure(options)
|
||||
# Default options.
|
||||
options[:build_dir] ||= "build"
|
||||
options[:prefix] ||= "/usr/local"
|
||||
cache = Cache.instance
|
||||
cache["failed_commands"] = []
|
||||
cache["configuration_data"] = {}
|
||||
if project_name = @script.project_name
|
||||
Ansi.write($stdout, "Configuring ", :cyan, project_name, :reset, "...\n")
|
||||
else
|
||||
$stdout.puts "Configuring project..."
|
||||
end
|
||||
Ansi.write($stdout, "Setting build directory... ", :green, options[:build_dir], :reset, "\n")
|
||||
Ansi.write($stdout, "Setting prefix... ", :green, options[:prefix], :reset, "\n")
|
||||
rv = 0
|
||||
co = ConfigureOp.new("#{options[:build_dir]}/configure")
|
||||
options = options.merge(project_name: @script.project_name)
|
||||
co = ConfigureOp.new(options)
|
||||
begin
|
||||
@script.configure(co)
|
||||
rescue ConfigureOp::ConfigureFailure
|
||||
Ansi.write($stderr, :red, "Configuration failed", :reset, "\n")
|
||||
rv = 1
|
||||
end
|
||||
co.close
|
||||
cache["configuration_data"]["build_dir"] = options[:build_dir]
|
||||
cache["configuration_data"]["prefix"] = options[:prefix]
|
||||
cache["configuration_data"]["configured"] = rv == 0
|
||||
cache.write
|
||||
co.close(rv == 0)
|
||||
rv
|
||||
end
|
||||
|
||||
# Remove installed files.
|
||||
#
|
||||
# @return [Integer]
|
||||
# Exit code.
|
||||
def uninstall
|
||||
cache = Cache.instance
|
||||
cache.targets(true).each do |target|
|
||||
cache.remove_target(target)
|
||||
next unless File.exists?(target)
|
||||
puts "Removing #{target}" if verbose
|
||||
FileUtils.rm_f(target)
|
||||
end
|
||||
# remove all created directories if they are empty
|
||||
cache.directories(true).sort {|a, b| b.size <=> a.size}.each do |directory|
|
||||
cache.remove_directory(directory)
|
||||
next unless File.directory?(directory)
|
||||
if (Dir.entries(directory) - ['.', '..']).empty?
|
||||
puts "Removing #{directory}" if verbose
|
||||
Dir.rmdir(directory) rescue nil
|
||||
end
|
||||
end
|
||||
cache.write
|
||||
0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@@ -4,27 +4,52 @@ module Rscons
|
||||
|
||||
# Create a BasicEnvironment object.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Construction options.
|
||||
# @option options [String, Array<String>] :use
|
||||
# Use flag(s). If specified, any configuration flags which were saved
|
||||
# with a corresponding `:use` value will be applied to this Environment.
|
||||
def initialize(options = {})
|
||||
@varset = VarSet.new(Rscons::DEFAULT_CONSTRUCTION_VARIABLES)
|
||||
load_configuration_data!(options)
|
||||
end
|
||||
|
||||
# Get a construction variable's value.
|
||||
# Access the value of a construction variable.
|
||||
#
|
||||
# @see VarSet#[]
|
||||
def [](*args)
|
||||
@varset.__send__(:[], *args)
|
||||
# @param key [String, Symbol]
|
||||
# The construction variable name.
|
||||
#
|
||||
# @return [Object]
|
||||
# The construction variable's value.
|
||||
def [](key)
|
||||
@varset[key]
|
||||
end
|
||||
|
||||
# Set a construction variable's value.
|
||||
# Access the value of a construction variable.
|
||||
#
|
||||
# @see VarSet#[]=
|
||||
def []=(*args)
|
||||
@varset.__send__(:[]=, *args)
|
||||
# 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
|
||||
|
||||
# Assign a value to a construction variable.
|
||||
#
|
||||
# @param key [String, Symbol]
|
||||
# The construction variable name.
|
||||
#
|
||||
# @param val [Object]
|
||||
# The value to set.
|
||||
def []=(key, val)
|
||||
@varset[key] = val
|
||||
end
|
||||
|
||||
# Add a set of construction variables to the BasicEnvironment.
|
||||
@@ -108,7 +133,7 @@ module Rscons
|
||||
append["LDFLAGS", ["-arch", val]]
|
||||
end
|
||||
skip = true
|
||||
elsif word =~ /^#{self["CPPDEFPREFIX"]}(.*)$/
|
||||
elsif word =~ /^#{get_var("CPPDEFPREFIX")}(.*)$/
|
||||
handle["CPPDEFINES", $1]
|
||||
elsif word == "-include"
|
||||
if val = words[i + 1]
|
||||
@@ -121,11 +146,11 @@ module Rscons
|
||||
append["LDFLAGS", ["-isysroot", val]]
|
||||
end
|
||||
skip = true
|
||||
elsif word =~ /^#{self["INCPREFIX"]}(.*)$/
|
||||
elsif word =~ /^#{get_var("INCPREFIX")}(.*)$/
|
||||
handle["CPPPATH", $1]
|
||||
elsif word =~ /^#{self["LIBLINKPREFIX"]}(.*)$/
|
||||
elsif word =~ /^#{get_var("LIBLINKPREFIX")}(.*)$/
|
||||
handle["LIBS", $1]
|
||||
elsif word =~ /^#{self["LIBDIRPREFIX"]}(.*)$/
|
||||
elsif word =~ /^#{get_var("LIBDIRPREFIX")}(.*)$/
|
||||
handle["LIBPATH", $1]
|
||||
elsif word == "-mno-cygwin"
|
||||
append["CCFLAGS", [word]]
|
||||
@@ -174,7 +199,7 @@ module Rscons
|
||||
# @return [void]
|
||||
def merge_flags(flags)
|
||||
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
|
||||
else
|
||||
self[key] = val
|
||||
@@ -223,5 +248,28 @@ module Rscons
|
||||
end
|
||||
end
|
||||
|
||||
# Execute a command using the system shell.
|
||||
#
|
||||
# The shell is automatically determined but can be overridden by the SHELL
|
||||
# construction variable. If the SHELL construction variable is specified,
|
||||
# the flag to pass to the shell is automatically dtermined but can be
|
||||
# overridden by the SHELLFLAG construction variable.
|
||||
#
|
||||
# @param command [String] Command to execute.
|
||||
#
|
||||
# @return [String] The command's standard output.
|
||||
def shell(command)
|
||||
shell_cmd =
|
||||
if shell = get_var("SHELL")
|
||||
flag = get_var("SHELLFLAG") || (shell == "cmd" ? "/c" : "-c")
|
||||
[shell, flag]
|
||||
else
|
||||
Rscons.get_system_shell
|
||||
end
|
||||
IO.popen([*shell_cmd, command]) do |io|
|
||||
io.read
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
+29
-2
@@ -47,6 +47,14 @@ module Rscons
|
||||
# Construction variables used to perform the build operation.
|
||||
attr_accessor :vars
|
||||
|
||||
# @return [Set<String>]
|
||||
# Side effect file(s) produced when this builder runs.
|
||||
attr_accessor :side_effects
|
||||
|
||||
# @return [Integer]
|
||||
# Build step.
|
||||
attr_accessor :build_step
|
||||
|
||||
# Create an instance of the Builder to build a target.
|
||||
#
|
||||
# @param options [Hash]
|
||||
@@ -67,6 +75,7 @@ module Rscons
|
||||
@cache = options[:cache]
|
||||
@env = options[:env]
|
||||
@vars = options[:vars]
|
||||
@side_effects = Set.new
|
||||
end
|
||||
|
||||
# Return the name of the builder.
|
||||
@@ -78,6 +87,14 @@ module Rscons
|
||||
self.class.name
|
||||
end
|
||||
|
||||
# Return whether the builder is a no-op.
|
||||
#
|
||||
# @return [Boolean]
|
||||
# Whether the builder is a no-op.
|
||||
def nop?
|
||||
false
|
||||
end
|
||||
|
||||
# Manually record a given build target as depending on the specified files.
|
||||
#
|
||||
# @param user_deps [Array<String>]
|
||||
@@ -88,6 +105,18 @@ module Rscons
|
||||
@env.depends(@target, *user_deps)
|
||||
end
|
||||
|
||||
# Manually record the given side effect file(s) as being produced when the
|
||||
# named target is produced.
|
||||
#
|
||||
# @return [void]
|
||||
def produces(*side_effects)
|
||||
side_effects.each do |side_effect|
|
||||
side_effect_expanded = @env.expand_path(@env.expand_varref(side_effect))
|
||||
@env.register_side_effect(side_effect_expanded)
|
||||
@side_effects << side_effect_expanded
|
||||
end
|
||||
end
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
#
|
||||
# @param options [Hash]
|
||||
@@ -172,8 +201,6 @@ module Rscons
|
||||
|
||||
# Register build results from a {Command} with the cache.
|
||||
#
|
||||
# @since 1.10.0
|
||||
#
|
||||
# @param options [Hash]
|
||||
# Builder finalize options.
|
||||
# @option options [String] :stdout
|
||||
|
||||
+29
-17
@@ -8,10 +8,10 @@ module Rscons
|
||||
# Hash mapping targets to a set of build dependencies. A builder will not
|
||||
# be returned as ready to run if any of its dependencies are still
|
||||
# building.
|
||||
# @param side_effects [Hash]
|
||||
# Hash mapping targets to a set of side-effect files. A builder 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.
|
||||
# @param side_effects [Set]
|
||||
# Set of side-effect files. A builder 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)
|
||||
super()
|
||||
@build_dependencies = build_dependencies
|
||||
@@ -31,6 +31,16 @@ module Rscons
|
||||
self[builder.target] << builder
|
||||
end
|
||||
|
||||
# Return the number of remaining build steps.
|
||||
#
|
||||
# @return [Integer]
|
||||
# The number of remaining build steps.
|
||||
def build_steps_remaining
|
||||
self.reduce(0) do |result, (target, builders)|
|
||||
result + builders.select {|b| not b.nop?}.size
|
||||
end
|
||||
end
|
||||
|
||||
# Get the next builder that is ready to run from the BuilderSet.
|
||||
#
|
||||
# This method will remove the builder from the BuilderSet.
|
||||
@@ -43,22 +53,24 @@ module Rscons
|
||||
# @return [nil, Builder]
|
||||
# The next builder to run.
|
||||
def get_next_builder_to_run(targets_still_building)
|
||||
not_built_yet = targets_still_building + self.keys
|
||||
not_built_yet += not_built_yet.reduce([]) do |result, target|
|
||||
result + (@side_effects[target] || [])
|
||||
to_build = self.find do |target, builders|
|
||||
deps = builders.first.sources + (@build_dependencies[target] || []).to_a
|
||||
# All dependencies must have been built for this target to be ready to
|
||||
# build.
|
||||
deps.all? do |dep|
|
||||
!(targets_still_building.include?(dep) ||
|
||||
self.include?(dep) ||
|
||||
@side_effects.include?(dep))
|
||||
end
|
||||
end
|
||||
|
||||
target_to_build = self.keys.find do |target|
|
||||
deps = self[target][0].sources + (@build_dependencies[target] || []).to_a
|
||||
!deps.find {|dep| not_built_yet.include?(dep)}
|
||||
end
|
||||
|
||||
if target_to_build
|
||||
builder = self[target_to_build][0]
|
||||
if self[target_to_build].size > 1
|
||||
self[target_to_build].slice!(0)
|
||||
if to_build
|
||||
target, builders = *to_build
|
||||
builder = builders.first
|
||||
if builders.size > 1
|
||||
builders.slice!(0)
|
||||
else
|
||||
self.delete(target_to_build)
|
||||
self.delete(target)
|
||||
end
|
||||
return builder
|
||||
end
|
||||
|
||||
@@ -26,7 +26,7 @@ module Rscons
|
||||
raise "Unknown source file #{@sources.first.inspect} for CFile builder"
|
||||
end
|
||||
command = @env.build_command("${#{cmd}_CMD}", @vars)
|
||||
standard_command("#{message} from #{Util.short_format_paths(@sources)} => #{@target}", command)
|
||||
standard_command("#{message} from <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -3,8 +3,6 @@ module Rscons
|
||||
# A builder to execute an arbitrary command that will produce the given
|
||||
# target based on the given sources.
|
||||
#
|
||||
# @since 1.8.0
|
||||
#
|
||||
# Example:
|
||||
# env.Command("docs.html", "docs.md",
|
||||
# CMD => %w[pandoc -fmarkdown -thtml -o${_TARGET} ${_SOURCES}])
|
||||
@@ -23,7 +21,7 @@ module Rscons
|
||||
if @vars["CMD_STDOUT"]
|
||||
options[:stdout] = @env.expand_varref("${CMD_STDOUT}", @vars)
|
||||
end
|
||||
standard_command("#{cmd_desc} => #{@target}", command, options)
|
||||
standard_command("#{cmd_desc} <target>#{@target}<reset>", command, options)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
require "pathname"
|
||||
|
||||
module Rscons
|
||||
module Builders
|
||||
# The Copy builder copies files/directories to new locations.
|
||||
class Copy < Builder
|
||||
|
||||
# Construct builder.
|
||||
def initialize(*args)
|
||||
super
|
||||
@install_builder = self.class.name == "Install"
|
||||
@nop = @install_builder && !Rscons.application.operation("install")
|
||||
end
|
||||
|
||||
# Return whether the builder is a no-op.
|
||||
#
|
||||
# @return [Boolean]
|
||||
# Whether the builder is a no-op.
|
||||
def nop?
|
||||
@nop
|
||||
end
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
def run(options)
|
||||
if @nop
|
||||
true
|
||||
else
|
||||
target_is_dir = (@sources.length > 1) ||
|
||||
Dir.exists?(@sources.first) ||
|
||||
Dir.exists?(@target)
|
||||
outdir = target_is_dir ? @target : File.dirname(@target)
|
||||
# Collect the list of files to copy over.
|
||||
file_map = {}
|
||||
if target_is_dir
|
||||
@sources.each do |src|
|
||||
if Dir.exists? src
|
||||
Dir.glob("#{src}/**/*", File::FNM_DOTMATCH).select do |f|
|
||||
File.file?(f)
|
||||
end.each do |subfile|
|
||||
subpath = Pathname.new(subfile).relative_path_from(Pathname.new(src)).to_s
|
||||
file_map[subfile] = "#{outdir}/#{subpath}"
|
||||
end
|
||||
else
|
||||
file_map[src] = "#{outdir}/#{File.basename(src)}"
|
||||
end
|
||||
end
|
||||
else
|
||||
file_map[sources.first] = target
|
||||
end
|
||||
printed_message = false
|
||||
file_map.each do |src, dest|
|
||||
# Check the cache and copy if necessary
|
||||
unless @cache.up_to_date?(dest, :Copy, [src], @env)
|
||||
unless printed_message
|
||||
message = "#{name} <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>"
|
||||
print_run_message(message, nil)
|
||||
printed_message = true
|
||||
end
|
||||
@cache.mkdir_p(File.dirname(dest), install: @install_builder)
|
||||
FileUtils.rm_f(dest)
|
||||
FileUtils.cp(src, dest, :preserve => true)
|
||||
end
|
||||
@cache.register_build(dest, :Copy, [src], @env, install: @install_builder)
|
||||
end
|
||||
(target_is_dir ? Dir.exists?(@target) : File.exists?(@target)) ? true : false
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# The Install builder is identical to the Copy builder.
|
||||
class Install < Copy; end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -1,22 +1,47 @@
|
||||
module Rscons
|
||||
module Builders
|
||||
|
||||
# The Directory builder creates a directory.
|
||||
class Directory < Builder
|
||||
|
||||
# Construct builder.
|
||||
def initialize(*args)
|
||||
super
|
||||
@install_builder = self.class.name == "InstallDirectory"
|
||||
@nop = @install_builder && !Rscons.application.operation("install")
|
||||
end
|
||||
|
||||
# Return whether the builder is a no-op.
|
||||
#
|
||||
# @return [Boolean]
|
||||
# Whether the builder is a no-op.
|
||||
def nop?
|
||||
@nop
|
||||
end
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
def run(options)
|
||||
if File.directory?(@target)
|
||||
if @nop
|
||||
true
|
||||
elsif File.exists?(@target)
|
||||
Ansi.write($stderr, :red, "Error: `#{@target}' already exists and is not a directory", :reset, "\n")
|
||||
false
|
||||
else
|
||||
print_run_message("Creating directory => #{@target}", nil)
|
||||
@cache.mkdir_p(@target)
|
||||
true
|
||||
if File.directory?(@target)
|
||||
true
|
||||
elsif File.exists?(@target)
|
||||
Ansi.write($stderr, :red, "Error: `#{@target}' already exists and is not a directory", :reset, "\n")
|
||||
false
|
||||
else
|
||||
print_run_message("Creating directory <target>#{@target}<reset>", nil)
|
||||
@cache.mkdir_p(@target, install: @install_builder)
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# The InstallDirectory class is identical to Directory but only performs
|
||||
# an action for an "install" operation.
|
||||
class InstallDirectory < Directory; end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -10,7 +10,7 @@ module Rscons
|
||||
else
|
||||
@vars["_SOURCES"] = @sources
|
||||
command = @env.build_command("${DISASM_CMD}", @vars)
|
||||
standard_command("Disassembling #{Util.short_format_paths(@sources)} => #{target}", command, stdout: @target)
|
||||
standard_command("Disassembling <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{target}<reset>", command, stdout: @target)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
require "pathname"
|
||||
|
||||
module Rscons
|
||||
module Builders
|
||||
# The Install builder copies files/directories to new locations.
|
||||
class Install < Builder
|
||||
|
||||
# Run the builder to produce a build target.
|
||||
def run(options)
|
||||
target_is_dir = (@sources.length > 1) ||
|
||||
Dir.exists?(@sources.first) ||
|
||||
Dir.exists?(@target)
|
||||
outdir = target_is_dir ? @target : File.dirname(@target)
|
||||
# Collect the list of files to copy over.
|
||||
file_map = {}
|
||||
if target_is_dir
|
||||
@sources.each do |src|
|
||||
if Dir.exists? src
|
||||
Dir.glob("#{src}/**/*", File::FNM_DOTMATCH).select do |f|
|
||||
File.file?(f)
|
||||
end.each do |subfile|
|
||||
subpath = Pathname.new(subfile).relative_path_from(Pathname.new(src)).to_s
|
||||
file_map[subfile] = "#{outdir}/#{subpath}"
|
||||
end
|
||||
else
|
||||
file_map[src] = "#{outdir}/#{File.basename(src)}"
|
||||
end
|
||||
end
|
||||
else
|
||||
file_map[sources.first] = target
|
||||
end
|
||||
printed_message = false
|
||||
file_map.each do |src, dest|
|
||||
# Check the cache and copy if necessary
|
||||
unless @cache.up_to_date?(dest, :Copy, [src], @env)
|
||||
unless printed_message
|
||||
message = "#{name} #{Util.short_format_paths(@sources)} => #{@target}"
|
||||
print_run_message(message, nil)
|
||||
printed_message = true
|
||||
end
|
||||
@cache.mkdir_p(File.dirname(dest))
|
||||
FileUtils.cp(src, dest, :preserve => true)
|
||||
end
|
||||
@cache.register_build(dest, :Copy, [src], @env)
|
||||
end
|
||||
(target_is_dir ? Dir.exists?(@target) : File.exists?(@target)) ? true : false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# The Copy builder is identical to the Install builder.
|
||||
class Copy < Install; end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -20,7 +20,7 @@ module Rscons
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @objects
|
||||
command = @env.build_command("${ARCMD}", @vars)
|
||||
standard_command("Building static library archive => #{@target}", command, sources: @objects)
|
||||
standard_command("Building static library archive <target>#{@target}<reset>", command, sources: @objects)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ module Rscons
|
||||
def finalize_command_with_depfile
|
||||
deps = @sources
|
||||
if File.exists?(@vars["_DEPFILE"])
|
||||
deps += Util.parse_makefile_deps(@vars["_DEPFILE"])
|
||||
deps += Util.parse_dependency_file(@vars["_DEPFILE"])
|
||||
end
|
||||
@cache.register_build(@target, @command, deps.uniq, @env)
|
||||
true
|
||||
|
||||
@@ -8,6 +8,7 @@ module Rscons
|
||||
|
||||
module Builders
|
||||
module Mixins
|
||||
# Mixin for builders needing to build object files.
|
||||
module Object
|
||||
|
||||
include Depfile
|
||||
@@ -77,15 +78,15 @@ module Rscons
|
||||
if @vars[:direct]
|
||||
@env.get_build_fname(target, depfilesuffix, self.class)
|
||||
else
|
||||
Rscons.set_suffix(target, depfilesuffix)
|
||||
"#{target}#{depfilesuffix}"
|
||||
end
|
||||
@cache.mkdir_p(File.dirname(@vars["_DEPFILE"]))
|
||||
command = @env.build_command(@command_template, @vars)
|
||||
@env.produces(@target, @vars["_DEPFILE"])
|
||||
self.produces(@vars["_DEPFILE"])
|
||||
if @vars[:direct]
|
||||
message = "#{@short_description}/Linking #{Util.short_format_paths(@sources)} => #{@target}"
|
||||
message = "#{@short_description}/Linking <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>"
|
||||
else
|
||||
message = "#{@short_description} #{Util.short_format_paths(@sources)}"
|
||||
message = "#{@short_description} <source>#{Util.short_format_paths(@sources)}<reset>"
|
||||
end
|
||||
standard_command(message, command)
|
||||
end
|
||||
|
||||
@@ -27,7 +27,7 @@ module Rscons
|
||||
@vars["_SOURCES"] = @objects
|
||||
@vars["#{ld_var}"] = ld
|
||||
command = @env.build_command("${#{ld_var}CMD}", @vars)
|
||||
standard_command("Linking => #{@target}", command, sources: @objects)
|
||||
standard_command("Linking <target>#{@target}<reset>", command, sources: @objects)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -23,10 +23,10 @@ module Rscons
|
||||
@vars["_PREPROCESS_DEPGEN"] = depgen
|
||||
@vars["_TARGET"] = @target
|
||||
@vars["_SOURCES"] = @sources
|
||||
@vars["_DEPFILE"] = Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars))
|
||||
@vars["_DEPFILE"] = "#{target}#{env.expand_varref("${DEPFILESUFFIX}", vars)}"
|
||||
command = @env.build_command("${CPP_CMD}", @vars)
|
||||
@env.produces(@target, @vars["_DEPFILE"])
|
||||
standard_command("Preprocessing #{Util.short_format_paths(@sources)} => #{@target}", command)
|
||||
self.produces(@vars["_DEPFILE"])
|
||||
standard_command("Preprocessing <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ module Rscons
|
||||
include Mixins::Program
|
||||
|
||||
class << self
|
||||
# Custom new method which will delegate to the correct class depending
|
||||
# on the options specified.
|
||||
def new(options, *more)
|
||||
unless File.basename(options[:target])["."]
|
||||
options[:target] += options[:env].expand_varref("${PROGSUFFIX}", options[:vars])
|
||||
|
||||
@@ -8,6 +8,8 @@ module Rscons
|
||||
include Mixins::Program
|
||||
|
||||
class << self
|
||||
# Custom new method which will delegate to the correct class depending
|
||||
# on the options specified.
|
||||
def new(options, *more)
|
||||
libprefix = options[:env].expand_varref("${SHLIBPREFIX}", options[:vars])
|
||||
unless File.basename(options[:target]).start_with?(libprefix)
|
||||
|
||||
@@ -7,6 +7,8 @@ module Rscons
|
||||
include Mixins::Object
|
||||
|
||||
class << self
|
||||
# Content component to add to build path to separate objects built
|
||||
# using this builder from others.
|
||||
def extra_path
|
||||
"_shared"
|
||||
end
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
module Rscons
|
||||
module Builders
|
||||
# A Generic builder which has its operation is defined at instantiation.
|
||||
#
|
||||
# @since 1.8.0
|
||||
class SimpleBuilder < Builder
|
||||
|
||||
# @return [String]
|
||||
|
||||
+61
-29
@@ -100,7 +100,6 @@ module Rscons
|
||||
# @return [void]
|
||||
def write
|
||||
@cache["version"] = VERSION
|
||||
validate_json_object(@cache)
|
||||
File.open(CACHE_FILE, "w") do |fh|
|
||||
fh.puts(JSON.dump(@cache))
|
||||
end
|
||||
@@ -236,11 +235,21 @@ module Rscons
|
||||
# trigger a rebuild.
|
||||
# @param deps [Array<String>] List of dependencies for the target.
|
||||
# @param env [Environment] The {Rscons::Environment}.
|
||||
# @param options [Hash] Optional arguments.
|
||||
# @option options [Boolean] :install
|
||||
# Whether the target is for an install operation.
|
||||
# @option options [Boolean] :side_effect
|
||||
# Whether the target is a side-effect file (no checksum will be stored).
|
||||
#
|
||||
# @return [void]
|
||||
def register_build(targets, command, deps, env)
|
||||
def register_build(targets, command, deps, env, options = {})
|
||||
Array(targets).each do |target|
|
||||
target_checksum = Rscons.phony_target?(target) ? "" : calculate_checksum(target)
|
||||
target_checksum =
|
||||
if options[:side_effect] or Rscons.phony_target?(target)
|
||||
""
|
||||
else
|
||||
calculate_checksum(target)
|
||||
end
|
||||
@cache["targets"][get_cache_key(target)] = {
|
||||
"command" => Digest::MD5.hexdigest(command.inspect),
|
||||
"checksum" => target_checksum,
|
||||
@@ -256,41 +265,79 @@ module Rscons
|
||||
"checksum" => lookup_checksum(dep),
|
||||
}
|
||||
end,
|
||||
"install" => !!options[:install],
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# Return a list of targets that have been built.
|
||||
# Return a list of targets that have been built or installed.
|
||||
#
|
||||
# @return [Array<String>] List of targets that have been built.
|
||||
def targets
|
||||
@cache["targets"].keys
|
||||
# @param install [Boolean]
|
||||
# Whether to return installed targets. If false, will only return normal
|
||||
# build targets and not install targets.
|
||||
#
|
||||
# @return [Array<String>]
|
||||
# List of build targets that have been built or installed.
|
||||
def targets(install)
|
||||
install = !!install
|
||||
@cache["targets"].select do |key, target|
|
||||
target["install"] == install
|
||||
end.map(&:first)
|
||||
end
|
||||
|
||||
# Make any needed directories and record the ones that are created for
|
||||
# removal upon a "clean" operation.
|
||||
# Create any needed directory components for a build or install operation.
|
||||
#
|
||||
# @param path [String] Directory to create.
|
||||
# Build directories will be removed if empty upon a "clean" operation.
|
||||
# Install directories will be removed if empty upon an "uninstall"
|
||||
# operation.
|
||||
#
|
||||
# @param path [String]
|
||||
# Directory to create.
|
||||
# @param options [Hash]
|
||||
# Optional arguments.
|
||||
# @option options [Boolean] :install
|
||||
# Whether the directory is for an install operation.
|
||||
#
|
||||
# @return [void]
|
||||
def mkdir_p(path)
|
||||
def mkdir_p(path, options = {})
|
||||
parts = path.split(/[\\\/]/)
|
||||
parts.each_index do |i|
|
||||
next if parts[i] == ""
|
||||
subpath = File.join(*parts[0, i + 1])
|
||||
unless File.exists?(subpath)
|
||||
FileUtils.mkdir_p(subpath)
|
||||
@cache["directories"][subpath] = true
|
||||
@cache["directories"][subpath] = !!options[:install]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Return a list of directories which were created as a part of the build.
|
||||
#
|
||||
# @param install [Boolean]
|
||||
# Whether to return installed directories. If false, will only return
|
||||
# normal build directories and not install targets.
|
||||
#
|
||||
# @return [Array<String>]
|
||||
# List of directories which were created as a part of the build.
|
||||
def directories
|
||||
@cache["directories"].keys
|
||||
def directories(install)
|
||||
install = !!install
|
||||
@cache["directories"].select do |key, d_install|
|
||||
d_install == install
|
||||
end.map(&:first)
|
||||
end
|
||||
|
||||
# Remove a target from the cache.
|
||||
#
|
||||
# @return [void]
|
||||
def remove_target(target)
|
||||
@cache["targets"].delete(target)
|
||||
end
|
||||
|
||||
# Remove a directory from the cache.
|
||||
#
|
||||
# @return [void]
|
||||
def remove_directory(directory)
|
||||
@cache["directories"].delete(directory)
|
||||
end
|
||||
|
||||
private
|
||||
@@ -343,20 +390,5 @@ module Rscons
|
||||
@lookup_checksums[file] = Digest::MD5.hexdigest(File.read(file, mode: "rb")) rescue ""
|
||||
end
|
||||
|
||||
# Validate that an object is one of a known set of values that can be
|
||||
# serialized properly with JSON.
|
||||
#
|
||||
# @param o [Object]
|
||||
# Object to validate.
|
||||
def validate_json_object(o)
|
||||
if o.is_a?(Array)
|
||||
o.each {|v| validate_json_object(v)}
|
||||
elsif o.is_a?(Hash)
|
||||
o.each {|*kv| validate_json_object(kv)}
|
||||
elsif [NilClass, TrueClass, FalseClass, String].none? {|c| o.is_a?(c)}
|
||||
raise "Unexpected cache value for serialization: #{o.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
+12
-8
@@ -1,11 +1,12 @@
|
||||
require "rscons"
|
||||
require "optparse"
|
||||
|
||||
# CLI usage string.
|
||||
USAGE = <<EOF
|
||||
Usage: #{$0} [global options] [operation] [operation options]
|
||||
|
||||
Global options:
|
||||
-F, --show-failure Show previous failed command(s) and exit
|
||||
-F, --show-failure Show failed command log from previous build and exit
|
||||
--version Show rscons version and exit
|
||||
-h, --help Show rscons help and exit
|
||||
-r COLOR, --color=COLOR Set color mode (off, auto, force)
|
||||
@@ -121,21 +122,24 @@ module Rscons
|
||||
rsconscript = DEFAULT_RSCONSCRIPTS.find do |f|
|
||||
File.exists?(f)
|
||||
end
|
||||
unless rsconscript
|
||||
$stderr.puts "Could not find the Rsconscript to execute."
|
||||
$stderr.puts "Looked for: #{DEFAULT_RSCONSCRIPTS.join(", ")}"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
script = Script.new
|
||||
script.load(rsconscript)
|
||||
if rsconscript
|
||||
script = Script.new
|
||||
script.load(rsconscript)
|
||||
end
|
||||
|
||||
if do_help
|
||||
puts USAGE
|
||||
exit 0
|
||||
end
|
||||
|
||||
unless rsconscript
|
||||
$stderr.puts "Could not find the Rsconscript to execute."
|
||||
$stderr.puts "Looked for: #{DEFAULT_RSCONSCRIPTS.join(", ")}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
operation_options = parse_operation_args(operation, argv) || {}
|
||||
|
||||
exit Rscons.application.run(operation, script, operation_options)
|
||||
|
||||
+235
-112
@@ -10,20 +10,48 @@ module Rscons
|
||||
|
||||
# Create a ConfigureOp.
|
||||
#
|
||||
# @param work_dir [String]
|
||||
# Work directory for configure operation.
|
||||
def initialize(work_dir)
|
||||
@work_dir = work_dir
|
||||
# @param options [Hash]
|
||||
# Optional parameters.
|
||||
# @option options [String] :build_dir
|
||||
# Build directory.
|
||||
# @option options [String] :prefix
|
||||
# Install prefix.
|
||||
# @option options [String] :project_name
|
||||
# Project name.
|
||||
def initialize(options)
|
||||
# Default options.
|
||||
options[:build_dir] ||= "build"
|
||||
options[:prefix] ||= "/usr/local"
|
||||
@work_dir = "#{options[:build_dir]}/configure"
|
||||
FileUtils.mkdir_p(@work_dir)
|
||||
@log_fh = File.open("#{@work_dir}/config.log", "wb")
|
||||
cache = Cache.instance
|
||||
cache["failed_commands"] = []
|
||||
cache["configuration_data"] = {}
|
||||
cache["configuration_data"]["build_dir"] = options[:build_dir]
|
||||
cache["configuration_data"]["prefix"] = options[:prefix]
|
||||
if project_name = options[:project_name]
|
||||
Ansi.write($stdout, "Configuring ", :cyan, project_name, :reset, "...\n")
|
||||
else
|
||||
$stdout.puts "Configuring project..."
|
||||
end
|
||||
Ansi.write($stdout, "Setting build directory... ", :green, options[:build_dir], :reset, "\n")
|
||||
Ansi.write($stdout, "Setting prefix... ", :green, options[:prefix], :reset, "\n")
|
||||
store_merge("prefix" => options[:prefix])
|
||||
end
|
||||
|
||||
# Close the log file handle.
|
||||
#
|
||||
# @param success [Boolean]
|
||||
# Whether all configure operations were successful.
|
||||
#
|
||||
# @return [void]
|
||||
def close
|
||||
def close(success)
|
||||
@log_fh.close
|
||||
@log_fh = nil
|
||||
cache = Cache.instance
|
||||
cache["configuration_data"]["configured"] = success
|
||||
cache.write
|
||||
end
|
||||
|
||||
# Check for a working C compiler.
|
||||
@@ -41,7 +69,7 @@ module Rscons
|
||||
cc = ccc.find do |cc|
|
||||
test_c_compiler(cc)
|
||||
end
|
||||
common_config_checks(cc ? 0 : 1, success_message: cc)
|
||||
complete(cc ? 0 : 1, success_message: cc)
|
||||
end
|
||||
|
||||
# Check for a working C++ compiler.
|
||||
@@ -59,7 +87,7 @@ module Rscons
|
||||
cc = ccc.find do |cc|
|
||||
test_cxx_compiler(cc)
|
||||
end
|
||||
common_config_checks(cc ? 0 : 1, success_message: cc)
|
||||
complete(cc ? 0 : 1, success_message: cc)
|
||||
end
|
||||
|
||||
# Check for a working D compiler.
|
||||
@@ -77,7 +105,7 @@ module Rscons
|
||||
dc = cdc.find do |dc|
|
||||
test_d_compiler(dc)
|
||||
end
|
||||
common_config_checks(dc ? 0 : 1, success_message: dc)
|
||||
complete(dc ? 0 : 1, success_message: dc)
|
||||
end
|
||||
|
||||
# Check for a package or configure program output.
|
||||
@@ -94,11 +122,12 @@ module Rscons
|
||||
if status == 0
|
||||
store_parse(stdout, options)
|
||||
end
|
||||
common_config_checks(status, options)
|
||||
complete(status, options)
|
||||
end
|
||||
|
||||
# Check for a C header.
|
||||
def check_c_header(header_name, options = {})
|
||||
check_cpppath = [nil] + (options[:check_cpppath] || [])
|
||||
Ansi.write($stdout, "Checking for C header '", :cyan, header_name, :reset, "'... ")
|
||||
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
|
||||
fh.puts <<-EOF
|
||||
@@ -111,15 +140,30 @@ module Rscons
|
||||
vars = {
|
||||
"LD" => "${CC}",
|
||||
"_SOURCES" => "#{@work_dir}/cfgtest.c",
|
||||
"_TARGET" => "#{@work_dir}/cfgtest.exe",
|
||||
"_TARGET" => "#{@work_dir}/cfgtest.o",
|
||||
"_DEPFILE" => "#{@work_dir}/cfgtest.mf",
|
||||
}
|
||||
command = BasicEnvironment.new.build_command("${LDCMD}", vars)
|
||||
_, _, status = log_and_test_command(command)
|
||||
common_config_checks(status, options)
|
||||
status = 1
|
||||
check_cpppath.each do |cpppath|
|
||||
env = BasicEnvironment.new
|
||||
if cpppath
|
||||
env["CPPPATH"] += Array(cpppath)
|
||||
end
|
||||
command = env.build_command("${CCCMD}", vars)
|
||||
_, _, status = log_and_test_command(command)
|
||||
if status == 0
|
||||
if cpppath
|
||||
store_append({"CPPPATH" => Array(cpppath)}, options)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
complete(status, options)
|
||||
end
|
||||
|
||||
# Check for a C++ header.
|
||||
def check_cxx_header(header_name, options = {})
|
||||
check_cpppath = [nil] + (options[:check_cpppath] || [])
|
||||
Ansi.write($stdout, "Checking for C++ header '", :cyan, header_name, :reset, "'... ")
|
||||
File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh|
|
||||
fh.puts <<-EOF
|
||||
@@ -132,15 +176,30 @@ module Rscons
|
||||
vars = {
|
||||
"LD" => "${CXX}",
|
||||
"_SOURCES" => "#{@work_dir}/cfgtest.cxx",
|
||||
"_TARGET" => "#{@work_dir}/cfgtest.exe",
|
||||
"_TARGET" => "#{@work_dir}/cfgtest.o",
|
||||
"_DEPFILE" => "#{@work_dir}/cfgtest.mf",
|
||||
}
|
||||
command = BasicEnvironment.new.build_command("${LDCMD}", vars)
|
||||
_, _, status = log_and_test_command(command)
|
||||
common_config_checks(status, options)
|
||||
status = 1
|
||||
check_cpppath.each do |cpppath|
|
||||
env = BasicEnvironment.new
|
||||
if cpppath
|
||||
env["CPPPATH"] += Array(cpppath)
|
||||
end
|
||||
command = env.build_command("${CXXCMD}", vars)
|
||||
_, _, status = log_and_test_command(command)
|
||||
if status == 0
|
||||
if cpppath
|
||||
store_append({"CPPPATH" => Array(cpppath)}, options)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
complete(status, options)
|
||||
end
|
||||
|
||||
# Check for a D import.
|
||||
def check_d_import(d_import, options = {})
|
||||
check_d_import_path = [nil] + (options[:check_d_import_path] || [])
|
||||
Ansi.write($stdout, "Checking for D import '", :cyan, d_import, :reset, "'... ")
|
||||
File.open("#{@work_dir}/cfgtest.d", "wb") do |fh|
|
||||
fh.puts <<-EOF
|
||||
@@ -153,15 +212,30 @@ module Rscons
|
||||
vars = {
|
||||
"LD" => "${DC}",
|
||||
"_SOURCES" => "#{@work_dir}/cfgtest.d",
|
||||
"_TARGET" => "#{@work_dir}/cfgtest.exe",
|
||||
"_TARGET" => "#{@work_dir}/cfgtest.o",
|
||||
"_DEPFILE" => "#{@work_dir}/cfgtest.mf",
|
||||
}
|
||||
command = BasicEnvironment.new.build_command("${LDCMD}", vars)
|
||||
_, _, status = log_and_test_command(command)
|
||||
common_config_checks(status, options)
|
||||
status = 1
|
||||
check_d_import_path.each do |d_import_path|
|
||||
env = BasicEnvironment.new
|
||||
if d_import_path
|
||||
env["D_IMPORT_PATH"] += Array(d_import_path)
|
||||
end
|
||||
command = env.build_command("${DCCMD}", vars)
|
||||
_, _, status = log_and_test_command(command)
|
||||
if status == 0
|
||||
if d_import_path
|
||||
store_append({"D_IMPORT_PATH" => Array(d_import_path)}, options)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
complete(status, options)
|
||||
end
|
||||
|
||||
# Check for a library.
|
||||
def check_lib(lib, options = {})
|
||||
check_libpath = [nil] + (options[:check_libpath] || [])
|
||||
Ansi.write($stdout, "Checking for library '", :cyan, lib, :reset, "'... ")
|
||||
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
|
||||
fh.puts <<-EOF
|
||||
@@ -176,19 +250,150 @@ module Rscons
|
||||
"_SOURCES" => "#{@work_dir}/cfgtest.c",
|
||||
"_TARGET" => "#{@work_dir}/cfgtest.exe",
|
||||
}
|
||||
command = BasicEnvironment.new.build_command("${LDCMD}", vars)
|
||||
_, _, status = log_and_test_command(command)
|
||||
status = 1
|
||||
check_libpath.each do |libpath|
|
||||
env = BasicEnvironment.new
|
||||
if libpath
|
||||
env["LIBPATH"] += Array(libpath)
|
||||
end
|
||||
command = env.build_command("${LDCMD}", vars)
|
||||
_, _, status = log_and_test_command(command)
|
||||
if status == 0
|
||||
if libpath
|
||||
store_append({"LIBPATH" => Array(libpath)}, options)
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
if status == 0
|
||||
store_append({"LIBS" => [lib]}, options)
|
||||
end
|
||||
common_config_checks(status, options)
|
||||
complete(status, options)
|
||||
end
|
||||
|
||||
# Check for a executable program.
|
||||
def check_program(program, options = {})
|
||||
Ansi.write($stdout, "Checking for program '", :cyan, program, :reset, "'... ")
|
||||
path = Util.find_executable(program)
|
||||
common_config_checks(path ? 0 : 1, options.merge(success_message: path))
|
||||
complete(path ? 0 : 1, options.merge(success_message: path))
|
||||
end
|
||||
|
||||
# Execute a test command and log the result.
|
||||
#
|
||||
# @param command [Array<String>]
|
||||
# Command to execute.
|
||||
# @param options [Hash]
|
||||
# Optional arguments.
|
||||
# @option options [String] :stdin
|
||||
# Data to send to standard input stream of the executed command.
|
||||
#
|
||||
# @return [String, String, Process::Status]
|
||||
# stdout, stderr, status
|
||||
def log_and_test_command(command, options = {})
|
||||
begin
|
||||
@log_fh.puts("Command: #{command.join(" ")}")
|
||||
stdout, stderr, status = Open3.capture3(*command, stdin_data: options[:stdin])
|
||||
@log_fh.puts("Exit status: #{status.to_i}")
|
||||
@log_fh.write(stdout)
|
||||
@log_fh.write(stderr)
|
||||
[stdout, stderr, status]
|
||||
rescue Errno::ENOENT
|
||||
["", "", 127]
|
||||
end
|
||||
end
|
||||
|
||||
# Store construction variables for merging into the Cache.
|
||||
#
|
||||
# @param vars [Hash]
|
||||
# Hash containing the variables to merge.
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
# @option options [String] :use
|
||||
# A 'use' name. If specified, the construction variables are only applied
|
||||
# to an Environment if the Environment is constructed with a matching
|
||||
# `:use` value.
|
||||
def store_merge(vars, options = {})
|
||||
store_vars = store_common(options)
|
||||
store_vars["merge"] ||= {}
|
||||
vars.each_pair do |key, value|
|
||||
store_vars["merge"][key] = value
|
||||
end
|
||||
end
|
||||
|
||||
# Store construction variables for appending into the Cache.
|
||||
#
|
||||
# @param vars [Hash]
|
||||
# Hash containing the variables to append.
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
# @option options [String] :use
|
||||
# A 'use' name. If specified, the construction variables are only applied
|
||||
# to an Environment if the Environment is constructed with a matching
|
||||
# `:use` value.
|
||||
def store_append(vars, options = {})
|
||||
store_vars = store_common(options)
|
||||
store_vars["append"] ||= {}
|
||||
vars.each_pair do |key, value|
|
||||
if store_vars["append"][key].is_a?(Array) and value.is_a?(Array)
|
||||
store_vars["append"][key] += value
|
||||
else
|
||||
store_vars["append"][key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Store flags to be parsed into the Cache.
|
||||
#
|
||||
# @param flags [String]
|
||||
# String containing the flags to parse.
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
# @option options [String] :use
|
||||
# A 'use' name. If specified, the construction variables are only applied
|
||||
# to an Environment if the Environment is constructed with a matching
|
||||
# `:use` value.
|
||||
def store_parse(flags, options = {})
|
||||
store_vars = store_common(options)
|
||||
store_vars["parse"] ||= []
|
||||
store_vars["parse"] << flags
|
||||
end
|
||||
|
||||
# Perform processing common to several configure checks.
|
||||
#
|
||||
# @param status [Process::Status, Integer]
|
||||
# Process exit code. 0 for success, non-zero for error.
|
||||
# @param options [Hash]
|
||||
# Common check options.
|
||||
# @option options [Boolean] :fail
|
||||
# Whether to fail configuration if the requested item is not found.
|
||||
# This defaults to true if the :set_define option is not specified,
|
||||
# otherwise defaults to false if :set_define option is specified.
|
||||
# @option options [String] :set_define
|
||||
# A define to set (in CPPDEFINES) if the requested item is found.
|
||||
# @option options [String] :success_message
|
||||
# Message to print on success (default "found").
|
||||
def complete(status, options)
|
||||
success_message = options[:success_message] || "found"
|
||||
fail_message = options[:fail_message] || "not found"
|
||||
if status == 0
|
||||
Ansi.write($stdout, :green, "#{success_message}\n")
|
||||
if options[:set_define]
|
||||
store_append("CPPDEFINES" => [options[:set_define]])
|
||||
end
|
||||
else
|
||||
should_fail =
|
||||
if options.has_key?(:fail)
|
||||
options[:fail]
|
||||
else
|
||||
!options[:set_define]
|
||||
end
|
||||
if should_fail
|
||||
Ansi.write($stdout, :red, "#{fail_message}\n")
|
||||
raise ConfigureFailure.new
|
||||
else
|
||||
Ansi.write($stdout, :yellow, "#{fail_message}\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
@@ -281,7 +486,12 @@ module Rscons
|
||||
"DC" => dc,
|
||||
"DCCMD" => env["DCCMD"].map {|e| if e == "-o"; "-of"; else; e; end},
|
||||
"LDCMD" => env["LDCMD"].map {|e| if e == "-o"; "-of"; else; e; end},
|
||||
"DDEPGEN" => ["-deps=${_DEPFILE}"],
|
||||
}
|
||||
if RUBY_PLATFORM =~ /mingw/
|
||||
# ldc2 on Windows expect an object file suffix of .obj.
|
||||
merge["OBJSUFFIX"] = %w[.obj]
|
||||
end
|
||||
end
|
||||
_, _, status = log_and_test_command(command)
|
||||
if status == 0
|
||||
@@ -294,64 +504,6 @@ module Rscons
|
||||
end
|
||||
end
|
||||
|
||||
# Execute a test command and log the result.
|
||||
def log_and_test_command(command)
|
||||
begin
|
||||
@log_fh.puts("Command: #{command.join(" ")}")
|
||||
stdout, stderr, status = Open3.capture3(*command)
|
||||
@log_fh.puts("Exit status: #{status.to_i}")
|
||||
@log_fh.write(stdout)
|
||||
@log_fh.write(stderr)
|
||||
[stdout, stderr, status]
|
||||
rescue Errno::ENOENT
|
||||
["", "", 127]
|
||||
end
|
||||
end
|
||||
|
||||
# Store construction variables for merging into the Cache.
|
||||
#
|
||||
# @param vars [Hash]
|
||||
# Hash containing the variables to merge.
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
def store_merge(vars, options = {})
|
||||
store_vars = store_common(options)
|
||||
store_vars["merge"] ||= {}
|
||||
vars.each_pair do |key, value|
|
||||
store_vars["merge"][key] = value
|
||||
end
|
||||
end
|
||||
|
||||
# Store construction variables for appending into the Cache.
|
||||
#
|
||||
# @param vars [Hash]
|
||||
# Hash containing the variables to append.
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
def store_append(vars, options = {})
|
||||
store_vars = store_common(options)
|
||||
store_vars["append"] ||= {}
|
||||
vars.each_pair do |key, value|
|
||||
if store_vars["append"][key].is_a?(Array) and value.is_a?(Array)
|
||||
store_vars["append"][key] += value
|
||||
else
|
||||
store_vars["append"][key] = value
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Store flags to be parsed into the Cache.
|
||||
#
|
||||
# @param flags [String]
|
||||
# String containing the flags to parse.
|
||||
# @param options [Hash]
|
||||
# Options.
|
||||
def store_parse(flags, options = {})
|
||||
store_vars = store_common(options)
|
||||
store_vars["parse"] ||= []
|
||||
store_vars["parse"] << flags
|
||||
end
|
||||
|
||||
# Common functionality for all store methods.
|
||||
#
|
||||
# @param options [Hash]
|
||||
@@ -375,34 +527,5 @@ module Rscons
|
||||
end
|
||||
end
|
||||
|
||||
# Perform processing common to several configure checks.
|
||||
#
|
||||
# @param status [Process::Status, Integer]
|
||||
# Process exit code.
|
||||
# @param options [Hash]
|
||||
# Common check options.
|
||||
# @option options [Boolean] :fail
|
||||
# Whether to fail configuration if the requested item is not found.
|
||||
# @option options [String] :set_define
|
||||
# A define to set (in CPPDEFINES) if the requested item is found.
|
||||
# @option options [String] :success_message
|
||||
# Message to print on success (default "found").
|
||||
def common_config_checks(status, options)
|
||||
success_message = options[:success_message] || "found"
|
||||
if status == 0
|
||||
Ansi.write($stdout, :green, "#{success_message}\n")
|
||||
if options[:set_define]
|
||||
store_append("CPPDEFINES" => [options[:set_define]])
|
||||
end
|
||||
else
|
||||
if options.has_key?(:fail) and not options[:fail]
|
||||
Ansi.write($stdout, :yellow, "not found\n")
|
||||
else
|
||||
Ansi.write($stdout, :red, "not found\n")
|
||||
raise ConfigureFailure.new
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
+57
-55
@@ -59,6 +59,9 @@ module Rscons
|
||||
# :command, :short, or :off (default :short)
|
||||
# @option options [Boolean] :exclude_builders
|
||||
# Whether to omit adding default builders (default false)
|
||||
# @option options [String, Array<String>] :use
|
||||
# Use flag(s). If specified, any configuration flags which were saved
|
||||
# with a corresponding `:use` value will be applied to this Environment.
|
||||
#
|
||||
# If a block is given, the Environment object is yielded to the block and
|
||||
# when the block returns, the {#process} method is automatically called.
|
||||
@@ -72,7 +75,8 @@ module Rscons
|
||||
# Hash of Thread object => {Command} or {Builder}.
|
||||
@threads = {}
|
||||
@registered_build_dependencies = {}
|
||||
@side_effects = {}
|
||||
# Set of side-effect files that have not yet been built.
|
||||
@side_effects = Set.new
|
||||
@builder_sets = []
|
||||
@build_targets = {}
|
||||
@user_deps = {}
|
||||
@@ -165,8 +169,6 @@ module Rscons
|
||||
# Create a new {Builders::SimpleBuilder} instance and add it to the
|
||||
# environment.
|
||||
#
|
||||
# @since 1.8.0
|
||||
#
|
||||
# @param name [String,Symbol]
|
||||
# The name of the builder to add.
|
||||
#
|
||||
@@ -216,8 +218,6 @@ module Rscons
|
||||
# build operation succeeded. Post-build hooks can register new build
|
||||
# targets.
|
||||
#
|
||||
# @since 1.7.0
|
||||
#
|
||||
# @yield [build_op]
|
||||
# Invoke the given block with the current build operation.
|
||||
# @yieldparam build_op [Hash]
|
||||
@@ -251,7 +251,7 @@ module Rscons
|
||||
if extra_path = builder_class.extra_path
|
||||
extra_path = "/#{extra_path}"
|
||||
end
|
||||
"#{@build_root}#{extra_path}/#{Util.make_relative_path(Rscons.set_suffix(source_fname, suffix))}".gsub("\\", "/")
|
||||
"#{@build_root}#{extra_path}/#{Util.make_relative_path("#{source_fname}#{suffix}")}".gsub("\\", "/")
|
||||
end
|
||||
|
||||
# Build all build targets specified in the Environment.
|
||||
@@ -261,6 +261,7 @@ module Rscons
|
||||
#
|
||||
# @return [void]
|
||||
def process
|
||||
Cache.instance.clear_checksum_cache!
|
||||
@process_failures = []
|
||||
@process_blocking_wait = false
|
||||
@process_commands_waiting_to_run = []
|
||||
@@ -286,7 +287,7 @@ module Rscons
|
||||
unless @process_failures.empty?
|
||||
msg = @process_failures.join("\n")
|
||||
if Cache.instance["failed_commands"].size > 0
|
||||
msg += "\nRun `#{$0} -F` to see the failed command(s)."
|
||||
msg += "\nUse -F to view the failed command log from the previous build operation"
|
||||
end
|
||||
raise BuildError.new(msg)
|
||||
end
|
||||
@@ -374,8 +375,6 @@ module Rscons
|
||||
# described:
|
||||
# env.build_after("program.o", "gen.c")
|
||||
#
|
||||
# @since 1.10.0
|
||||
#
|
||||
# @param targets [String, Array<String>]
|
||||
# Target files to wait to build until the prerequisites are finished
|
||||
# building.
|
||||
@@ -402,8 +401,6 @@ module Rscons
|
||||
# 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>]
|
||||
@@ -412,11 +409,25 @@ module Rscons
|
||||
# @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
|
||||
@builder_sets.reverse.each do |builder_set|
|
||||
if builders = builder_set[target]
|
||||
builders.last.produces(*side_effects)
|
||||
return
|
||||
end
|
||||
end
|
||||
raise "Could not find a registered build target #{target.inspect}"
|
||||
end
|
||||
|
||||
# Register a side effect file.
|
||||
#
|
||||
# This is an internally used method.
|
||||
#
|
||||
# @api private
|
||||
#
|
||||
# @param side_effect [String]
|
||||
# Side effect fiel name.
|
||||
def register_side_effect(side_effect)
|
||||
@side_effects << side_effect
|
||||
end
|
||||
|
||||
# Return the list of user dependencies for a given target.
|
||||
@@ -479,29 +490,6 @@ module Rscons
|
||||
end
|
||||
end
|
||||
|
||||
# Execute a command using the system shell.
|
||||
#
|
||||
# The shell is automatically determined but can be overridden by the SHELL
|
||||
# construction variable. If the SHELL construction variable is specified,
|
||||
# the flag to pass to the shell is automatically dtermined but can be
|
||||
# overridden by the SHELLFLAG construction variable.
|
||||
#
|
||||
# @param command [String] Command to execute.
|
||||
#
|
||||
# @return [String] The command's standard output.
|
||||
def shell(command)
|
||||
shell_cmd =
|
||||
if self["SHELL"]
|
||||
flag = self["SHELLFLAG"] || (self["SHELL"] == "cmd" ? "/c" : "-c")
|
||||
[self["SHELL"], flag]
|
||||
else
|
||||
Rscons.get_system_shell
|
||||
end
|
||||
IO.popen([*shell_cmd, command]) do |io|
|
||||
io.read
|
||||
end
|
||||
end
|
||||
|
||||
# Print the builder run message, depending on the Environment's echo mode.
|
||||
#
|
||||
# @param builder [Builder]
|
||||
@@ -526,7 +514,12 @@ module Rscons
|
||||
when :short
|
||||
message = short_description if short_description
|
||||
end
|
||||
Ansi.write($stdout, :cyan, message, :reset, "\n") if message
|
||||
if message
|
||||
total_build_steps = Rscons.application.get_total_build_steps.to_s
|
||||
this_build_step = sprintf("%#{total_build_steps.size}d", builder.build_step)
|
||||
progress = "[#{this_build_step}/#{total_build_steps}]"
|
||||
Ansi.write($stdout, *Util.colorize_markup("#{progress} #{message}"), "\n")
|
||||
end
|
||||
end
|
||||
|
||||
# Get the Builder for a target.
|
||||
@@ -547,6 +540,16 @@ module Rscons
|
||||
@builder_sets << build_builder_set
|
||||
end
|
||||
|
||||
# Get the number of build steps remaining.
|
||||
#
|
||||
# @return [Integer]
|
||||
# The number of build steps remaining.
|
||||
def build_steps_remaining
|
||||
@builder_sets.reduce(0) do |result, builder_set|
|
||||
result + builder_set.build_steps_remaining
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Build a BuilderSet.
|
||||
@@ -564,9 +567,9 @@ module Rscons
|
||||
#
|
||||
# @return [void]
|
||||
def run_builder(builder)
|
||||
# TODO: have Cache determine when checksums may be invalid based on
|
||||
# file size and/or timestamp.
|
||||
Cache.instance.clear_checksum_cache!
|
||||
unless builder.nop?
|
||||
builder.build_step ||= Rscons.application.get_next_build_step
|
||||
end
|
||||
case result = builder.run({})
|
||||
when Array
|
||||
result.each do |waititem|
|
||||
@@ -588,8 +591,9 @@ module Rscons
|
||||
when true
|
||||
# Register side-effect files as build targets so that a Cache
|
||||
# clean operation will remove them.
|
||||
(@side_effects[builder.target] || []).each do |side_effect_file|
|
||||
Cache.instance.register_build(side_effect_file, nil, [], self)
|
||||
builder.side_effects.each do |side_effect|
|
||||
Cache.instance.register_build(side_effect, nil, [], self, side_effect: true)
|
||||
@side_effects.delete(side_effect)
|
||||
end
|
||||
@build_hooks[:post].each do |build_hook_block|
|
||||
build_hook_block.call(builder)
|
||||
@@ -634,7 +638,7 @@ module Rscons
|
||||
if thread
|
||||
# We found a completed thread.
|
||||
process_remove_wait(thread)
|
||||
builder = builder_for_thread(thread)
|
||||
unblocked_builder = builder_for_thread(thread)
|
||||
completed_command = @threads[thread]
|
||||
@threads.delete(thread)
|
||||
if completed_command.is_a?(Command)
|
||||
@@ -642,7 +646,7 @@ module Rscons
|
||||
completed_command.status = thread.value
|
||||
unless completed_command.status
|
||||
Cache.instance["failed_commands"] << completed_command.command
|
||||
@process_failures << "Failed to build #{builder.target}."
|
||||
@process_failures << "Failed to build #{unblocked_builder.target}."
|
||||
return
|
||||
end
|
||||
end
|
||||
@@ -667,15 +671,13 @@ module Rscons
|
||||
result << builder_for_thread(thread).target
|
||||
end
|
||||
if @builder_sets.size > 0
|
||||
builder = @builder_sets[0].get_next_builder_to_run(targets_still_building)
|
||||
end
|
||||
|
||||
if builder
|
||||
builder.vars = @varset.merge(builder.vars)
|
||||
@build_hooks[:pre].each do |build_hook_block|
|
||||
build_hook_block.call(builder)
|
||||
if builder = @builder_sets[0].get_next_builder_to_run(targets_still_building)
|
||||
builder.vars = @varset.merge(builder.vars)
|
||||
@build_hooks[:pre].each do |build_hook_block|
|
||||
build_hook_block.call(builder)
|
||||
end
|
||||
return run_builder(builder)
|
||||
end
|
||||
return run_builder(builder)
|
||||
end
|
||||
|
||||
if @threads.size > 0
|
||||
|
||||
@@ -3,6 +3,7 @@ module Rscons
|
||||
# The Script class encapsulates the state of a build script.
|
||||
class Script
|
||||
|
||||
# DSL available to the Rsconscript.
|
||||
class Dsl
|
||||
# Create a Dsl.
|
||||
def initialize(script)
|
||||
@@ -57,6 +58,7 @@ module Rscons
|
||||
end
|
||||
end
|
||||
|
||||
# DSL available to the 'configure' block.
|
||||
class ConfigureDsl
|
||||
# Create a ConfigureDsl.
|
||||
#
|
||||
@@ -81,6 +83,19 @@ module Rscons
|
||||
@configure_op.__send__(method_name, *args)
|
||||
end
|
||||
end
|
||||
|
||||
# Perform a custom configuration check.
|
||||
#
|
||||
# @param message [String]
|
||||
# Custom configuration check message (e.g. "Checking for foo").
|
||||
# rscons will add "... " to the end of the message.
|
||||
# @yieldparam configure_op [ConfigureOp]
|
||||
# {ConfigureOp} object.
|
||||
# @return [void]
|
||||
def custom_check(message, &block)
|
||||
$stdout.write(message + "... ")
|
||||
block[@configure_op]
|
||||
end
|
||||
end
|
||||
|
||||
# @return [String, nil]
|
||||
|
||||
+65
-13
@@ -1,4 +1,5 @@
|
||||
module Rscons
|
||||
# A collection of stand-alone utility methods.
|
||||
module Util
|
||||
class << self
|
||||
|
||||
@@ -15,6 +16,30 @@ module Rscons
|
||||
end
|
||||
end
|
||||
|
||||
# Colorize a builder run message.
|
||||
#
|
||||
# @param message [String]
|
||||
# Builder run message.
|
||||
#
|
||||
# @return [Array]
|
||||
# Colorized message with color codes for {Ansi} module.
|
||||
def colorize_markup(message)
|
||||
if message =~ /^(.*?)(<[^>]+>)(.*)$/
|
||||
prefix, code, suffix = $1, $2, $3
|
||||
case code
|
||||
when "<target>"
|
||||
code = :magenta
|
||||
when "<source>"
|
||||
code = :cyan
|
||||
when "<reset>"
|
||||
code = :reset
|
||||
end
|
||||
[prefix, code, *colorize_markup(suffix)].delete_if {|v| v == ""}
|
||||
else
|
||||
[message]
|
||||
end
|
||||
end
|
||||
|
||||
# Return a string representation of a command.
|
||||
#
|
||||
# @param command [Array<String>]
|
||||
@@ -95,6 +120,27 @@ module Rscons
|
||||
end
|
||||
end
|
||||
|
||||
# Format an elapsed time in human-readable format.
|
||||
#
|
||||
# @return [String]
|
||||
# Elapsed time in human-readable format.
|
||||
def format_elapsed_time(elapsed)
|
||||
hours = (elapsed / (60 * 60)).to_i
|
||||
elapsed -= hours * 60 * 60
|
||||
minutes = (elapsed / 60).to_i
|
||||
elapsed -= minutes * 60
|
||||
seconds = elapsed.ceil
|
||||
result = ""
|
||||
if hours > 0
|
||||
result += "#{hours}h "
|
||||
end
|
||||
if hours > 0 || minutes > 0
|
||||
result += "#{minutes}m "
|
||||
end
|
||||
result += "#{seconds}s"
|
||||
result
|
||||
end
|
||||
|
||||
# Make a relative path corresponding to a possibly absolute one.
|
||||
#
|
||||
# @param path [String]
|
||||
@@ -114,7 +160,7 @@ module Rscons
|
||||
end
|
||||
end
|
||||
|
||||
# Parse dependencies from a Makefile.
|
||||
# Parse dependencies from a Makefile or ldc2 dependency file.
|
||||
#
|
||||
# This method is used internally by Rscons builders.
|
||||
#
|
||||
@@ -123,7 +169,7 @@ module Rscons
|
||||
#
|
||||
# @return [Array<String>]
|
||||
# Paths of dependency files.
|
||||
def parse_makefile_deps(mf_fname)
|
||||
def parse_dependency_file(mf_fname)
|
||||
deps = []
|
||||
buildup = ''
|
||||
File.read(mf_fname).each_line do |line|
|
||||
@@ -131,7 +177,11 @@ module Rscons
|
||||
buildup += ' ' + $1
|
||||
else
|
||||
buildup += ' ' + line
|
||||
if buildup =~ /^.*: (.*)$/
|
||||
if buildup =~ /^[^:]+\(\S+\)\s*:.*?:[^:]+\((\S+)\)/
|
||||
# ldc2-style dependency line
|
||||
deps << $1
|
||||
elsif buildup =~ /^.*: (.*)$/
|
||||
# Makefile-style dependency line
|
||||
mf_deps = $1
|
||||
deps += mf_deps.split(' ').map(&:strip)
|
||||
end
|
||||
@@ -154,16 +204,18 @@ module Rscons
|
||||
File.file?(path) and File.executable?(path)
|
||||
end
|
||||
if RbConfig::CONFIG["host_os"] =~ /mswin|windows|mingw/i
|
||||
executable = executable.downcase
|
||||
dir_entries = Dir.entries(path_entry)
|
||||
dir_entries.find do |entry|
|
||||
path = "#{path_entry}/#{entry}"
|
||||
entry = entry.downcase
|
||||
if ((entry == executable) or
|
||||
(entry == "#{executable}.exe") or
|
||||
(entry == "#{executable}.com") or
|
||||
(entry == "#{executable}.bat")) and is_executable[path]
|
||||
return path
|
||||
if File.directory?(path_entry)
|
||||
executable = executable.downcase
|
||||
dir_entries = Dir.entries(path_entry)
|
||||
dir_entries.find do |entry|
|
||||
path = "#{path_entry}/#{entry}"
|
||||
entry = entry.downcase
|
||||
if ((entry == executable) or
|
||||
(entry == "#{executable}.exe") or
|
||||
(entry == "#{executable}.com") or
|
||||
(entry == "#{executable}.bat")) and is_executable[path]
|
||||
return path
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
+50
-23
@@ -11,11 +11,13 @@ module Rscons
|
||||
append(vars)
|
||||
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)
|
||||
if @my_vars.include?(key)
|
||||
@my_vars[key]
|
||||
@@ -30,11 +32,38 @@ module Rscons
|
||||
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.
|
||||
#
|
||||
# @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)
|
||||
@my_vars[key] = val
|
||||
end
|
||||
@@ -95,29 +124,27 @@ module Rscons
|
||||
# Expanded value with "$!{var}" variable references replaced.
|
||||
def expand_varref(varref, lambda_args)
|
||||
case varref
|
||||
when Symbol, true, false, nil
|
||||
varref
|
||||
when String
|
||||
if varref =~ /^(.*)\$\{([^}]+)\}(.*)$/
|
||||
prefix, varname, suffix = $1, $2, $3
|
||||
prefix = expand_varref(prefix, lambda_args) unless prefix.empty?
|
||||
varval = expand_varref(self[varname], lambda_args)
|
||||
varval = expand_varref(get_var(varname), lambda_args)
|
||||
# suffix needs no expansion since the regex matches the last occurence
|
||||
case varval
|
||||
when Symbol, true, false, nil, String
|
||||
if prefix.is_a?(Array)
|
||||
prefix.map {|p| "#{p}#{varval}#{suffix}"}
|
||||
else
|
||||
"#{prefix}#{varval}#{suffix}"
|
||||
end
|
||||
when Array
|
||||
if prefix.is_a?(Array)
|
||||
varval.map {|vv| prefix.map {|p| "#{p}#{vv}#{suffix}"}}.flatten
|
||||
else
|
||||
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} => #{self[varname].inspect})"
|
||||
raise "Unknown construction variable type: #{varval.class} (from #{varname.inspect} => #{get_var(varname).inspect})"
|
||||
end
|
||||
else
|
||||
varref
|
||||
@@ -126,6 +153,8 @@ module Rscons
|
||||
varref.map do |ent|
|
||||
expand_varref(ent, lambda_args)
|
||||
end.flatten
|
||||
when Symbol, true, false, nil
|
||||
varref
|
||||
when Proc
|
||||
expand_varref(varref[*lambda_args], lambda_args)
|
||||
else
|
||||
@@ -135,8 +164,6 @@ module Rscons
|
||||
|
||||
# Return a Hash containing all variables in the VarSet.
|
||||
#
|
||||
# @since 1.8.0
|
||||
#
|
||||
# This method is not terribly efficient. It is intended to be used only by
|
||||
# debugging code to dump out a VarSet's variables.
|
||||
#
|
||||
@@ -194,16 +221,16 @@ module Rscons
|
||||
#
|
||||
# @return [Object] Deep copied value.
|
||||
def deep_dup(obj)
|
||||
obj_class = obj.class
|
||||
if obj_class == Hash
|
||||
case obj
|
||||
when String
|
||||
obj.dup
|
||||
when Array
|
||||
obj.map { |v| deep_dup(v) }
|
||||
when Hash
|
||||
obj.reduce({}) do |result, (k, v)|
|
||||
result[k] = deep_dup(v)
|
||||
result
|
||||
end
|
||||
elsif obj_class == Array
|
||||
obj.map { |v| deep_dup(v) }
|
||||
elsif obj_class == String
|
||||
obj.dup
|
||||
else
|
||||
obj
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
module Rscons
|
||||
# Project version.
|
||||
VERSION = "1.16.0"
|
||||
VERSION = "2.0.2"
|
||||
end
|
||||
@@ -0,0 +1,82 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Rscons User Guide<%= subpage_title %> - Version <%= Rscons::VERSION %></title>
|
||||
<style>
|
||||
body {
|
||||
background-color: #CCC;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
#body-table {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
#body-left-border {
|
||||
width: 10px;
|
||||
background-image: linear-gradient(to right, #CCC, #000, #FFF);
|
||||
}
|
||||
#body-right-border {
|
||||
width: 10px;
|
||||
background-image: linear-gradient(to right, #FFF, #000, #CCC);
|
||||
}
|
||||
#body-center {
|
||||
background-color: #FFF;
|
||||
width: 120ex;
|
||||
padding: 1ex;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.separator {
|
||||
height: 2em;
|
||||
}
|
||||
.img_block_center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.page_nav {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
}
|
||||
.page_nav_toc {
|
||||
text-align: center;
|
||||
}
|
||||
.page_nav_next {
|
||||
text-align: right;
|
||||
}
|
||||
.code {
|
||||
padding-left: 2ex;
|
||||
width: 116ex;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.ruby_code .normal {}
|
||||
.ruby_code .comment { color: #005; font-style: italic; }
|
||||
.ruby_code .keyword { color: #A00; font-weight: bold; }
|
||||
.ruby_code .method { color: #077; }
|
||||
.ruby_code .class { color: #074; }
|
||||
.ruby_code .module { color: #050; }
|
||||
.ruby_code .punct { color: #447; font-weight: bold; }
|
||||
.ruby_code .symbol { color: #099; }
|
||||
.ruby_code .string { color: #090; }
|
||||
.ruby_code .char { color: #F07; }
|
||||
.ruby_code .ident { color: #004; }
|
||||
.ruby_code .constant { color: #07F; }
|
||||
.ruby_code .regex { color: #B66; }
|
||||
.ruby_code .number { color: #D55; }
|
||||
.ruby_code .attribute { color: #377; }
|
||||
.ruby_code .global { color: #3B7; }
|
||||
.ruby_code .expr { color: #227; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<table id="body-table">
|
||||
<tr>
|
||||
<td id="body-left-border"></td>
|
||||
<td id="body-center"><%= content %></td>
|
||||
<td id="body-right-border"></td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
+9
-10
@@ -1,17 +1,13 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "fileutils"
|
||||
require "digest/md5"
|
||||
|
||||
if File.read("lib/rscons/version.rb") =~ /VERSION = "(.+)"/
|
||||
VERSION = $1
|
||||
else
|
||||
raise "Could not determine version."
|
||||
end
|
||||
if `git show | head -n 1` =~ /commit\s+([0-9a-f]{7})/i
|
||||
GIT = $1
|
||||
else
|
||||
raise "Could not determine git revision."
|
||||
end
|
||||
PROG_NAME = "rscons"
|
||||
START_FILE = "bin/#{PROG_NAME}"
|
||||
LIB_DIR = "lib"
|
||||
@@ -63,19 +59,22 @@ combined_file.each do |line|
|
||||
end
|
||||
|
||||
require "zlib"
|
||||
require "base64"
|
||||
compressed_script = Zlib::Deflate.deflate(stripped_comments.join)
|
||||
escaped_compressed_script = compressed_script.gsub("#", "#1").gsub("\n", "#2").gsub("\r", "#3").gsub("\0", "#4")
|
||||
encoded_compressed_script = Base64.encode64(compressed_script).gsub("\n", "")
|
||||
hash = Digest::MD5.hexdigest(encoded_compressed_script)
|
||||
|
||||
FileUtils.mkdir_p(DIST)
|
||||
File.open("#{DIST}/#{PROG_NAME}", "wb", 0755) do |fh|
|
||||
fh.write(<<EOF)
|
||||
#!/usr/bin/env ruby
|
||||
script = ".rscons-#{VERSION}-#{GIT}.rb"
|
||||
script = File.join(File.dirname(__FILE__), ".rscons-#{VERSION}-#{hash}.rb")
|
||||
unless File.exists?(script)
|
||||
if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/
|
||||
escaped_compressed = $1
|
||||
unescaped_compressed = escaped_compressed.gsub("#4", "\\0").gsub("#3", "\\r").gsub("#2", "\\n").gsub("#1", "#")
|
||||
require "zlib"
|
||||
require "base64"
|
||||
encoded_compressed = $1
|
||||
unescaped_compressed = Base64.decode64(encoded_compressed)
|
||||
inflated = Zlib::Inflate.inflate(unescaped_compressed)
|
||||
File.open(script, "wb") do |fh|
|
||||
fh.write(inflated)
|
||||
@@ -88,6 +87,6 @@ load script
|
||||
if __FILE__ == $0
|
||||
Rscons::Cli.run(ARGV)
|
||||
end
|
||||
#==>#{escaped_compressed_script}
|
||||
#==>#{encoded_compressed_script}
|
||||
EOF
|
||||
end
|
||||
@@ -0,0 +1,229 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "erb"
|
||||
require "fileutils"
|
||||
require "redcarpet"
|
||||
require "syntax"
|
||||
require "syntax/convertors/html"
|
||||
require "rscons/version"
|
||||
|
||||
def load_file(file_name)
|
||||
contents = File.read(file_name)
|
||||
contents.gsub(/\$\{include (.*?)\}/) do |match|
|
||||
include_file_name = $1
|
||||
File.read(include_file_name)
|
||||
end
|
||||
end
|
||||
|
||||
class Generator
|
||||
class Section
|
||||
attr_reader :number
|
||||
attr_reader :title
|
||||
attr_reader :page
|
||||
attr_reader :contents
|
||||
attr_reader :anchor
|
||||
def initialize(number, title, page, anchor)
|
||||
@number = number
|
||||
@title = title.gsub(/[`]/, "")
|
||||
@page = page
|
||||
@anchor = anchor
|
||||
@contents = ""
|
||||
end
|
||||
def append(contents)
|
||||
@contents += contents
|
||||
end
|
||||
end
|
||||
|
||||
class Page
|
||||
attr_reader :name
|
||||
attr_reader :title
|
||||
attr_accessor :contents
|
||||
def initialize(name, title, contents = "")
|
||||
@name = name
|
||||
@title = title
|
||||
@contents = contents
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(input, output_file, multi_page)
|
||||
current_page =
|
||||
if multi_page
|
||||
nil
|
||||
else
|
||||
File.basename(output_file).sub(/\.html$/, "")
|
||||
end
|
||||
@sections = []
|
||||
@current_section_number = [0]
|
||||
@lines = input.lines
|
||||
while @lines.size > 0
|
||||
line = @lines.slice!(0)
|
||||
if line =~ /^```(.*)$/
|
||||
@sections.last.append(gather_code_section($1))
|
||||
elsif line.chomp == "${remove}"
|
||||
remove_section
|
||||
elsif line =~ /^(#+)(>)?\s*(.*)$/
|
||||
level_text, new_page_text, title_text = $1, $2, $3
|
||||
level = $1.size
|
||||
new_page = !new_page_text.nil?
|
||||
section_number = get_next_section_number(level)
|
||||
anchor = make_anchor(section_number, title_text)
|
||||
if multi_page and (new_page or current_page.nil?)
|
||||
current_page = anchor
|
||||
end
|
||||
@sections << Section.new(section_number, title_text, current_page, anchor)
|
||||
@sections.last.append("#{level_text} #{section_number} #{title_text}")
|
||||
elsif @sections.size > 0
|
||||
@sections.last.append(line)
|
||||
end
|
||||
end
|
||||
|
||||
renderer = Redcarpet::Render::HTML.new
|
||||
@markdown_renderer = Redcarpet::Markdown.new(renderer)
|
||||
changelog = @markdown_renderer.render(File.read("CHANGELOG.md"))
|
||||
|
||||
@pages = [Page.new("toc", "Table of Contents", render_toc)]
|
||||
@sections.each do |section|
|
||||
unless @pages.last.name == section.page
|
||||
@pages << Page.new(section.page, "#{section.number} #{section.title}")
|
||||
end
|
||||
@pages.last.contents += render_section(section)
|
||||
end
|
||||
|
||||
@pages.each do |page|
|
||||
page.contents.gsub!("${changelog}", changelog)
|
||||
page.contents.gsub!(%r{\$\{#(.+?)\}}) do |match|
|
||||
section_name = $1
|
||||
href = get_link_to_section(section_name)
|
||||
%[<a href="#{href}">#{section_name}</a>]
|
||||
end
|
||||
end
|
||||
|
||||
template = File.read("rb/assets/user_guide.html.erb")
|
||||
erb = ERB.new(template, nil, "<>")
|
||||
|
||||
if multi_page
|
||||
@pages.each_with_index do |page, page_index|
|
||||
subpage_title = " - #{page.title}"
|
||||
page_nav_bar = render_page_nav_bar(page_index)
|
||||
content = page_nav_bar + separator + page.contents + separator + page_nav_bar
|
||||
html_result = erb.result(binding.clone)
|
||||
File.open(File.join(output_file, "#{page.name}.html"), "w") do |fh|
|
||||
fh.write(html_result)
|
||||
end
|
||||
end
|
||||
else
|
||||
subpage_title = ""
|
||||
content = @pages.reduce("") do |result, page|
|
||||
result + page.contents
|
||||
end
|
||||
html_result = erb.result(binding.clone)
|
||||
File.open(output_file, "w") do |fh|
|
||||
fh.write(html_result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def separator
|
||||
%[<div class="separator"></div>]
|
||||
end
|
||||
|
||||
def render_page_nav_bar(page_index)
|
||||
page_nav_prev =
|
||||
if page_index > 1
|
||||
%[<a href="#{@pages[page_index - 1].name}.html">« Prev<br/>#{@pages[page_index - 1].title}</a>]
|
||||
else
|
||||
""
|
||||
end
|
||||
page_nav_toc =
|
||||
if page_index > 0
|
||||
%[<a href="toc.html">Table of Contents</a>]
|
||||
else
|
||||
""
|
||||
end
|
||||
page_nav_next =
|
||||
if page_index < @pages.size - 1
|
||||
%[<a href="#{@pages[page_index + 1].name}.html">Next »<br/>#{@pages[page_index + 1].title}</a>]
|
||||
else
|
||||
""
|
||||
end
|
||||
%[<table class="page_nav"><tr>] + \
|
||||
%[<td class="page_nav_prev">#{page_nav_prev}</td>] + \
|
||||
%[<td class="page_nav_toc">#{page_nav_toc}</td>] + \
|
||||
%[<td class="page_nav_next">#{page_nav_next}</td>] + \
|
||||
%[</tr></table>]
|
||||
end
|
||||
|
||||
def render_toc
|
||||
toc_content = %[<img class="img_block_center" src="rscons_logo_1000.png" />]
|
||||
toc_content += %[<h1>Table of Contents</h1>\n]
|
||||
@sections.each do |section|
|
||||
indent = section.number.split(".").size - 1
|
||||
toc_content += %[<span style="padding-left: #{4 * indent}ex;">]
|
||||
toc_content += %[<a href="#{section.page}.html##{section.anchor}">#{section.number} #{section.title}</a><br/>\n]
|
||||
toc_content += %[</span>]
|
||||
end
|
||||
toc_content
|
||||
end
|
||||
|
||||
def render_section(section)
|
||||
%[<a name="#{section.anchor}" />] + \
|
||||
@markdown_renderer.render(section.contents)
|
||||
end
|
||||
|
||||
def make_anchor(section_number, section_title)
|
||||
"s" + ("#{section_number} #{section_title}").gsub(/[^a-zA-Z0-9]/, "_")
|
||||
end
|
||||
|
||||
def get_link_to_section(section_name)
|
||||
section = @sections.find do |section|
|
||||
section.title == section_name
|
||||
end
|
||||
raise "Could not find section #{section_name}" unless section
|
||||
"#{section.page}.html##{section.anchor}"
|
||||
end
|
||||
|
||||
def gather_code_section(syntax)
|
||||
code = ""
|
||||
loop do
|
||||
line = @lines.slice!(0)
|
||||
if line =~ /^```/
|
||||
break
|
||||
end
|
||||
code += line
|
||||
end
|
||||
if syntax != ""
|
||||
convertor = Syntax::Convertors::HTML.for_syntax(syntax)
|
||||
%[<div class="code #{syntax}_code">\n#{convertor.convert(code)}\n</div>\n]
|
||||
else
|
||||
%[<div class="code">\n<pre>#{code}</pre>\n</div>\n]
|
||||
end
|
||||
end
|
||||
|
||||
def remove_section
|
||||
loop do
|
||||
line = @lines.slice!(0)
|
||||
if line.chomp == "${/remove}"
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def get_next_section_number(level)
|
||||
if @current_section_number.size == level - 1
|
||||
@current_section_number << 1
|
||||
elsif @current_section_number.size >= level
|
||||
@current_section_number[level - 1] += 1
|
||||
@current_section_number.slice!(level, @current_section_number.size)
|
||||
else
|
||||
raise "Section level change from #{@current_section_number.size} to #{level}"
|
||||
end
|
||||
@current_section_number.join(".")
|
||||
end
|
||||
end
|
||||
|
||||
input = load_file("doc/user_guide.md")
|
||||
FileUtils.rm_rf("gen/user_guide")
|
||||
FileUtils.mkdir_p("gen/user_guide")
|
||||
FileUtils.cp("img/rscons_logo_1000.png", "gen/user_guide")
|
||||
Generator.new(input, "gen/user_guide/user_guide.html", false)
|
||||
Generator.new(input, "gen/user_guide", true)
|
||||
+751
-301
File diff suppressed because it is too large.
Load diff
@@ -1,30 +0,0 @@
|
||||
module Rscons
|
||||
describe Application do
|
||||
|
||||
describe ".clean" do
|
||||
it "removes all build targets and created directories" do
|
||||
cache = "cache"
|
||||
expect(Rscons::Cache).to receive(:instance).and_return(cache)
|
||||
expect(cache).to receive(:targets).and_return(["build/a.out", "build/main.o"])
|
||||
expect(FileUtils).to receive(:rm_f).with("build/a.out")
|
||||
expect(FileUtils).to receive(:rm_f).with("build/main.o")
|
||||
expect(cache).to receive(:directories).and_return(["build/one", "build/one/two", "build", "other"])
|
||||
expect(File).to receive(:directory?).with("build/one/two").and_return(true)
|
||||
expect(Dir).to receive(:entries).with("build/one/two").and_return([".", ".."])
|
||||
expect(Dir).to receive(:rmdir).with("build/one/two")
|
||||
expect(File).to receive(:directory?).with("build/one").and_return(true)
|
||||
expect(Dir).to receive(:entries).with("build/one").and_return([".", ".."])
|
||||
expect(Dir).to receive(:rmdir).with("build/one")
|
||||
expect(File).to receive(:directory?).with("build").and_return(true)
|
||||
expect(Dir).to receive(:entries).with("build").and_return([".", ".."])
|
||||
expect(Dir).to receive(:rmdir).with("build")
|
||||
expect(File).to receive(:directory?).with("other").and_return(true)
|
||||
expect(Dir).to receive(:entries).with("other").and_return([".", "..", "other.file"])
|
||||
expect(cache).to receive(:clear)
|
||||
|
||||
Rscons.application.__send__(:clean)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -13,13 +13,6 @@ module Rscons
|
||||
end
|
||||
end
|
||||
|
||||
describe "#targets" do
|
||||
it "returns a list of targets that are cached" do
|
||||
cache = {"targets" => {"t1" => {}, "t2" => {}, "t3" => {}}}
|
||||
expect(build_from(cache).targets).to eq ["t1", "t2", "t3"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#mkdir_p" do
|
||||
it "makes directories and records any created in the cache" do
|
||||
_cache = {}
|
||||
@@ -35,7 +28,7 @@ module Rscons
|
||||
expect(FileUtils).to receive(:mkdir_p).with("one/two/four")
|
||||
cache.mkdir_p("one/two/three")
|
||||
cache.mkdir_p("one\\two\\four")
|
||||
expect(cache.directories).to eq ["one/two", "one/two/three", "one/two/four"]
|
||||
expect(cache.directories(false)).to eq ["one/two", "one/two/three", "one/two/four"]
|
||||
end
|
||||
|
||||
it "handles absolute paths" do
|
||||
@@ -45,14 +38,7 @@ module Rscons
|
||||
expect(File).to receive(:exists?).with("/one/two").and_return(false)
|
||||
expect(FileUtils).to receive(:mkdir_p).with("/one/two")
|
||||
cache.mkdir_p("/one/two")
|
||||
expect(cache.directories).to eq ["/one/two"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "#directories" do
|
||||
it "returns a list of directories that are cached" do
|
||||
_cache = {"directories" => {"dir1" => true, "dir2" => true}}
|
||||
expect(build_from(_cache).directories).to eq ["dir1", "dir2"]
|
||||
expect(cache.directories(false)).to eq ["/one/two"]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -79,12 +65,5 @@ module Rscons
|
||||
expect(build_from({}).send(:calculate_checksum, "fname")).to eq "the_checksum"
|
||||
end
|
||||
end
|
||||
|
||||
describe "#validate_json_object" do
|
||||
it "raises an error when an unexpected object is passed" do
|
||||
cache = build_from({})
|
||||
expect {cache.__send__(:validate_json_object, :symbol)}.to raise_error /Unexpected cache value/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -29,6 +29,12 @@ module Rscons
|
||||
end
|
||||
end
|
||||
|
||||
describe ".colorize_markup" do
|
||||
it "turns a builder message string with markup into an Array for Ansi module" do
|
||||
expect(Util.colorize_markup("[ 1/33] Generating <foo> lexer from <source>parser/lexer.l<reset> => <target>build/e.1/parser/lexer.cc<reset>")).to eq ["[ 1/33] Generating ", "<foo>", " lexer from ", :cyan, "parser/lexer.l", :reset, " => ", :magenta, "build/e.1/parser/lexer.cc", :reset]
|
||||
end
|
||||
end
|
||||
|
||||
describe ".determine_n_threads" do
|
||||
context "when specified by environment variable" do
|
||||
before(:each) do
|
||||
@@ -115,7 +121,10 @@ EOF
|
||||
before(:each) do
|
||||
stub_const("File::PATH_SEPARATOR", ";")
|
||||
stub_const("RbConfig::CONFIG", "host_os" => "mingw")
|
||||
expect(ENV).to receive(:[]).with("PATH").and_return("C:\\bin;C:\\Windows")
|
||||
expect(ENV).to receive(:[]).with("PATH").and_return("C:\\none;C:\\bin;C:\\Windows")
|
||||
allow(File).to receive(:directory?).with("C:\\none").and_return(false)
|
||||
allow(File).to receive(:directory?).with("C:\\bin").and_return(true)
|
||||
allow(File).to receive(:directory?).with("C:\\Windows").and_return(true)
|
||||
allow(Dir).to receive(:entries).with("C:\\bin").and_return(%w[one.com])
|
||||
allow(Dir).to receive(:entries).with("C:\\Windows").and_return(%w[two.exe Three.bat])
|
||||
end
|
||||
@@ -151,6 +160,20 @@ EOF
|
||||
end
|
||||
end
|
||||
|
||||
describe ".format_elapsed_time" do
|
||||
it "includes minutes when hours is nonzero and minutes is zero" do
|
||||
expect(Util.format_elapsed_time(60 * 60 + 4)).to eq "1h 0m 4s"
|
||||
end
|
||||
|
||||
it "only includes minutes and seconds when hours is zero and minutes is nonzero" do
|
||||
expect(Util.format_elapsed_time(3 * 60 + 33)).to eq "3m 33s"
|
||||
end
|
||||
|
||||
it "only includes seconds when hours and minutes are zero" do
|
||||
expect(Util.format_elapsed_time(7.84)).to eq "8s"
|
||||
end
|
||||
end
|
||||
|
||||
describe ".make_relative_path" do
|
||||
context "when passed a relative path" do
|
||||
it "returns the path itself" do
|
||||
@@ -177,12 +200,12 @@ EOF
|
||||
end
|
||||
end
|
||||
|
||||
describe ".parse_makefile_deps" do
|
||||
describe ".parse_dependency_file" do
|
||||
it 'handles dependencies on one line' do
|
||||
expect(File).to receive(:read).with('makefile').and_return(<<EOS)
|
||||
module.o: source.cc
|
||||
EOS
|
||||
expect(Util.parse_makefile_deps('makefile')).to eq ['source.cc']
|
||||
expect(Util.parse_dependency_file('makefile')).to eq ['source.cc']
|
||||
end
|
||||
|
||||
it 'handles dependencies split across many lines' do
|
||||
@@ -191,9 +214,22 @@ module.o: module.c \\
|
||||
module.h \\
|
||||
other.h
|
||||
EOS
|
||||
expect(Util.parse_makefile_deps('makefile')).to eq [
|
||||
expect(Util.parse_dependency_file('makefile')).to eq [
|
||||
'module.c', 'module.h', 'other.h']
|
||||
end
|
||||
|
||||
it "parses ldc2-style dependency lines" do
|
||||
expect(File).to receive(:read).with("deps.out").and_return(<<EOS)
|
||||
std.traits (/usr/lib/ldc/x86_64-linux-gnu/include/d/std/traits.d) : private : std.meta (/usr/lib/ldc/x86_64-linux-gnu/include/d/std/meta.d):staticIndexOf
|
||||
std.conv (/usr/lib/ldc/x86_64-linux-gnu/include/d/std/conv.d) : private : std.array (/usr/lib/ldc/x86_64-linux-gnu/include/d/std/array.d):array
|
||||
std.stdio (/usr/lib/ldc/x86_64-linux-gnu/include/d/std/stdio.d) : private static : core.sys.posix.sys.socket (/usr/lib/ldc/x86_64-linux-gnu/include/d/core/sys/posix/sys/socket.d) -> sock
|
||||
EOS
|
||||
expect(Util.parse_dependency_file("deps.out")).to eq [
|
||||
"/usr/lib/ldc/x86_64-linux-gnu/include/d/std/meta.d",
|
||||
"/usr/lib/ldc/x86_64-linux-gnu/include/d/std/array.d",
|
||||
"/usr/lib/ldc/x86_64-linux-gnu/include/d/core/sys/posix/sys/socket.d",
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -169,7 +169,7 @@ module Rscons
|
||||
expect { v.expand_varref({a: :b}, :lambda_args) }.to raise_error /Unknown construction variable type: Hash/
|
||||
end
|
||||
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).and_call_original
|
||||
expect { v.expand_varref("${bad}", :lambda_args) }.to raise_error /Unknown construction variable type: Hash/
|
||||
|
||||
@@ -1,15 +1,5 @@
|
||||
describe Rscons do
|
||||
|
||||
describe ".set_suffix" do
|
||||
it "changes the suffix to the new one" do
|
||||
expect(Rscons.set_suffix("foo.c", ".h")).to eq("foo.h")
|
||||
end
|
||||
|
||||
it "appends a suffix if the given file name does not have one" do
|
||||
expect(Rscons.set_suffix("bazz", ".d")).to eq("bazz.d")
|
||||
end
|
||||
end
|
||||
|
||||
describe ".get_system_shell" do
|
||||
before(:each) do
|
||||
Rscons.instance_variable_set(:@shell, nil)
|
||||
|
||||
Reference in new issue
Block a user