# Rscons Rscons is a software construction framework inspired by SCons and implemented in Ruby. [![Gem Version](https://badge.fury.io/rb/rscons.png)](http://badge.fury.io/rb/rscons) ## Installation Add this line to your application's Gemfile: gem "rscons" And then execute: $ bundle install Or install it yourself as: $ gem install rscons ## Usage Rscons is implemented as a Ruby library and distributed as a gem. It also provides a "rscons" executable which can evaluate a build script (by default named Rsconsfile or Rsconsfile.rb). It can also be used from rake or from a standalone Ruby script. ### Example: Building a C Program ```ruby Rscons::Environment.new do |env| env["CFLAGS"] << "-Wall" env.Program("program", Dir["**/*.c"]) end ``` ### Example: Building a D Program ```ruby Rscons::Environment.new do |env| env["DFLAGS"] << "-Wall" env.Program("program", Dir["**/*.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", Dir["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", Dir["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 < 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", Dir["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. #### 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", Dir["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`. ### Managing Environments An Rscons::Environment consists of: * a collection of construction variables * a collection of builders * a mapping of build directories from source directories * a default build root to apply if no build directories are matched * a collection of targets to build * a collection of build hooks When cloning an environment, by default the construction variables and builders are cloned, but the new environment does not inherit any of the targets, build hooks, build directories, or the build root from the source environment. The set of environment attributes that are cloned is controllable via the `:clone` option to the `#clone` method. For example, `env.clone(clone: :all)` will include construction variables, builders, build hooks, build directories, and 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". #### 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. ### 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 ``` ### Construction Variable Naming * uppercase strings - the default construction variables that Rscons uses * symbols, lowercase strings - reserved as user-defined construction variables ### API documentation Documentation for the complete Rscons API can be found at http://rubydoc.info/github/holtrop/rscons/frames. ## Release Notes ### v1.9.2 - allow phony targets in conjunction with build roots ### v1.9.1 - change *SUFFIX defaults to arrays - add various C++ file suffixes - use ${INCPREFIX} instead of hard-coded "-I" in Preprocess builder ### v1.9.0 #### New Features - #6 - add Install and Copy builders - #22 - allow overriding Command builder short description with CMD_DESC variable - #24 - add "rscons" executable - #25 - add support for phony targets given as Symbols instead of Strings - #26 - support registering multiple build targets with the same target name - #27 - add Directory builder #### Fixes - #20 - fix variable references that expand to arrays in build target sources - #21 - rework Preprocess builder to consider deep dependencies - fix Rscons.set_suffix to append the given suffix if the filename has none - remove ${CFLAGS} from default CPP_CMD ### v1.8.1 - fix Environment#dump when construction variables are symbols ### v1.8.0 - new Command builder to execute arbitrary user commands - new SimpleBuilder class - create new builders quickly by passing a block to Environment#add_builder - improved YARD documentation - add Environment#dump to debug Environment construction variables ### v1.7.0 - allow build hooks to register new build targets - add post-build hooks (register with Environment#add_post_build_hook) - clear all build targets after processing an Environment - allow trailing slashes in arguments to Environment#build_dir ### v1.6.1 - add DEPFILESUFFIX construction variable to override dependency file suffix - fix Environment#depends to expand its arguments for construction variables ### v1.6.0 - support lambdas as construction variable values ### v1.5.0 - add "json" as a runtime dependency - update construction variables to match SCons more closely - add CPPDEFPREFIX, INCPREFIX, CPPDEFINES, CCFLAGS, LIBDIRPREFIX, and LIBLINKPREFIX - add Environment#shell - add Environment#parse_flags, #parse_flags!, #merge_flags - unbuffer $stdout by default - add PROGSUFFIX construction variable (defaults to .exe on MinGW/Cygwin) - add Rscons::BuildTarget and Builder#create_build_target - update specs to RSpec 3.x and fix to run on MinGW/Cygwin/Linux - add YARD documentation to get to 100% coverage ### v1.4.3 - fix builders properly using construction variable overrides - expand nil construction variables to empty strings ### v1.4.2 - add Environment#expand_path - expand construction variable references in builder targets and sources before invoking builder ### v1.4.1 - fix invoking a builder with no sources while a build root defined ### v1.4.0 - add CFile builder - add Disassemble builder - add Preprocess builder - pass the Environment object to build hooks in the :env key of the build_op parameter - expand target/source paths beginning with "^/" to be relative to the Environment's build root - many performance improvements, including: - use JSON instead of YAML for the cache to improve loading speed (Issue #7) - store a hash of the build command instead of the full command contents in the cache - implement copy-on-write semantics for construction variables when cloning Environments - only load the cache once instead of on each Environment#process - only write the cache when something has changed - fix Cache#mkdir_p to handle relative paths (Issue #5) - flush the cache to disk if a builder raises an exception (Issue #4) ### v1.3.0 - change Environment#execute() options parameter to accept the following options keys: - :env to pass an environment Hash to Kernel#system - :options to pass an options Hash to Kernel#system ### v1.2.0 - add :clone option to Environment#clone to control exactly which Environment attributes are cloned - allow nil to be passed in to Environment#build_root= ### v1.1.0 - Change Cache#up_to_date?() and #register_build() to accept a single target file or an array of target file names ## Contributing 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