Compare commits
115
Commits
784bbd471b
..
v2.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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/
|
/build_test_run/
|
||||||
/coverage/
|
/coverage/
|
||||||
/dist/
|
/dist/
|
||||||
/doc/
|
/gen/
|
||||||
|
/large_project/
|
||||||
/pkg/
|
/pkg/
|
||||||
/test/
|
/test/
|
||||||
|
/yard/
|
||||||
+104
-70
@@ -1,80 +1,114 @@
|
|||||||
## ChangeLog
|
## v2.0.0
|
||||||
|
|
||||||
### v1.17.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
|
||||||
|
|
||||||
#### New Features
|
## v1.17.0
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
- allow construction variable expansion on `true` and `false` values.
|
- allow construction variable expansion on `true` and `false` values.
|
||||||
- remove makefile target name check when parsing dependencies
|
- remove makefile target name check when parsing dependencies
|
||||||
|
|
||||||
### v1.16.0
|
## v1.16.0
|
||||||
|
|
||||||
#### New Features
|
### New Features
|
||||||
|
|
||||||
- Add `Rscons.glob`
|
- Add `Rscons.glob`
|
||||||
- Support command-line variables
|
- 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
|
- allow passing a VarSet into cache methods
|
||||||
|
|
||||||
#### Fixes
|
### Fixes
|
||||||
|
|
||||||
- generate dependencies for D builds
|
- generate dependencies for D builds
|
||||||
|
|
||||||
### v1.15.0
|
## v1.15.0
|
||||||
|
|
||||||
- allow json 1.x or 2.x
|
- 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
|
- #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
|
- #41 - be more colorful
|
||||||
|
|
||||||
#### Fixes
|
### Fixes
|
||||||
|
|
||||||
- #39 - wait for in-progress subcommands to complete on build failure
|
- #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
|
- 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
|
- Change default Environment :clone option to :all to clone all attributes
|
||||||
- #38 - raise error when circular dependencies are found
|
- #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
|
- #35 - `env.build_after` should expand paths
|
||||||
- #36 - SHCFLAGS and SHCXXFLAGS should inherit non-SH flags by default
|
- #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
|
- #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
|
- #23 - add parallelization - builds are now parallelized by default
|
||||||
- #31 - add LEXSUFFIX, YACCSUFFIX construction variables
|
- #31 - add LEXSUFFIX, YACCSUFFIX construction variables
|
||||||
@@ -84,100 +118,100 @@
|
|||||||
- Add builder features
|
- Add builder features
|
||||||
- #8 - add SharedObject and SharedLibrary builders
|
- #8 - add SharedObject and SharedLibrary builders
|
||||||
|
|
||||||
#### Fixes
|
### Fixes
|
||||||
|
|
||||||
- expand target and source paths before calling Builder#create_build_target
|
- expand target and source paths before calling `Builder#create_build_target`
|
||||||
- #29 - fix PROGSUFFIX handling
|
- #29 - fix `PROGSUFFIX` handling
|
||||||
- #32 - Pre-build hooks do not respect modified key values
|
- #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
|
- allow phony targets in conjunction with build roots
|
||||||
|
|
||||||
### v1.9.1
|
## v1.9.1
|
||||||
|
|
||||||
- change *SUFFIX defaults to arrays
|
- change *SUFFIX defaults to arrays
|
||||||
- add various C++ file suffixes
|
- add various C++ file suffixes
|
||||||
- use ${INCPREFIX} instead of hard-coded "-I" in Preprocess builder
|
- 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
|
- #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
|
- #24 - add "rscons" executable
|
||||||
- #25 - add support for phony targets given as Symbols instead of Strings
|
- #25 - add support for phony targets given as Symbols instead of Strings
|
||||||
- #26 - support registering multiple build targets with the same target name
|
- #26 - support registering multiple build targets with the same target name
|
||||||
- #27 - add Directory builder
|
- #27 - add Directory builder
|
||||||
|
|
||||||
#### Fixes
|
### Fixes
|
||||||
|
|
||||||
- #20 - fix variable references that expand to arrays in build target sources
|
- #20 - fix variable references that expand to arrays in build target sources
|
||||||
- #21 - rework Preprocess builder to consider deep dependencies
|
- #21 - rework Preprocess builder to consider deep dependencies
|
||||||
- fix Rscons.set_suffix to append the given suffix if the filename has none
|
- fix `Rscons.set_suffix` to append the given suffix if the filename has none
|
||||||
- remove ${CFLAGS} from default CPP_CMD
|
- remove ${CFLAGS} from default `CPP_CMD`
|
||||||
|
|
||||||
### v1.8.1
|
## v1.8.1
|
||||||
|
|
||||||
- fix Environment#dump when construction variables are symbols
|
- fix Environment#dump when construction variables are symbols
|
||||||
|
|
||||||
### v1.8.0
|
## v1.8.0
|
||||||
|
|
||||||
- new Command builder to execute arbitrary user commands
|
- new Command builder to execute arbitrary user commands
|
||||||
- new SimpleBuilder class
|
- 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
|
- improved YARD documentation
|
||||||
- add Environment#dump to debug Environment construction variables
|
- add Environment#dump to debug Environment construction variables
|
||||||
|
|
||||||
### v1.7.0
|
## v1.7.0
|
||||||
|
|
||||||
- allow build hooks to register new build targets
|
- 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
|
- 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
|
- add DEPFILESUFFIX construction variable to override dependency file suffix
|
||||||
- fix Environment#depends to expand its arguments for construction variables
|
- fix Environment#depends to expand its arguments for construction variables
|
||||||
|
|
||||||
### v1.6.0
|
## v1.6.0
|
||||||
|
|
||||||
- support lambdas as construction variable values
|
- support lambdas as construction variable values
|
||||||
|
|
||||||
### v1.5.0
|
## v1.5.0
|
||||||
|
|
||||||
- add "json" as a runtime dependency
|
- add "json" as a runtime dependency
|
||||||
- update construction variables to match SCons more closely
|
- update construction variables to match SCons more closely
|
||||||
- add CPPDEFPREFIX, INCPREFIX, CPPDEFINES, CCFLAGS, LIBDIRPREFIX, and LIBLINKPREFIX
|
- add `CPPDEFPREFIX`, `INCPREFIX`, `CPPDEFINES`, `CCFLAGS`, `LIBDIRPREFIX`, and `LIBLINKPREFIX`
|
||||||
- add Environment#shell
|
- add `Environment#shell`
|
||||||
- add Environment#parse_flags, #parse_flags!, #merge_flags
|
- add `Environment#parse_flags`, `#parse_flags!`, `#merge_flags`
|
||||||
- unbuffer $stdout by default
|
- unbuffer `$stdout` by default
|
||||||
- add PROGSUFFIX construction variable (defaults to .exe on MinGW/Cygwin)
|
- add `PROGSUFFIX` construction variable (defaults to `.exe` on MinGW/Cygwin)
|
||||||
- add Rscons::BuildTarget and Builder#create_build_target
|
- add `Rscons::BuildTarget` and `Builder#create_build_target`
|
||||||
- update specs to RSpec 3.x and fix to run on MinGW/Cygwin/Linux
|
- update specs to RSpec 3.x and fix to run on MinGW/Cygwin/Linux
|
||||||
- add YARD documentation to get to 100% coverage
|
- add YARD documentation to get to 100% coverage
|
||||||
|
|
||||||
### v1.4.3
|
## v1.4.3
|
||||||
|
|
||||||
- fix builders properly using construction variable overrides
|
- fix builders properly using construction variable overrides
|
||||||
- expand nil construction variables to empty strings
|
- 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
|
- 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
|
- fix invoking a builder with no sources while a build root defined
|
||||||
|
|
||||||
### v1.4.0
|
## v1.4.0
|
||||||
|
|
||||||
- add CFile builder
|
- add CFile builder
|
||||||
- add Disassemble builder
|
- add Disassemble builder
|
||||||
@@ -190,21 +224,21 @@
|
|||||||
- implement copy-on-write semantics for construction variables when cloning Environments
|
- implement copy-on-write semantics for construction variables when cloning Environments
|
||||||
- only load the cache once instead of on each Environment#process
|
- only load the cache once instead of on each Environment#process
|
||||||
- only write the cache when something has changed
|
- 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)
|
- 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:
|
- change Environment#execute() options parameter to accept the following options keys:
|
||||||
- :env to pass an environment Hash to Kernel#system
|
- :env to pass an environment Hash to Kernel#system
|
||||||
- :options to pass an options 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
|
- 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
|
file or an array of target file names
|
||||||
@@ -6,3 +6,5 @@ gem "rake"
|
|||||||
gem "simplecov"
|
gem "simplecov"
|
||||||
gem "yard"
|
gem "yard"
|
||||||
gem "rdoc"
|
gem "rdoc"
|
||||||
|
gem "redcarpet"
|
||||||
|
gem "syntax"
|
||||||
+21
-17
@@ -2,29 +2,31 @@ GEM
|
|||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
diff-lcs (1.3)
|
diff-lcs (1.3)
|
||||||
docile (1.1.5)
|
docile (1.3.2)
|
||||||
json (2.1.0)
|
json (2.2.0)
|
||||||
rake (12.3.1)
|
rake (12.3.2)
|
||||||
rdoc (5.1.0)
|
rdoc (6.1.1)
|
||||||
rspec (3.6.0)
|
redcarpet (3.4.0)
|
||||||
rspec-core (~> 3.6.0)
|
rspec (3.8.0)
|
||||||
rspec-expectations (~> 3.6.0)
|
rspec-core (~> 3.8.0)
|
||||||
rspec-mocks (~> 3.6.0)
|
rspec-expectations (~> 3.8.0)
|
||||||
rspec-core (3.6.0)
|
rspec-mocks (~> 3.8.0)
|
||||||
rspec-support (~> 3.6.0)
|
rspec-core (3.8.2)
|
||||||
rspec-expectations (3.6.0)
|
rspec-support (~> 3.8.0)
|
||||||
|
rspec-expectations (3.8.4)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.6.0)
|
rspec-support (~> 3.8.0)
|
||||||
rspec-mocks (3.6.0)
|
rspec-mocks (3.8.1)
|
||||||
diff-lcs (>= 1.2.0, < 2.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rspec-support (~> 3.6.0)
|
rspec-support (~> 3.8.0)
|
||||||
rspec-support (3.6.0)
|
rspec-support (3.8.2)
|
||||||
simplecov (0.15.0)
|
simplecov (0.15.0)
|
||||||
docile (~> 1.1.0)
|
docile (~> 1.1.0)
|
||||||
json (>= 1.8, < 3)
|
json (>= 1.8, < 3)
|
||||||
simplecov-html (~> 0.10.0)
|
simplecov-html (~> 0.10.0)
|
||||||
simplecov-html (0.10.2)
|
simplecov-html (0.10.2)
|
||||||
yard (0.9.12)
|
syntax (1.2.2)
|
||||||
|
yard (0.9.20)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
@@ -34,9 +36,11 @@ DEPENDENCIES
|
|||||||
json
|
json
|
||||||
rake
|
rake
|
||||||
rdoc
|
rdoc
|
||||||
|
redcarpet
|
||||||
rspec
|
rspec
|
||||||
simplecov
|
simplecov
|
||||||
|
syntax
|
||||||
yard
|
yard
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
1.16.2
|
1.17.3
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2013 Josh Holtrop
|
Copyright (c) 2013-2019 Josh Holtrop
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
|
|||||||
@@ -1,591 +1,10 @@
|
|||||||
# Rscons
|
# 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
|
Rscons is written in Ruby, and is inspired by [SCons](https://scons.org/) and [waf](https://waf.io/).
|
||||||
(containing the complete rscons build system) can be checked into your project
|
|
||||||
and versioned along with your project.
|
|
||||||
|
|
||||||
## Usage
|
See [https://holtrop.github.io/rscons/index.html](https://holtrop.github.io/rscons/index.html) for User Guide and Installation instructions.
|
||||||
|
|
||||||
### 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
|
|
||||||
+60
-2
@@ -10,8 +10,8 @@ require "yard"
|
|||||||
require "rake/clean"
|
require "rake/clean"
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
|
|
||||||
CLEAN.include %w[build_test_run .yardoc doc coverage test]
|
CLEAN.include %w[build_test_run .yardoc yard coverage test]
|
||||||
CLOBBER.include %w[dist pkg]
|
CLOBBER.include %w[dist gen large_project pkg]
|
||||||
|
|
||||||
task :build_dist do
|
task :build_dist do
|
||||||
sh "ruby rb/build_dist.rb"
|
sh "ruby rb/build_dist.rb"
|
||||||
@@ -39,6 +39,64 @@ end
|
|||||||
|
|
||||||
YARD::Rake::YardocTask.new do |yard|
|
YARD::Rake::YardocTask.new do |yard|
|
||||||
yard.files = ['lib/**/*.rb']
|
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
|
end
|
||||||
|
|
||||||
task :default => :spec
|
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
|
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
|
end
|
||||||
|
|
||||||
build do
|
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;
|
||||||
|
}
|
||||||
@@ -7,10 +7,10 @@ build do
|
|||||||
env.depends("${build_root}/program.o", "${inc_h}")
|
env.depends("${build_root}/program.o", "${inc_h}")
|
||||||
env.Program("program.exe", ["program.c", "inc.c"])
|
env.Program("program.exe", ["program.c", "inc.c"])
|
||||||
|
|
||||||
env.Command("inc.c",
|
inc_c = env.Command("inc.c",
|
||||||
[],
|
[],
|
||||||
"CMD" => %w[ruby gen.rb ${_TARGET}],
|
"CMD" => %w[ruby gen.rb ${_TARGET}],
|
||||||
"CMD_DESC" => "Generating")
|
"CMD_DESC" => "Generating")
|
||||||
env.produces("inc.c", "inc.h")
|
inc_c.produces("inc.h")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
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("${build_root}/program.o", "${inc_h}")
|
||||||
|
env.Program("program.exe", ["program.c", "inc.c"])
|
||||||
|
|
||||||
|
env.Command("inc.c",
|
||||||
|
[],
|
||||||
|
"CMD" => %w[ruby gen.rb ${_TARGET}],
|
||||||
|
"CMD_DESC" => "Generating")
|
||||||
|
env.produces("inc.c", "inc.h")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,3 +1,7 @@
|
|||||||
|
configure do
|
||||||
|
check_d_compiler "gdc"
|
||||||
|
end
|
||||||
|
|
||||||
build do
|
build do
|
||||||
Environment.new(echo: :command) do |env|
|
Environment.new(echo: :command) do |env|
|
||||||
env.Program("hello-d.exe", glob("*.d"))
|
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;
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
build do
|
build do
|
||||||
Environment.new do |env|
|
Environment.new do |env|
|
||||||
env["LIBS"] += ["c"]
|
env["LIBS"] += ["m"]
|
||||||
env.Program('simple.exe', Dir['*.c'])
|
env.Program('simple.exe', Dir['*.c'])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
build do
|
||||||
|
Environment.new do |env|
|
||||||
|
env.produces("foo", "bar")
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,5 +1,14 @@
|
|||||||
|
project_name "install_test"
|
||||||
|
|
||||||
build do
|
build do
|
||||||
Environment.new do |env|
|
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
|
||||||
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
-1
@@ -26,6 +26,7 @@ module Rscons
|
|||||||
:Directory,
|
:Directory,
|
||||||
:Disassemble,
|
:Disassemble,
|
||||||
:Install,
|
:Install,
|
||||||
|
:InstallDirectory,
|
||||||
:Library,
|
:Library,
|
||||||
:Object,
|
:Object,
|
||||||
:Preprocess,
|
:Preprocess,
|
||||||
@@ -145,9 +146,9 @@ require_relative "rscons/builders/mixins/program"
|
|||||||
# default builders
|
# default builders
|
||||||
require_relative "rscons/builders/cfile"
|
require_relative "rscons/builders/cfile"
|
||||||
require_relative "rscons/builders/command"
|
require_relative "rscons/builders/command"
|
||||||
|
require_relative "rscons/builders/copy"
|
||||||
require_relative "rscons/builders/directory"
|
require_relative "rscons/builders/directory"
|
||||||
require_relative "rscons/builders/disassemble"
|
require_relative "rscons/builders/disassemble"
|
||||||
require_relative "rscons/builders/install"
|
|
||||||
require_relative "rscons/builders/library"
|
require_relative "rscons/builders/library"
|
||||||
require_relative "rscons/builders/object"
|
require_relative "rscons/builders/object"
|
||||||
require_relative "rscons/builders/preprocess"
|
require_relative "rscons/builders/preprocess"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module Rscons
|
|||||||
module Ansi
|
module Ansi
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
|
# ANSI terminal reset sequence.
|
||||||
RESET = "\e[0m"
|
RESET = "\e[0m"
|
||||||
|
|
||||||
# Write a message to an IO with ANSI escape codes.
|
# Write a message to an IO with ANSI escape codes.
|
||||||
|
|||||||
+94
-28
@@ -1,3 +1,5 @@
|
|||||||
|
require "set"
|
||||||
|
|
||||||
module Rscons
|
module Rscons
|
||||||
|
|
||||||
# Functionality for an instance of the rscons application invocation.
|
# Functionality for an instance of the rscons application invocation.
|
||||||
@@ -23,6 +25,19 @@ module Rscons
|
|||||||
def initialize
|
def initialize
|
||||||
@n_threads = Util.determine_n_threads
|
@n_threads = Util.determine_n_threads
|
||||||
@vars = VarSet.new
|
@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
|
end
|
||||||
|
|
||||||
# Run the specified operation.
|
# Run the specified operation.
|
||||||
@@ -33,35 +48,73 @@ module Rscons
|
|||||||
# The script.
|
# The script.
|
||||||
# @param operation_options [Hash]
|
# @param operation_options [Hash]
|
||||||
# Option values from the CLI for the operation.
|
# 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]
|
# @return [Integer]
|
||||||
# Process exit code (0 on success).
|
# 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
|
@script = script
|
||||||
|
@operations << operation
|
||||||
|
puts "Starting '#{operation}' at #{Time.new}" if verbose
|
||||||
|
rv =
|
||||||
case operation
|
case operation
|
||||||
when "build"
|
when "build"
|
||||||
|
rv = 0
|
||||||
unless Cache.instance["configuration_data"]["configured"]
|
unless Cache.instance["configuration_data"]["configured"]
|
||||||
|
rv =
|
||||||
if @script.autoconf
|
if @script.autoconf
|
||||||
rv = run("configure", script, operation_options)
|
run("configure", script, operation_options, sub_op: false)
|
||||||
if rv != 0
|
|
||||||
return rv
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
$stderr.puts "Project must be configured first, and autoconf is disabled"
|
$stderr.puts "Project must be configured first, and autoconf is disabled"
|
||||||
return 1
|
1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if rv == 0
|
||||||
build(operation_options)
|
build(operation_options)
|
||||||
|
end
|
||||||
when "clean"
|
when "clean"
|
||||||
clean
|
clean
|
||||||
when "configure"
|
when "configure"
|
||||||
configure(operation_options)
|
configure(operation_options)
|
||||||
when "distclean"
|
when "distclean"
|
||||||
distclean
|
distclean
|
||||||
|
when "install"
|
||||||
|
run("build", script, operation_options, sub_op: false)
|
||||||
|
when "uninstall"
|
||||||
|
uninstall
|
||||||
else
|
else
|
||||||
$stderr.puts "Unknown operation: #{operation}"
|
$stderr.puts "Unknown operation: #{operation}"
|
||||||
1
|
1
|
||||||
end
|
end
|
||||||
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -94,17 +147,19 @@ module Rscons
|
|||||||
def clean
|
def clean
|
||||||
cache = Cache.instance
|
cache = Cache.instance
|
||||||
# remove all built files
|
# remove all built files
|
||||||
cache.targets.each do |target|
|
cache.targets(false).each do |target|
|
||||||
|
cache.remove_target(target)
|
||||||
FileUtils.rm_f(target)
|
FileUtils.rm_f(target)
|
||||||
end
|
end
|
||||||
# remove all created directories if they are empty
|
# 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)
|
next unless File.directory?(directory)
|
||||||
if (Dir.entries(directory) - ['.', '..']).empty?
|
if (Dir.entries(directory) - ['.', '..']).empty?
|
||||||
Dir.rmdir(directory) rescue nil
|
Dir.rmdir(directory) rescue nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
cache.clear
|
cache.write
|
||||||
0
|
0
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -116,7 +171,9 @@ module Rscons
|
|||||||
cache = Cache.instance
|
cache = Cache.instance
|
||||||
build_dir = cache["configuration_data"]["build_dir"]
|
build_dir = cache["configuration_data"]["build_dir"]
|
||||||
clean
|
clean
|
||||||
|
if build_dir
|
||||||
FileUtils.rm_rf(build_dir)
|
FileUtils.rm_rf(build_dir)
|
||||||
|
end
|
||||||
cache.clear
|
cache.clear
|
||||||
0
|
0
|
||||||
end
|
end
|
||||||
@@ -129,34 +186,43 @@ module Rscons
|
|||||||
# @return [Integer]
|
# @return [Integer]
|
||||||
# Exit code.
|
# Exit code.
|
||||||
def configure(options)
|
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
|
rv = 0
|
||||||
co = ConfigureOp.new("#{options[:build_dir]}/configure")
|
options = options.merge(project_name: @script.project_name)
|
||||||
|
co = ConfigureOp.new(options)
|
||||||
begin
|
begin
|
||||||
@script.configure(co)
|
@script.configure(co)
|
||||||
rescue ConfigureOp::ConfigureFailure
|
rescue ConfigureOp::ConfigureFailure
|
||||||
rv = 1
|
rv = 1
|
||||||
end
|
end
|
||||||
co.close
|
co.close(rv == 0)
|
||||||
cache["configuration_data"]["build_dir"] = options[:build_dir]
|
|
||||||
cache["configuration_data"]["prefix"] = options[:prefix]
|
|
||||||
cache["configuration_data"]["configured"] = rv == 0
|
|
||||||
cache.write
|
|
||||||
rv
|
rv
|
||||||
end
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -4,27 +4,52 @@ module Rscons
|
|||||||
|
|
||||||
# Create a BasicEnvironment object.
|
# Create a BasicEnvironment object.
|
||||||
#
|
#
|
||||||
# @api private
|
|
||||||
#
|
|
||||||
# @param options [Hash]
|
# @param options [Hash]
|
||||||
# Construction options.
|
# 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 = {})
|
def initialize(options = {})
|
||||||
@varset = VarSet.new(Rscons::DEFAULT_CONSTRUCTION_VARIABLES)
|
@varset = VarSet.new(Rscons::DEFAULT_CONSTRUCTION_VARIABLES)
|
||||||
load_configuration_data!(options)
|
load_configuration_data!(options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Get a construction variable's value.
|
# Access the value of a construction variable.
|
||||||
#
|
#
|
||||||
# @see VarSet#[]
|
# @param key [String, Symbol]
|
||||||
def [](*args)
|
# The construction variable name.
|
||||||
@varset.__send__(:[], *args)
|
#
|
||||||
|
# @return [Object]
|
||||||
|
# The construction variable's value.
|
||||||
|
def [](key)
|
||||||
|
@varset[key]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Set a construction variable's value.
|
# Access the value of a construction variable.
|
||||||
#
|
#
|
||||||
# @see VarSet#[]=
|
# This method is similar to #[] but does not make a copy-on-access copy of
|
||||||
def []=(*args)
|
# the variable accessed. This means that the returned value is NOT safe to
|
||||||
@varset.__send__(:[]=, *args)
|
# 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
|
end
|
||||||
|
|
||||||
# Add a set of construction variables to the BasicEnvironment.
|
# Add a set of construction variables to the BasicEnvironment.
|
||||||
@@ -108,7 +133,7 @@ module Rscons
|
|||||||
append["LDFLAGS", ["-arch", val]]
|
append["LDFLAGS", ["-arch", val]]
|
||||||
end
|
end
|
||||||
skip = true
|
skip = true
|
||||||
elsif word =~ /^#{self["CPPDEFPREFIX"]}(.*)$/
|
elsif word =~ /^#{get_var("CPPDEFPREFIX")}(.*)$/
|
||||||
handle["CPPDEFINES", $1]
|
handle["CPPDEFINES", $1]
|
||||||
elsif word == "-include"
|
elsif word == "-include"
|
||||||
if val = words[i + 1]
|
if val = words[i + 1]
|
||||||
@@ -121,11 +146,11 @@ module Rscons
|
|||||||
append["LDFLAGS", ["-isysroot", val]]
|
append["LDFLAGS", ["-isysroot", val]]
|
||||||
end
|
end
|
||||||
skip = true
|
skip = true
|
||||||
elsif word =~ /^#{self["INCPREFIX"]}(.*)$/
|
elsif word =~ /^#{get_var("INCPREFIX")}(.*)$/
|
||||||
handle["CPPPATH", $1]
|
handle["CPPPATH", $1]
|
||||||
elsif word =~ /^#{self["LIBLINKPREFIX"]}(.*)$/
|
elsif word =~ /^#{get_var("LIBLINKPREFIX")}(.*)$/
|
||||||
handle["LIBS", $1]
|
handle["LIBS", $1]
|
||||||
elsif word =~ /^#{self["LIBDIRPREFIX"]}(.*)$/
|
elsif word =~ /^#{get_var("LIBDIRPREFIX")}(.*)$/
|
||||||
handle["LIBPATH", $1]
|
handle["LIBPATH", $1]
|
||||||
elsif word == "-mno-cygwin"
|
elsif word == "-mno-cygwin"
|
||||||
append["CCFLAGS", [word]]
|
append["CCFLAGS", [word]]
|
||||||
@@ -174,7 +199,7 @@ module Rscons
|
|||||||
# @return [void]
|
# @return [void]
|
||||||
def merge_flags(flags)
|
def merge_flags(flags)
|
||||||
flags.each_pair do |key, val|
|
flags.each_pair do |key, val|
|
||||||
if self[key].is_a?(Array) and val.is_a?(Array)
|
if self.get_var(key).is_a?(Array) and val.is_a?(Array)
|
||||||
self[key] += val
|
self[key] += val
|
||||||
else
|
else
|
||||||
self[key] = val
|
self[key] = val
|
||||||
@@ -223,5 +248,28 @@ module Rscons
|
|||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
+29
-2
@@ -47,6 +47,14 @@ module Rscons
|
|||||||
# Construction variables used to perform the build operation.
|
# Construction variables used to perform the build operation.
|
||||||
attr_accessor :vars
|
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.
|
# Create an instance of the Builder to build a target.
|
||||||
#
|
#
|
||||||
# @param options [Hash]
|
# @param options [Hash]
|
||||||
@@ -67,6 +75,7 @@ module Rscons
|
|||||||
@cache = options[:cache]
|
@cache = options[:cache]
|
||||||
@env = options[:env]
|
@env = options[:env]
|
||||||
@vars = options[:vars]
|
@vars = options[:vars]
|
||||||
|
@side_effects = Set.new
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return the name of the builder.
|
# Return the name of the builder.
|
||||||
@@ -78,6 +87,14 @@ module Rscons
|
|||||||
self.class.name
|
self.class.name
|
||||||
end
|
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.
|
# Manually record a given build target as depending on the specified files.
|
||||||
#
|
#
|
||||||
# @param user_deps [Array<String>]
|
# @param user_deps [Array<String>]
|
||||||
@@ -88,6 +105,18 @@ module Rscons
|
|||||||
@env.depends(@target, *user_deps)
|
@env.depends(@target, *user_deps)
|
||||||
end
|
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.
|
# Run the builder to produce a build target.
|
||||||
#
|
#
|
||||||
# @param options [Hash]
|
# @param options [Hash]
|
||||||
@@ -172,8 +201,6 @@ module Rscons
|
|||||||
|
|
||||||
# Register build results from a {Command} with the cache.
|
# Register build results from a {Command} with the cache.
|
||||||
#
|
#
|
||||||
# @since 1.10.0
|
|
||||||
#
|
|
||||||
# @param options [Hash]
|
# @param options [Hash]
|
||||||
# Builder finalize options.
|
# Builder finalize options.
|
||||||
# @option options [String] :stdout
|
# @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
|
# 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
|
# be returned as ready to run if any of its dependencies are still
|
||||||
# building.
|
# building.
|
||||||
# @param side_effects [Hash]
|
# @param side_effects [Set]
|
||||||
# Hash mapping targets to a set of side-effect files. A builder will not
|
# Set of side-effect files. A builder will not be returned as ready to
|
||||||
# be returned as ready to run if any of its dependencies is a side-effect
|
# run if any of its dependencies is a side-effect of another target that
|
||||||
# of another target that has not yet been built.
|
# has not yet been built.
|
||||||
def initialize(build_dependencies, side_effects)
|
def initialize(build_dependencies, side_effects)
|
||||||
super()
|
super()
|
||||||
@build_dependencies = build_dependencies
|
@build_dependencies = build_dependencies
|
||||||
@@ -31,6 +31,16 @@ module Rscons
|
|||||||
self[builder.target] << builder
|
self[builder.target] << builder
|
||||||
end
|
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.
|
# Get the next builder that is ready to run from the BuilderSet.
|
||||||
#
|
#
|
||||||
# This method will remove the builder from the BuilderSet.
|
# This method will remove the builder from the BuilderSet.
|
||||||
@@ -43,22 +53,24 @@ module Rscons
|
|||||||
# @return [nil, Builder]
|
# @return [nil, Builder]
|
||||||
# The next builder to run.
|
# The next builder to run.
|
||||||
def get_next_builder_to_run(targets_still_building)
|
def get_next_builder_to_run(targets_still_building)
|
||||||
not_built_yet = targets_still_building + self.keys
|
to_build = self.find do |target, builders|
|
||||||
not_built_yet += not_built_yet.reduce([]) do |result, target|
|
deps = builders.first.sources + (@build_dependencies[target] || []).to_a
|
||||||
result + (@side_effects[target] || [])
|
# 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
|
end
|
||||||
|
|
||||||
target_to_build = self.keys.find do |target|
|
if to_build
|
||||||
deps = self[target][0].sources + (@build_dependencies[target] || []).to_a
|
target, builders = *to_build
|
||||||
!deps.find {|dep| not_built_yet.include?(dep)}
|
builder = builders.first
|
||||||
end
|
if builders.size > 1
|
||||||
|
builders.slice!(0)
|
||||||
if target_to_build
|
|
||||||
builder = self[target_to_build][0]
|
|
||||||
if self[target_to_build].size > 1
|
|
||||||
self[target_to_build].slice!(0)
|
|
||||||
else
|
else
|
||||||
self.delete(target_to_build)
|
self.delete(target)
|
||||||
end
|
end
|
||||||
return builder
|
return builder
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ module Rscons
|
|||||||
raise "Unknown source file #{@sources.first.inspect} for CFile builder"
|
raise "Unknown source file #{@sources.first.inspect} for CFile builder"
|
||||||
end
|
end
|
||||||
command = @env.build_command("${#{cmd}_CMD}", @vars)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,6 @@ module Rscons
|
|||||||
# A builder to execute an arbitrary command that will produce the given
|
# A builder to execute an arbitrary command that will produce the given
|
||||||
# target based on the given sources.
|
# target based on the given sources.
|
||||||
#
|
#
|
||||||
# @since 1.8.0
|
|
||||||
#
|
|
||||||
# Example:
|
# Example:
|
||||||
# env.Command("docs.html", "docs.md",
|
# env.Command("docs.html", "docs.md",
|
||||||
# CMD => %w[pandoc -fmarkdown -thtml -o${_TARGET} ${_SOURCES}])
|
# CMD => %w[pandoc -fmarkdown -thtml -o${_TARGET} ${_SOURCES}])
|
||||||
@@ -23,7 +21,7 @@ module Rscons
|
|||||||
if @vars["CMD_STDOUT"]
|
if @vars["CMD_STDOUT"]
|
||||||
options[:stdout] = @env.expand_varref("${CMD_STDOUT}", @vars)
|
options[:stdout] = @env.expand_varref("${CMD_STDOUT}", @vars)
|
||||||
end
|
end
|
||||||
standard_command("#{cmd_desc} => #{@target}", command, options)
|
standard_command("#{cmd_desc} <target>#{@target}<reset>", command, options)
|
||||||
end
|
end
|
||||||
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 Rscons
|
||||||
module Builders
|
module Builders
|
||||||
|
|
||||||
# The Directory builder creates a directory.
|
# The Directory builder creates a directory.
|
||||||
class Directory < Builder
|
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.
|
# Run the builder to produce a build target.
|
||||||
def run(options)
|
def run(options)
|
||||||
|
if @nop
|
||||||
|
true
|
||||||
|
else
|
||||||
if File.directory?(@target)
|
if File.directory?(@target)
|
||||||
true
|
true
|
||||||
elsif File.exists?(@target)
|
elsif File.exists?(@target)
|
||||||
Ansi.write($stderr, :red, "Error: `#{@target}' already exists and is not a directory", :reset, "\n")
|
Ansi.write($stderr, :red, "Error: `#{@target}' already exists and is not a directory", :reset, "\n")
|
||||||
false
|
false
|
||||||
else
|
else
|
||||||
print_run_message("Creating directory => #{@target}", nil)
|
print_run_message("Creating directory <target>#{@target}<reset>", nil)
|
||||||
@cache.mkdir_p(@target)
|
@cache.mkdir_p(@target, install: @install_builder)
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
@@ -10,7 +10,7 @@ module Rscons
|
|||||||
else
|
else
|
||||||
@vars["_SOURCES"] = @sources
|
@vars["_SOURCES"] = @sources
|
||||||
command = @env.build_command("${DISASM_CMD}", @vars)
|
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
|
||||||
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["_TARGET"] = @target
|
||||||
@vars["_SOURCES"] = @objects
|
@vars["_SOURCES"] = @objects
|
||||||
command = @env.build_command("${ARCMD}", @vars)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ module Rscons
|
|||||||
def finalize_command_with_depfile
|
def finalize_command_with_depfile
|
||||||
deps = @sources
|
deps = @sources
|
||||||
if File.exists?(@vars["_DEPFILE"])
|
if File.exists?(@vars["_DEPFILE"])
|
||||||
deps += Util.parse_makefile_deps(@vars["_DEPFILE"])
|
deps += Util.parse_dependency_file(@vars["_DEPFILE"])
|
||||||
end
|
end
|
||||||
@cache.register_build(@target, @command, deps.uniq, @env)
|
@cache.register_build(@target, @command, deps.uniq, @env)
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ module Rscons
|
|||||||
|
|
||||||
module Builders
|
module Builders
|
||||||
module Mixins
|
module Mixins
|
||||||
|
# Mixin for builders needing to build object files.
|
||||||
module Object
|
module Object
|
||||||
|
|
||||||
include Depfile
|
include Depfile
|
||||||
@@ -81,11 +82,11 @@ module Rscons
|
|||||||
end
|
end
|
||||||
@cache.mkdir_p(File.dirname(@vars["_DEPFILE"]))
|
@cache.mkdir_p(File.dirname(@vars["_DEPFILE"]))
|
||||||
command = @env.build_command(@command_template, @vars)
|
command = @env.build_command(@command_template, @vars)
|
||||||
@env.produces(@target, @vars["_DEPFILE"])
|
self.produces(@vars["_DEPFILE"])
|
||||||
if @vars[:direct]
|
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
|
else
|
||||||
message = "#{@short_description} #{Util.short_format_paths(@sources)}"
|
message = "#{@short_description} <source>#{Util.short_format_paths(@sources)}<reset>"
|
||||||
end
|
end
|
||||||
standard_command(message, command)
|
standard_command(message, command)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ module Rscons
|
|||||||
@vars["_SOURCES"] = @objects
|
@vars["_SOURCES"] = @objects
|
||||||
@vars["#{ld_var}"] = ld
|
@vars["#{ld_var}"] = ld
|
||||||
command = @env.build_command("${#{ld_var}CMD}", @vars)
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ module Rscons
|
|||||||
@vars["_SOURCES"] = @sources
|
@vars["_SOURCES"] = @sources
|
||||||
@vars["_DEPFILE"] = Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars))
|
@vars["_DEPFILE"] = Rscons.set_suffix(target, env.expand_varref("${DEPFILESUFFIX}", vars))
|
||||||
command = @env.build_command("${CPP_CMD}", @vars)
|
command = @env.build_command("${CPP_CMD}", @vars)
|
||||||
@env.produces(@target, @vars["_DEPFILE"])
|
self.produces(@vars["_DEPFILE"])
|
||||||
standard_command("Preprocessing #{Util.short_format_paths(@sources)} => #{@target}", command)
|
standard_command("Preprocessing <source>#{Util.short_format_paths(@sources)}<reset> => <target>#{@target}<reset>", command)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ module Rscons
|
|||||||
include Mixins::Program
|
include Mixins::Program
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
# Custom new method which will delegate to the correct class depending
|
||||||
|
# on the options specified.
|
||||||
def new(options, *more)
|
def new(options, *more)
|
||||||
unless File.basename(options[:target])["."]
|
unless File.basename(options[:target])["."]
|
||||||
options[:target] += options[:env].expand_varref("${PROGSUFFIX}", options[:vars])
|
options[:target] += options[:env].expand_varref("${PROGSUFFIX}", options[:vars])
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ module Rscons
|
|||||||
include Mixins::Program
|
include Mixins::Program
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
# Custom new method which will delegate to the correct class depending
|
||||||
|
# on the options specified.
|
||||||
def new(options, *more)
|
def new(options, *more)
|
||||||
libprefix = options[:env].expand_varref("${SHLIBPREFIX}", options[:vars])
|
libprefix = options[:env].expand_varref("${SHLIBPREFIX}", options[:vars])
|
||||||
unless File.basename(options[:target]).start_with?(libprefix)
|
unless File.basename(options[:target]).start_with?(libprefix)
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ module Rscons
|
|||||||
include Mixins::Object
|
include Mixins::Object
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
# Content component to add to build path to separate objects built
|
||||||
|
# using this builder from others.
|
||||||
def extra_path
|
def extra_path
|
||||||
"_shared"
|
"_shared"
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
module Rscons
|
module Rscons
|
||||||
module Builders
|
module Builders
|
||||||
# A Generic builder which has its operation is defined at instantiation.
|
# A Generic builder which has its operation is defined at instantiation.
|
||||||
#
|
|
||||||
# @since 1.8.0
|
|
||||||
class SimpleBuilder < Builder
|
class SimpleBuilder < Builder
|
||||||
|
|
||||||
# @return [String]
|
# @return [String]
|
||||||
|
|||||||
+23
-18
@@ -100,7 +100,6 @@ module Rscons
|
|||||||
# @return [void]
|
# @return [void]
|
||||||
def write
|
def write
|
||||||
@cache["version"] = VERSION
|
@cache["version"] = VERSION
|
||||||
validate_json_object(@cache)
|
|
||||||
File.open(CACHE_FILE, "w") do |fh|
|
File.open(CACHE_FILE, "w") do |fh|
|
||||||
fh.puts(JSON.dump(@cache))
|
fh.puts(JSON.dump(@cache))
|
||||||
end
|
end
|
||||||
@@ -239,11 +238,18 @@ module Rscons
|
|||||||
# @param options [Hash] Optional arguments.
|
# @param options [Hash] Optional arguments.
|
||||||
# @option options [Boolean] :install
|
# @option options [Boolean] :install
|
||||||
# Whether the target is for an install operation.
|
# 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]
|
# @return [void]
|
||||||
def register_build(targets, command, deps, env, options = {})
|
def register_build(targets, command, deps, env, options = {})
|
||||||
Array(targets).each do |target|
|
Array(targets).each do |target|
|
||||||
target_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)] = {
|
@cache["targets"][get_cache_key(target)] = {
|
||||||
"command" => Digest::MD5.hexdigest(command.inspect),
|
"command" => Digest::MD5.hexdigest(command.inspect),
|
||||||
"checksum" => target_checksum,
|
"checksum" => target_checksum,
|
||||||
@@ -275,7 +281,7 @@ module Rscons
|
|||||||
def targets(install)
|
def targets(install)
|
||||||
install = !!install
|
install = !!install
|
||||||
@cache["targets"].select do |key, target|
|
@cache["targets"].select do |key, target|
|
||||||
target[:install] == install
|
target["install"] == install
|
||||||
end.map(&:first)
|
end.map(&:first)
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -320,6 +326,20 @@ module Rscons
|
|||||||
end.map(&:first)
|
end.map(&:first)
|
||||||
end
|
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
|
private
|
||||||
|
|
||||||
# Return a String key based on the target name to use in the on-disk cache.
|
# Return a String key based on the target name to use in the on-disk cache.
|
||||||
@@ -370,20 +390,5 @@ module Rscons
|
|||||||
@lookup_checksums[file] = Digest::MD5.hexdigest(File.read(file, mode: "rb")) rescue ""
|
@lookup_checksums[file] = Digest::MD5.hexdigest(File.read(file, mode: "rb")) rescue ""
|
||||||
end
|
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
|
||||||
end
|
end
|
||||||
+10
-6
@@ -1,11 +1,12 @@
|
|||||||
require "rscons"
|
require "rscons"
|
||||||
require "optparse"
|
require "optparse"
|
||||||
|
|
||||||
|
# CLI usage string.
|
||||||
USAGE = <<EOF
|
USAGE = <<EOF
|
||||||
Usage: #{$0} [global options] [operation] [operation options]
|
Usage: #{$0} [global options] [operation] [operation options]
|
||||||
|
|
||||||
Global 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
|
--version Show rscons version and exit
|
||||||
-h, --help Show rscons help and exit
|
-h, --help Show rscons help and exit
|
||||||
-r COLOR, --color=COLOR Set color mode (off, auto, force)
|
-r COLOR, --color=COLOR Set color mode (off, auto, force)
|
||||||
@@ -121,21 +122,24 @@ module Rscons
|
|||||||
rsconscript = DEFAULT_RSCONSCRIPTS.find do |f|
|
rsconscript = DEFAULT_RSCONSCRIPTS.find do |f|
|
||||||
File.exists?(f)
|
File.exists?(f)
|
||||||
end
|
end
|
||||||
unless rsconscript
|
|
||||||
$stderr.puts "Could not find the Rsconscript to execute."
|
|
||||||
$stderr.puts "Looked for: #{DEFAULT_RSCONSCRIPTS.join(", ")}"
|
|
||||||
exit 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if rsconscript
|
||||||
script = Script.new
|
script = Script.new
|
||||||
script.load(rsconscript)
|
script.load(rsconscript)
|
||||||
|
end
|
||||||
|
|
||||||
if do_help
|
if do_help
|
||||||
puts USAGE
|
puts USAGE
|
||||||
exit 0
|
exit 0
|
||||||
end
|
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) || {}
|
operation_options = parse_operation_args(operation, argv) || {}
|
||||||
|
|
||||||
exit Rscons.application.run(operation, script, operation_options)
|
exit Rscons.application.run(operation, script, operation_options)
|
||||||
|
|||||||
+231
-108
@@ -10,20 +10,48 @@ module Rscons
|
|||||||
|
|
||||||
# Create a ConfigureOp.
|
# Create a ConfigureOp.
|
||||||
#
|
#
|
||||||
# @param work_dir [String]
|
# @param options [Hash]
|
||||||
# Work directory for configure operation.
|
# Optional parameters.
|
||||||
def initialize(work_dir)
|
# @option options [String] :build_dir
|
||||||
@work_dir = work_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)
|
FileUtils.mkdir_p(@work_dir)
|
||||||
@log_fh = File.open("#{@work_dir}/config.log", "wb")
|
@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
|
end
|
||||||
|
|
||||||
# Close the log file handle.
|
# Close the log file handle.
|
||||||
#
|
#
|
||||||
|
# @param success [Boolean]
|
||||||
|
# Whether all configure operations were successful.
|
||||||
|
#
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def close
|
def close(success)
|
||||||
@log_fh.close
|
@log_fh.close
|
||||||
@log_fh = nil
|
@log_fh = nil
|
||||||
|
cache = Cache.instance
|
||||||
|
cache["configuration_data"]["configured"] = success
|
||||||
|
cache.write
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for a working C compiler.
|
# Check for a working C compiler.
|
||||||
@@ -41,7 +69,7 @@ module Rscons
|
|||||||
cc = ccc.find do |cc|
|
cc = ccc.find do |cc|
|
||||||
test_c_compiler(cc)
|
test_c_compiler(cc)
|
||||||
end
|
end
|
||||||
common_config_checks(cc ? 0 : 1, success_message: cc)
|
complete(cc ? 0 : 1, success_message: cc)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for a working C++ compiler.
|
# Check for a working C++ compiler.
|
||||||
@@ -59,7 +87,7 @@ module Rscons
|
|||||||
cc = ccc.find do |cc|
|
cc = ccc.find do |cc|
|
||||||
test_cxx_compiler(cc)
|
test_cxx_compiler(cc)
|
||||||
end
|
end
|
||||||
common_config_checks(cc ? 0 : 1, success_message: cc)
|
complete(cc ? 0 : 1, success_message: cc)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for a working D compiler.
|
# Check for a working D compiler.
|
||||||
@@ -77,7 +105,7 @@ module Rscons
|
|||||||
dc = cdc.find do |dc|
|
dc = cdc.find do |dc|
|
||||||
test_d_compiler(dc)
|
test_d_compiler(dc)
|
||||||
end
|
end
|
||||||
common_config_checks(dc ? 0 : 1, success_message: dc)
|
complete(dc ? 0 : 1, success_message: dc)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for a package or configure program output.
|
# Check for a package or configure program output.
|
||||||
@@ -94,11 +122,12 @@ module Rscons
|
|||||||
if status == 0
|
if status == 0
|
||||||
store_parse(stdout, options)
|
store_parse(stdout, options)
|
||||||
end
|
end
|
||||||
common_config_checks(status, options)
|
complete(status, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for a C header.
|
# Check for a C header.
|
||||||
def check_c_header(header_name, options = {})
|
def check_c_header(header_name, options = {})
|
||||||
|
check_cpppath = [nil] + (options[:check_cpppath] || [])
|
||||||
Ansi.write($stdout, "Checking for C header '", :cyan, header_name, :reset, "'... ")
|
Ansi.write($stdout, "Checking for C header '", :cyan, header_name, :reset, "'... ")
|
||||||
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
|
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
|
||||||
fh.puts <<-EOF
|
fh.puts <<-EOF
|
||||||
@@ -111,15 +140,30 @@ module Rscons
|
|||||||
vars = {
|
vars = {
|
||||||
"LD" => "${CC}",
|
"LD" => "${CC}",
|
||||||
"_SOURCES" => "#{@work_dir}/cfgtest.c",
|
"_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 = 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)
|
_, _, status = log_and_test_command(command)
|
||||||
common_config_checks(status, options)
|
if status == 0
|
||||||
|
if cpppath
|
||||||
|
store_append({"CPPPATH" => Array(cpppath)}, options)
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
complete(status, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for a C++ header.
|
# Check for a C++ header.
|
||||||
def check_cxx_header(header_name, options = {})
|
def check_cxx_header(header_name, options = {})
|
||||||
|
check_cpppath = [nil] + (options[:check_cpppath] || [])
|
||||||
Ansi.write($stdout, "Checking for C++ header '", :cyan, header_name, :reset, "'... ")
|
Ansi.write($stdout, "Checking for C++ header '", :cyan, header_name, :reset, "'... ")
|
||||||
File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh|
|
File.open("#{@work_dir}/cfgtest.cxx", "wb") do |fh|
|
||||||
fh.puts <<-EOF
|
fh.puts <<-EOF
|
||||||
@@ -132,15 +176,30 @@ module Rscons
|
|||||||
vars = {
|
vars = {
|
||||||
"LD" => "${CXX}",
|
"LD" => "${CXX}",
|
||||||
"_SOURCES" => "#{@work_dir}/cfgtest.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 = 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)
|
_, _, status = log_and_test_command(command)
|
||||||
common_config_checks(status, options)
|
if status == 0
|
||||||
|
if cpppath
|
||||||
|
store_append({"CPPPATH" => Array(cpppath)}, options)
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
complete(status, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for a D import.
|
# Check for a D import.
|
||||||
def check_d_import(d_import, options = {})
|
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, "'... ")
|
Ansi.write($stdout, "Checking for D import '", :cyan, d_import, :reset, "'... ")
|
||||||
File.open("#{@work_dir}/cfgtest.d", "wb") do |fh|
|
File.open("#{@work_dir}/cfgtest.d", "wb") do |fh|
|
||||||
fh.puts <<-EOF
|
fh.puts <<-EOF
|
||||||
@@ -153,15 +212,30 @@ module Rscons
|
|||||||
vars = {
|
vars = {
|
||||||
"LD" => "${DC}",
|
"LD" => "${DC}",
|
||||||
"_SOURCES" => "#{@work_dir}/cfgtest.d",
|
"_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 = 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)
|
_, _, status = log_and_test_command(command)
|
||||||
common_config_checks(status, options)
|
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
|
end
|
||||||
|
|
||||||
# Check for a library.
|
# Check for a library.
|
||||||
def check_lib(lib, options = {})
|
def check_lib(lib, options = {})
|
||||||
|
check_libpath = [nil] + (options[:check_libpath] || [])
|
||||||
Ansi.write($stdout, "Checking for library '", :cyan, lib, :reset, "'... ")
|
Ansi.write($stdout, "Checking for library '", :cyan, lib, :reset, "'... ")
|
||||||
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
|
File.open("#{@work_dir}/cfgtest.c", "wb") do |fh|
|
||||||
fh.puts <<-EOF
|
fh.puts <<-EOF
|
||||||
@@ -176,19 +250,150 @@ module Rscons
|
|||||||
"_SOURCES" => "#{@work_dir}/cfgtest.c",
|
"_SOURCES" => "#{@work_dir}/cfgtest.c",
|
||||||
"_TARGET" => "#{@work_dir}/cfgtest.exe",
|
"_TARGET" => "#{@work_dir}/cfgtest.exe",
|
||||||
}
|
}
|
||||||
command = BasicEnvironment.new.build_command("${LDCMD}", vars)
|
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)
|
_, _, status = log_and_test_command(command)
|
||||||
|
if status == 0
|
||||||
|
if libpath
|
||||||
|
store_append({"LIBPATH" => Array(libpath)}, options)
|
||||||
|
end
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
if status == 0
|
if status == 0
|
||||||
store_append({"LIBS" => [lib]}, options)
|
store_append({"LIBS" => [lib]}, options)
|
||||||
end
|
end
|
||||||
common_config_checks(status, options)
|
complete(status, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for a executable program.
|
# Check for a executable program.
|
||||||
def check_program(program, options = {})
|
def check_program(program, options = {})
|
||||||
Ansi.write($stdout, "Checking for program '", :cyan, program, :reset, "'... ")
|
Ansi.write($stdout, "Checking for program '", :cyan, program, :reset, "'... ")
|
||||||
path = Util.find_executable(program)
|
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
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
@@ -281,7 +486,12 @@ module Rscons
|
|||||||
"DC" => dc,
|
"DC" => dc,
|
||||||
"DCCMD" => env["DCCMD"].map {|e| if e == "-o"; "-of"; else; e; end},
|
"DCCMD" => env["DCCMD"].map {|e| if e == "-o"; "-of"; else; e; end},
|
||||||
"LDCMD" => env["LDCMD"].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
|
end
|
||||||
_, _, status = log_and_test_command(command)
|
_, _, status = log_and_test_command(command)
|
||||||
if status == 0
|
if status == 0
|
||||||
@@ -294,64 +504,6 @@ module Rscons
|
|||||||
end
|
end
|
||||||
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.
|
# Common functionality for all store methods.
|
||||||
#
|
#
|
||||||
# @param options [Hash]
|
# @param options [Hash]
|
||||||
@@ -375,34 +527,5 @@ module Rscons
|
|||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
+52
-50
@@ -59,6 +59,9 @@ module Rscons
|
|||||||
# :command, :short, or :off (default :short)
|
# :command, :short, or :off (default :short)
|
||||||
# @option options [Boolean] :exclude_builders
|
# @option options [Boolean] :exclude_builders
|
||||||
# Whether to omit adding default builders (default false)
|
# 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
|
# If a block is given, the Environment object is yielded to the block and
|
||||||
# when the block returns, the {#process} method is automatically called.
|
# when the block returns, the {#process} method is automatically called.
|
||||||
@@ -72,7 +75,8 @@ module Rscons
|
|||||||
# Hash of Thread object => {Command} or {Builder}.
|
# Hash of Thread object => {Command} or {Builder}.
|
||||||
@threads = {}
|
@threads = {}
|
||||||
@registered_build_dependencies = {}
|
@registered_build_dependencies = {}
|
||||||
@side_effects = {}
|
# Set of side-effect files that have not yet been built.
|
||||||
|
@side_effects = Set.new
|
||||||
@builder_sets = []
|
@builder_sets = []
|
||||||
@build_targets = {}
|
@build_targets = {}
|
||||||
@user_deps = {}
|
@user_deps = {}
|
||||||
@@ -165,8 +169,6 @@ module Rscons
|
|||||||
# Create a new {Builders::SimpleBuilder} instance and add it to the
|
# Create a new {Builders::SimpleBuilder} instance and add it to the
|
||||||
# environment.
|
# environment.
|
||||||
#
|
#
|
||||||
# @since 1.8.0
|
|
||||||
#
|
|
||||||
# @param name [String,Symbol]
|
# @param name [String,Symbol]
|
||||||
# The name of the builder to add.
|
# The name of the builder to add.
|
||||||
#
|
#
|
||||||
@@ -216,8 +218,6 @@ module Rscons
|
|||||||
# build operation succeeded. Post-build hooks can register new build
|
# build operation succeeded. Post-build hooks can register new build
|
||||||
# targets.
|
# targets.
|
||||||
#
|
#
|
||||||
# @since 1.7.0
|
|
||||||
#
|
|
||||||
# @yield [build_op]
|
# @yield [build_op]
|
||||||
# Invoke the given block with the current build operation.
|
# Invoke the given block with the current build operation.
|
||||||
# @yieldparam build_op [Hash]
|
# @yieldparam build_op [Hash]
|
||||||
@@ -261,6 +261,7 @@ module Rscons
|
|||||||
#
|
#
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def process
|
def process
|
||||||
|
Cache.instance.clear_checksum_cache!
|
||||||
@process_failures = []
|
@process_failures = []
|
||||||
@process_blocking_wait = false
|
@process_blocking_wait = false
|
||||||
@process_commands_waiting_to_run = []
|
@process_commands_waiting_to_run = []
|
||||||
@@ -286,7 +287,7 @@ module Rscons
|
|||||||
unless @process_failures.empty?
|
unless @process_failures.empty?
|
||||||
msg = @process_failures.join("\n")
|
msg = @process_failures.join("\n")
|
||||||
if Cache.instance["failed_commands"].size > 0
|
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
|
end
|
||||||
raise BuildError.new(msg)
|
raise BuildError.new(msg)
|
||||||
end
|
end
|
||||||
@@ -374,8 +375,6 @@ module Rscons
|
|||||||
# described:
|
# described:
|
||||||
# env.build_after("program.o", "gen.c")
|
# env.build_after("program.o", "gen.c")
|
||||||
#
|
#
|
||||||
# @since 1.10.0
|
|
||||||
#
|
|
||||||
# @param targets [String, Array<String>]
|
# @param targets [String, Array<String>]
|
||||||
# Target files to wait to build until the prerequisites are finished
|
# Target files to wait to build until the prerequisites are finished
|
||||||
# building.
|
# building.
|
||||||
@@ -402,8 +401,6 @@ module Rscons
|
|||||||
# Manually record the given side effect file(s) as being produced when the
|
# Manually record the given side effect file(s) as being produced when the
|
||||||
# named target is produced.
|
# named target is produced.
|
||||||
#
|
#
|
||||||
# @since 1.13.0
|
|
||||||
#
|
|
||||||
# @param target [String]
|
# @param target [String]
|
||||||
# Target of a build operation.
|
# Target of a build operation.
|
||||||
# @param side_effects [Array<String>]
|
# @param side_effects [Array<String>]
|
||||||
@@ -412,11 +409,25 @@ module Rscons
|
|||||||
# @return [void]
|
# @return [void]
|
||||||
def produces(target, *side_effects)
|
def produces(target, *side_effects)
|
||||||
target = expand_path(expand_varref(target))
|
target = expand_path(expand_varref(target))
|
||||||
side_effects = Array(side_effects).map do |side_effect|
|
@builder_sets.reverse.each do |builder_set|
|
||||||
expand_path(expand_varref(side_effect))
|
if builders = builder_set[target]
|
||||||
end.flatten
|
builders.last.produces(*side_effects)
|
||||||
@side_effects[target] ||= []
|
return
|
||||||
@side_effects[target] += side_effects
|
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
|
end
|
||||||
|
|
||||||
# Return the list of user dependencies for a given target.
|
# Return the list of user dependencies for a given target.
|
||||||
@@ -479,29 +490,6 @@ module Rscons
|
|||||||
end
|
end
|
||||||
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.
|
# Print the builder run message, depending on the Environment's echo mode.
|
||||||
#
|
#
|
||||||
# @param builder [Builder]
|
# @param builder [Builder]
|
||||||
@@ -526,7 +514,12 @@ module Rscons
|
|||||||
when :short
|
when :short
|
||||||
message = short_description if short_description
|
message = short_description if short_description
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
# Get the Builder for a target.
|
# Get the Builder for a target.
|
||||||
@@ -547,6 +540,16 @@ module Rscons
|
|||||||
@builder_sets << build_builder_set
|
@builder_sets << build_builder_set
|
||||||
end
|
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
|
private
|
||||||
|
|
||||||
# Build a BuilderSet.
|
# Build a BuilderSet.
|
||||||
@@ -564,9 +567,9 @@ module Rscons
|
|||||||
#
|
#
|
||||||
# @return [void]
|
# @return [void]
|
||||||
def run_builder(builder)
|
def run_builder(builder)
|
||||||
# TODO: have Cache determine when checksums may be invalid based on
|
unless builder.nop?
|
||||||
# file size and/or timestamp.
|
builder.build_step ||= Rscons.application.get_next_build_step
|
||||||
Cache.instance.clear_checksum_cache!
|
end
|
||||||
case result = builder.run({})
|
case result = builder.run({})
|
||||||
when Array
|
when Array
|
||||||
result.each do |waititem|
|
result.each do |waititem|
|
||||||
@@ -588,8 +591,9 @@ module Rscons
|
|||||||
when true
|
when true
|
||||||
# Register side-effect files as build targets so that a Cache
|
# Register side-effect files as build targets so that a Cache
|
||||||
# clean operation will remove them.
|
# clean operation will remove them.
|
||||||
(@side_effects[builder.target] || []).each do |side_effect_file|
|
builder.side_effects.each do |side_effect|
|
||||||
Cache.instance.register_build(side_effect_file, nil, [], self)
|
Cache.instance.register_build(side_effect, nil, [], self, side_effect: true)
|
||||||
|
@side_effects.delete(side_effect)
|
||||||
end
|
end
|
||||||
@build_hooks[:post].each do |build_hook_block|
|
@build_hooks[:post].each do |build_hook_block|
|
||||||
build_hook_block.call(builder)
|
build_hook_block.call(builder)
|
||||||
@@ -634,7 +638,7 @@ module Rscons
|
|||||||
if thread
|
if thread
|
||||||
# We found a completed thread.
|
# We found a completed thread.
|
||||||
process_remove_wait(thread)
|
process_remove_wait(thread)
|
||||||
builder = builder_for_thread(thread)
|
unblocked_builder = builder_for_thread(thread)
|
||||||
completed_command = @threads[thread]
|
completed_command = @threads[thread]
|
||||||
@threads.delete(thread)
|
@threads.delete(thread)
|
||||||
if completed_command.is_a?(Command)
|
if completed_command.is_a?(Command)
|
||||||
@@ -642,7 +646,7 @@ module Rscons
|
|||||||
completed_command.status = thread.value
|
completed_command.status = thread.value
|
||||||
unless completed_command.status
|
unless completed_command.status
|
||||||
Cache.instance["failed_commands"] << completed_command.command
|
Cache.instance["failed_commands"] << completed_command.command
|
||||||
@process_failures << "Failed to build #{builder.target}."
|
@process_failures << "Failed to build #{unblocked_builder.target}."
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -667,16 +671,14 @@ module Rscons
|
|||||||
result << builder_for_thread(thread).target
|
result << builder_for_thread(thread).target
|
||||||
end
|
end
|
||||||
if @builder_sets.size > 0
|
if @builder_sets.size > 0
|
||||||
builder = @builder_sets[0].get_next_builder_to_run(targets_still_building)
|
if builder = @builder_sets[0].get_next_builder_to_run(targets_still_building)
|
||||||
end
|
|
||||||
|
|
||||||
if builder
|
|
||||||
builder.vars = @varset.merge(builder.vars)
|
builder.vars = @varset.merge(builder.vars)
|
||||||
@build_hooks[:pre].each do |build_hook_block|
|
@build_hooks[:pre].each do |build_hook_block|
|
||||||
build_hook_block.call(builder)
|
build_hook_block.call(builder)
|
||||||
end
|
end
|
||||||
return run_builder(builder)
|
return run_builder(builder)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if @threads.size > 0
|
if @threads.size > 0
|
||||||
# A runnable builder was not found but there is a thread running,
|
# A runnable builder was not found but there is a thread running,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ module Rscons
|
|||||||
# The Script class encapsulates the state of a build script.
|
# The Script class encapsulates the state of a build script.
|
||||||
class Script
|
class Script
|
||||||
|
|
||||||
|
# DSL available to the Rsconscript.
|
||||||
class Dsl
|
class Dsl
|
||||||
# Create a Dsl.
|
# Create a Dsl.
|
||||||
def initialize(script)
|
def initialize(script)
|
||||||
@@ -57,6 +58,7 @@ module Rscons
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# DSL available to the 'configure' block.
|
||||||
class ConfigureDsl
|
class ConfigureDsl
|
||||||
# Create a ConfigureDsl.
|
# Create a ConfigureDsl.
|
||||||
#
|
#
|
||||||
@@ -81,6 +83,19 @@ module Rscons
|
|||||||
@configure_op.__send__(method_name, *args)
|
@configure_op.__send__(method_name, *args)
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
||||||
# @return [String, nil]
|
# @return [String, nil]
|
||||||
|
|||||||
+55
-3
@@ -1,4 +1,5 @@
|
|||||||
module Rscons
|
module Rscons
|
||||||
|
# A collection of stand-alone utility methods.
|
||||||
module Util
|
module Util
|
||||||
class << self
|
class << self
|
||||||
|
|
||||||
@@ -15,6 +16,30 @@ module Rscons
|
|||||||
end
|
end
|
||||||
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.
|
# Return a string representation of a command.
|
||||||
#
|
#
|
||||||
# @param command [Array<String>]
|
# @param command [Array<String>]
|
||||||
@@ -95,6 +120,27 @@ module Rscons
|
|||||||
end
|
end
|
||||||
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.
|
# Make a relative path corresponding to a possibly absolute one.
|
||||||
#
|
#
|
||||||
# @param path [String]
|
# @param path [String]
|
||||||
@@ -114,7 +160,7 @@ module Rscons
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Parse dependencies from a Makefile.
|
# Parse dependencies from a Makefile or ldc2 dependency file.
|
||||||
#
|
#
|
||||||
# This method is used internally by Rscons builders.
|
# This method is used internally by Rscons builders.
|
||||||
#
|
#
|
||||||
@@ -123,7 +169,7 @@ module Rscons
|
|||||||
#
|
#
|
||||||
# @return [Array<String>]
|
# @return [Array<String>]
|
||||||
# Paths of dependency files.
|
# Paths of dependency files.
|
||||||
def parse_makefile_deps(mf_fname)
|
def parse_dependency_file(mf_fname)
|
||||||
deps = []
|
deps = []
|
||||||
buildup = ''
|
buildup = ''
|
||||||
File.read(mf_fname).each_line do |line|
|
File.read(mf_fname).each_line do |line|
|
||||||
@@ -131,7 +177,11 @@ module Rscons
|
|||||||
buildup += ' ' + $1
|
buildup += ' ' + $1
|
||||||
else
|
else
|
||||||
buildup += ' ' + line
|
buildup += ' ' + line
|
||||||
if buildup =~ /^.*: (.*)$/
|
if buildup =~ /^[^:]+\(\S+\)\s*:.*?:[^:]+\((\S+)\)/
|
||||||
|
# ldc2-style dependency line
|
||||||
|
deps << $1
|
||||||
|
elsif buildup =~ /^.*: (.*)$/
|
||||||
|
# Makefile-style dependency line
|
||||||
mf_deps = $1
|
mf_deps = $1
|
||||||
deps += mf_deps.split(' ').map(&:strip)
|
deps += mf_deps.split(' ').map(&:strip)
|
||||||
end
|
end
|
||||||
@@ -154,6 +204,7 @@ module Rscons
|
|||||||
File.file?(path) and File.executable?(path)
|
File.file?(path) and File.executable?(path)
|
||||||
end
|
end
|
||||||
if RbConfig::CONFIG["host_os"] =~ /mswin|windows|mingw/i
|
if RbConfig::CONFIG["host_os"] =~ /mswin|windows|mingw/i
|
||||||
|
if File.directory?(path_entry)
|
||||||
executable = executable.downcase
|
executable = executable.downcase
|
||||||
dir_entries = Dir.entries(path_entry)
|
dir_entries = Dir.entries(path_entry)
|
||||||
dir_entries.find do |entry|
|
dir_entries.find do |entry|
|
||||||
@@ -166,6 +217,7 @@ module Rscons
|
|||||||
return path
|
return path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
path = "#{path_entry}/#{executable}"
|
path = "#{path_entry}/#{executable}"
|
||||||
return path if is_executable[path]
|
return path if is_executable[path]
|
||||||
|
|||||||
+50
-23
@@ -11,11 +11,13 @@ module Rscons
|
|||||||
append(vars)
|
append(vars)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Access the value of variable.
|
# Access the value of a variable.
|
||||||
#
|
#
|
||||||
# @param key [String, Symbol] The variable name.
|
# @param key [String, Symbol]
|
||||||
|
# The variable name.
|
||||||
#
|
#
|
||||||
# @return [Object] The variable's value.
|
# @return [Object]
|
||||||
|
# The variable's value.
|
||||||
def [](key)
|
def [](key)
|
||||||
if @my_vars.include?(key)
|
if @my_vars.include?(key)
|
||||||
@my_vars[key]
|
@my_vars[key]
|
||||||
@@ -30,11 +32,38 @@ module Rscons
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Access the value of a variable.
|
||||||
|
#
|
||||||
|
# This method is similar to #[] but does not make a copy-on-access copy of
|
||||||
|
# the variable accessed. This means that the returned value is NOT safe to
|
||||||
|
# be modified by the caller. Thus the caller must guarantee that it does
|
||||||
|
# not modify the returned value.
|
||||||
|
#
|
||||||
|
# @param key [String, Symbol]
|
||||||
|
# The variable name.
|
||||||
|
#
|
||||||
|
# @return [Object]
|
||||||
|
# The variable's value.
|
||||||
|
def get_var(key)
|
||||||
|
if @my_vars.include?(key)
|
||||||
|
@my_vars[key]
|
||||||
|
else
|
||||||
|
@coa_vars.each do |coa_vars|
|
||||||
|
if coa_vars.include?(key)
|
||||||
|
return coa_vars[key]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Assign a value to a variable.
|
# Assign a value to a variable.
|
||||||
#
|
#
|
||||||
# @param key [String, Symbol] The variable name.
|
# @param key [String, Symbol]
|
||||||
|
# The variable name.
|
||||||
#
|
#
|
||||||
# @param val [Object] The value to set.
|
# @param val [Object]
|
||||||
|
# The value to set.
|
||||||
def []=(key, val)
|
def []=(key, val)
|
||||||
@my_vars[key] = val
|
@my_vars[key] = val
|
||||||
end
|
end
|
||||||
@@ -95,29 +124,27 @@ module Rscons
|
|||||||
# Expanded value with "$!{var}" variable references replaced.
|
# Expanded value with "$!{var}" variable references replaced.
|
||||||
def expand_varref(varref, lambda_args)
|
def expand_varref(varref, lambda_args)
|
||||||
case varref
|
case varref
|
||||||
when Symbol, true, false, nil
|
|
||||||
varref
|
|
||||||
when String
|
when String
|
||||||
if varref =~ /^(.*)\$\{([^}]+)\}(.*)$/
|
if varref =~ /^(.*)\$\{([^}]+)\}(.*)$/
|
||||||
prefix, varname, suffix = $1, $2, $3
|
prefix, varname, suffix = $1, $2, $3
|
||||||
prefix = expand_varref(prefix, lambda_args) unless prefix.empty?
|
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
|
# suffix needs no expansion since the regex matches the last occurence
|
||||||
case varval
|
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
|
when Array
|
||||||
if prefix.is_a?(Array)
|
if prefix.is_a?(Array)
|
||||||
varval.map {|vv| prefix.map {|p| "#{p}#{vv}#{suffix}"}}.flatten
|
varval.map {|vv| prefix.map {|p| "#{p}#{vv}#{suffix}"}}.flatten
|
||||||
else
|
else
|
||||||
varval.map {|vv| "#{prefix}#{vv}#{suffix}"}
|
varval.map {|vv| "#{prefix}#{vv}#{suffix}"}
|
||||||
end
|
end
|
||||||
|
when String, Symbol, true, false, nil
|
||||||
|
if prefix.is_a?(Array)
|
||||||
|
prefix.map {|p| "#{p}#{varval}#{suffix}"}
|
||||||
else
|
else
|
||||||
raise "Unknown construction variable type: #{varval.class} (from #{varname.inspect} => #{self[varname].inspect})"
|
"#{prefix}#{varval}#{suffix}"
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "Unknown construction variable type: #{varval.class} (from #{varname.inspect} => #{get_var(varname).inspect})"
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
varref
|
varref
|
||||||
@@ -126,6 +153,8 @@ module Rscons
|
|||||||
varref.map do |ent|
|
varref.map do |ent|
|
||||||
expand_varref(ent, lambda_args)
|
expand_varref(ent, lambda_args)
|
||||||
end.flatten
|
end.flatten
|
||||||
|
when Symbol, true, false, nil
|
||||||
|
varref
|
||||||
when Proc
|
when Proc
|
||||||
expand_varref(varref[*lambda_args], lambda_args)
|
expand_varref(varref[*lambda_args], lambda_args)
|
||||||
else
|
else
|
||||||
@@ -135,8 +164,6 @@ module Rscons
|
|||||||
|
|
||||||
# Return a Hash containing all variables in the VarSet.
|
# 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
|
# This method is not terribly efficient. It is intended to be used only by
|
||||||
# debugging code to dump out a VarSet's variables.
|
# debugging code to dump out a VarSet's variables.
|
||||||
#
|
#
|
||||||
@@ -194,16 +221,16 @@ module Rscons
|
|||||||
#
|
#
|
||||||
# @return [Object] Deep copied value.
|
# @return [Object] Deep copied value.
|
||||||
def deep_dup(obj)
|
def deep_dup(obj)
|
||||||
obj_class = obj.class
|
case obj
|
||||||
if obj_class == Hash
|
when String
|
||||||
|
obj.dup
|
||||||
|
when Array
|
||||||
|
obj.map { |v| deep_dup(v) }
|
||||||
|
when Hash
|
||||||
obj.reduce({}) do |result, (k, v)|
|
obj.reduce({}) do |result, (k, v)|
|
||||||
result[k] = deep_dup(v)
|
result[k] = deep_dup(v)
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
elsif obj_class == Array
|
|
||||||
obj.map { |v| deep_dup(v) }
|
|
||||||
elsif obj_class == String
|
|
||||||
obj.dup
|
|
||||||
else
|
else
|
||||||
obj
|
obj
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
module Rscons
|
module Rscons
|
||||||
# Project version.
|
# Project version.
|
||||||
VERSION = "1.16.0"
|
VERSION = "2.0.1"
|
||||||
end
|
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
|
#!/usr/bin/env ruby
|
||||||
|
|
||||||
require "fileutils"
|
require "fileutils"
|
||||||
|
require "digest/md5"
|
||||||
|
|
||||||
if File.read("lib/rscons/version.rb") =~ /VERSION = "(.+)"/
|
if File.read("lib/rscons/version.rb") =~ /VERSION = "(.+)"/
|
||||||
VERSION = $1
|
VERSION = $1
|
||||||
else
|
else
|
||||||
raise "Could not determine version."
|
raise "Could not determine version."
|
||||||
end
|
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"
|
PROG_NAME = "rscons"
|
||||||
START_FILE = "bin/#{PROG_NAME}"
|
START_FILE = "bin/#{PROG_NAME}"
|
||||||
LIB_DIR = "lib"
|
LIB_DIR = "lib"
|
||||||
@@ -63,19 +59,22 @@ combined_file.each do |line|
|
|||||||
end
|
end
|
||||||
|
|
||||||
require "zlib"
|
require "zlib"
|
||||||
|
require "base64"
|
||||||
compressed_script = Zlib::Deflate.deflate(stripped_comments.join)
|
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)
|
FileUtils.mkdir_p(DIST)
|
||||||
File.open("#{DIST}/#{PROG_NAME}", "wb", 0755) do |fh|
|
File.open("#{DIST}/#{PROG_NAME}", "wb", 0755) do |fh|
|
||||||
fh.write(<<EOF)
|
fh.write(<<EOF)
|
||||||
#!/usr/bin/env ruby
|
#!/usr/bin/env ruby
|
||||||
script = ".rscons-#{VERSION}-#{GIT}.rb"
|
script = File.join(File.dirname(__FILE__), ".rscons-#{VERSION}-#{hash}.rb")
|
||||||
unless File.exists?(script)
|
unless File.exists?(script)
|
||||||
if File.read(__FILE__, mode: "rb") =~ /^#==>(.*)/
|
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 "zlib"
|
||||||
|
require "base64"
|
||||||
|
encoded_compressed = $1
|
||||||
|
unescaped_compressed = Base64.decode64(encoded_compressed)
|
||||||
inflated = Zlib::Inflate.inflate(unescaped_compressed)
|
inflated = Zlib::Inflate.inflate(unescaped_compressed)
|
||||||
File.open(script, "wb") do |fh|
|
File.open(script, "wb") do |fh|
|
||||||
fh.write(inflated)
|
fh.write(inflated)
|
||||||
@@ -88,6 +87,6 @@ load script
|
|||||||
if __FILE__ == $0
|
if __FILE__ == $0
|
||||||
Rscons::Cli.run(ARGV)
|
Rscons::Cli.run(ARGV)
|
||||||
end
|
end
|
||||||
#==>#{escaped_compressed_script}
|
#==>#{encoded_compressed_script}
|
||||||
EOF
|
EOF
|
||||||
end
|
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)
|
||||||
+688
-252
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
|
||||||
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
|
describe "#mkdir_p" do
|
||||||
it "makes directories and records any created in the cache" do
|
it "makes directories and records any created in the cache" do
|
||||||
_cache = {}
|
_cache = {}
|
||||||
@@ -35,7 +28,7 @@ module Rscons
|
|||||||
expect(FileUtils).to receive(:mkdir_p).with("one/two/four")
|
expect(FileUtils).to receive(:mkdir_p).with("one/two/four")
|
||||||
cache.mkdir_p("one/two/three")
|
cache.mkdir_p("one/two/three")
|
||||||
cache.mkdir_p("one\\two\\four")
|
cache.mkdir_p("one\\two\\four")
|
||||||
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
|
end
|
||||||
|
|
||||||
it "handles absolute paths" do
|
it "handles absolute paths" do
|
||||||
@@ -45,14 +38,7 @@ module Rscons
|
|||||||
expect(File).to receive(:exists?).with("/one/two").and_return(false)
|
expect(File).to receive(:exists?).with("/one/two").and_return(false)
|
||||||
expect(FileUtils).to receive(:mkdir_p).with("/one/two")
|
expect(FileUtils).to receive(:mkdir_p).with("/one/two")
|
||||||
cache.mkdir_p("/one/two")
|
cache.mkdir_p("/one/two")
|
||||||
expect(cache.directories).to eq ["/one/two"]
|
expect(cache.directories(false)).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"]
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -79,12 +65,5 @@ module Rscons
|
|||||||
expect(build_from({}).send(:calculate_checksum, "fname")).to eq "the_checksum"
|
expect(build_from({}).send(:calculate_checksum, "fname")).to eq "the_checksum"
|
||||||
end
|
end
|
||||||
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
|
||||||
end
|
end
|
||||||
@@ -29,6 +29,12 @@ module Rscons
|
|||||||
end
|
end
|
||||||
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
|
describe ".determine_n_threads" do
|
||||||
context "when specified by environment variable" do
|
context "when specified by environment variable" do
|
||||||
before(:each) do
|
before(:each) do
|
||||||
@@ -115,7 +121,10 @@ EOF
|
|||||||
before(:each) do
|
before(:each) do
|
||||||
stub_const("File::PATH_SEPARATOR", ";")
|
stub_const("File::PATH_SEPARATOR", ";")
|
||||||
stub_const("RbConfig::CONFIG", "host_os" => "mingw")
|
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:\\bin").and_return(%w[one.com])
|
||||||
allow(Dir).to receive(:entries).with("C:\\Windows").and_return(%w[two.exe Three.bat])
|
allow(Dir).to receive(:entries).with("C:\\Windows").and_return(%w[two.exe Three.bat])
|
||||||
end
|
end
|
||||||
@@ -151,6 +160,20 @@ EOF
|
|||||||
end
|
end
|
||||||
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
|
describe ".make_relative_path" do
|
||||||
context "when passed a relative path" do
|
context "when passed a relative path" do
|
||||||
it "returns the path itself" do
|
it "returns the path itself" do
|
||||||
@@ -177,12 +200,12 @@ EOF
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe ".parse_makefile_deps" do
|
describe ".parse_dependency_file" do
|
||||||
it 'handles dependencies on one line' do
|
it 'handles dependencies on one line' do
|
||||||
expect(File).to receive(:read).with('makefile').and_return(<<EOS)
|
expect(File).to receive(:read).with('makefile').and_return(<<EOS)
|
||||||
module.o: source.cc
|
module.o: source.cc
|
||||||
EOS
|
EOS
|
||||||
expect(Util.parse_makefile_deps('makefile')).to eq ['source.cc']
|
expect(Util.parse_dependency_file('makefile')).to eq ['source.cc']
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'handles dependencies split across many lines' do
|
it 'handles dependencies split across many lines' do
|
||||||
@@ -191,9 +214,22 @@ module.o: module.c \\
|
|||||||
module.h \\
|
module.h \\
|
||||||
other.h
|
other.h
|
||||||
EOS
|
EOS
|
||||||
expect(Util.parse_makefile_deps('makefile')).to eq [
|
expect(Util.parse_dependency_file('makefile')).to eq [
|
||||||
'module.c', 'module.h', 'other.h']
|
'module.c', 'module.h', 'other.h']
|
||||||
end
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ module Rscons
|
|||||||
expect { v.expand_varref({a: :b}, :lambda_args) }.to raise_error /Unknown construction variable type: Hash/
|
expect { v.expand_varref({a: :b}, :lambda_args) }.to raise_error /Unknown construction variable type: Hash/
|
||||||
end
|
end
|
||||||
it "raises an error when an expanded variable is an unexpected type" do
|
it "raises an error when an expanded variable is an unexpected type" do
|
||||||
expect(v).to receive(:[]).at_least(1).times.with("bad").and_return("bad_val")
|
expect(v).to receive(:get_var).at_least(1).times.with("bad").and_return("bad_val")
|
||||||
expect(v).to receive(:expand_varref).with("bad_val", :lambda_args).and_return({a: :b})
|
expect(v).to receive(:expand_varref).with("bad_val", :lambda_args).and_return({a: :b})
|
||||||
expect(v).to receive(:expand_varref).and_call_original
|
expect(v).to receive(:expand_varref).and_call_original
|
||||||
expect { v.expand_varref("${bad}", :lambda_args) }.to raise_error /Unknown construction variable type: Hash/
|
expect { v.expand_varref("${bad}", :lambda_args) }.to raise_error /Unknown construction variable type: Hash/
|
||||||
|
|||||||
Reference in new issue
Block a user