Compare commits

...
31 Commits
Author SHA1 Message Date
josh e900e74342 v1.14.0 2017-08-03 15:55:54 -04:00
josh 62ae3f162d add v1.14.0 release notes 2017-08-03 15:55:38 -04:00
josh 099d26f33c Add Rscons::VarSet#values_at - close #45 2017-08-03 15:54:24 -04:00
josh 89562c584a Environment#print_builder_run_message should support string commands - close #44 2017-08-03 14:10:53 -04:00
josh a9aeaf2356 v1.13.0 2017-07-14 16:24:27 -04:00
josh 5409b3cb34 add v1.13.0 release notes 2017-07-14 16:24:04 -04:00
josh 6d04500c13 Add ability to record side-effect file production - close #43 2017-07-14 16:17:01 -04:00
josh 45e30662d2 add YARD comment for Rscons::Ansi to get to 100% coverage 2017-07-12 15:51:00 -04:00
josh 8f7e044bfd fix YARD @option tag 2017-07-12 15:50:13 -04:00
josh dbea6f0a26 v1.12.0 2017-07-11 16:16:01 -04:00
josh c923eb5c7c add v1.12.0 release notes 2017-07-11 16:14:49 -04:00
josh 529d9aacaa colorize a couple other builders' output messages 2017-07-11 16:12:38 -04:00
josh 65c1bac4f9 be more colorful - close #41 2017-07-11 13:52:01 -04:00
josh 775363ddbd cloned Environments should inherit n_threads - close #42 2017-07-11 10:29:25 -04:00
josh 2a96495e83 wait for in-progress subcommands to complete on build failure - close #39 2017-06-26 22:44:49 -04:00
josh 15e52e488c env.depends should imply env.build_after - close #40 2017-06-21 10:44:52 -04:00
josh c0b12132f0 1.11.1 release notes 2017-06-14 09:58:09 -04:00
josh 9141cf91e7 v1.11.1 2017-06-14 09:57:11 -04:00
josh 7f7e6ef3da fix circular build dependency detection logic 2017-06-14 09:56:51 -04:00
josh 60b3fdbcac update YARD documentation link 2017-06-14 09:12:13 -04:00
josh 5263ea03c6 v1.11.0 2017-06-14 08:56:21 -04:00
josh 6c91823ce2 v1.11.0 release notes 2017-06-14 08:54:57 -04:00
josh 8deb1eb6f8 Change default :clone option to :all to clone all Environment attributes 2017-06-14 08:53:28 -04:00
josh 6d6319799a add v1.10.1 release notes 2017-06-13 19:47:58 -04:00
josh 694642dfbd Allow overriding n_threads on a per-Environment level - close #34 2017-06-13 19:45:53 -04:00
josh 6dd70ff65e raise error when circular dependencies are found - close #38 2017-06-13 19:37:41 -04:00
josh eca01c38df Fix non-blocking thread-wait if Rscons.n_threads is set to 0 - close #37 2017-06-13 19:07:04 -04:00
josh 4ad6ce1d9c SHCFLAGS and SHCXXFLAGS should inherit non-SH flags by default - close #36 2017-06-13 18:41:47 -04:00
josh a49ebc486e env.build_after should expand paths - close #35 2017-06-13 18:38:47 -04:00
josh 74d19ea235 fix construction variable reference table OBJSUFFIX entries 2017-06-12 09:33:52 -04:00
josh d7a6d69a92 update v1.10.0 release notes 2017-06-12 09:33:13 -04:00
27 changed files with 502 additions and 91 deletions

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
PATH PATH
remote: . remote: .
specs: specs:
rscons (1.10.0) rscons (1.13.0)
json (~> 1.0) json (~> 1.0)
GEM GEM
+58 -10
View File
@@ -230,14 +230,15 @@ An Environment consists of:
* a collection of user-defined build targets * a collection of user-defined build targets
* a collection of user-defined build hooks * a collection of user-defined build hooks
When cloning an environment, by default the construction variables and builders When cloning an environment, by default the construction variables, builders,
are cloned, but the new environment does not inherit any of the targets, build build hooks, build directories, and build root are cloned, but the new
hooks, build directories, or the build root from the source environment. environment does not inherit any of the registered build targets.
The set of environment attributes that are cloned is controllable via the The set of environment attributes that are cloned is controllable via the
`:clone` option to the `#clone` method. `:clone` option to the `#clone` method.
For example, `env.clone(clone: :all)` will include construction variables, For example, `env.clone(clone: [:variables, :builders])` will include
builders, build hooks, build directories, and the build root. construction variables, and builders but not build hooks, build directories, or
the build root.
The set of pending targets is never cloned. The set of pending targets is never cloned.
@@ -455,16 +456,14 @@ construction variables can be overridden by the user.
| LIBSUFFIX | String/Array | Default static library file suffix | ".a" | | LIBSUFFIX | String/Array | Default static library file suffix | ".a" |
| OBJDUMP | String | Objdump executable | "objdump" | | OBJDUMP | String | Objdump executable | "objdump" |
| OBJSUFFIX | String/Array | Default object file suffix | ".o" | | OBJSUFFIX | String/Array | Default object file suffix | ".o" |
| This name will include variant components. |
| The user should not change this construction variable. | Determined at runtime. |
| PROGSUFFIX | String | Default program suffix. | Windows: ".exe", POSIX: "" | | PROGSUFFIX | String | Default program suffix. | Windows: ".exe", POSIX: "" |
| SHCC | String | Shared object C compiler | "${CC}" | | 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}"] | | 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"] | | SHCCFLAGS | Array | Shared object C and C++ compiler flags | Windows: ["${CCFLAGS}"], POSIX: ["${CCFLAGS}", -fPIC"] |
| SHCFLAGS | Array | Shared object C compiler flags | [] | | SHCFLAGS | Array | Shared object C compiler flags | ["${CFLAGS}"] |
| SHCXX | String | Shared object C++ compiler | "${CXX}" | | 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}"] | | 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 | [] | | SHCXXFLAGS | Array | Shared object C++ compiler flags | ["${CXXFLAGS}"] |
| SHDC | String | Shared object D compiler | "gdc" | | 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}"] | | 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"] | | SHDFLAGS | Array | Shared object D compiler flags | Windows: ["${DFLAGS}"], POSIX: ["${DFLAGS}", "-fPIC"] |
@@ -561,10 +560,58 @@ env.depends("my_app", "config/link.ld", "README.txt", *Dir.glob("assets/**/*"))
### API documentation ### API documentation
Documentation for the complete Rscons API can be found at Documentation for the complete Rscons API can be found at
http://rubydoc.info/github/holtrop/rscons/frames. http://www.rubydoc.info/github/holtrop/rscons/master.
## Release Notes ## Release Notes
### v1.14.0
#### New Features
- #45 - Add Rscons::VarSet#values_at
#### Fixes
- #44 - Environment#print_builder_run_message should support string commands
### v1.13.0
#### New Features
- #43 - Add ability to record side-effect file production
### v1.12.0
#### New Features
- #40 - env.depends should imply env.build_after
- #41 - be more colorful
#### Fixes
- #39 - wait for in-progress subcommands to complete on build failure
- #42 - cloned Environments should inherit n_threads
### v1.11.1
#### Fixes
- fix the circular build dependency detection logic
### v1.11.0
#### New Features
- Change default Environment :clone option to :all to clone all attributes
- #38 - raise error when circular dependencies are found
- #34 - Allow overriding n_threads on a per-Environment level
#### Fixes
- #35 - env.build_after should expand paths
- #36 - SHCFLAGS and SHCXXFLAGS should inherit non-SH flags by default
- #37 - Fix non-blocking thread-wait if Rscons.n_threads is set to 0
### v1.10.0 ### v1.10.0
#### New Features #### New Features
@@ -581,6 +628,7 @@ http://rubydoc.info/github/holtrop/rscons/frames.
- 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
### v1.9.3 ### v1.9.3
@@ -0,0 +1,5 @@
Rscons::Environment.new do |env|
env.echo = :command
env.Install("inst.exe", "install.rb")
end
@@ -0,0 +1,13 @@
class MyBuilder < Rscons::Builder
def run(options)
env, target = options.values_at(:env, :target)
env.print_builder_run_message("MyBuilder #{target}", "MyBuilder #{target} command")
target
end
end
Rscons::Environment.new do |env|
env.echo = :command
env.add_builder(MyBuilder.new)
env.MyBuilder("foo")
end
+1 -1
View File
@@ -10,6 +10,6 @@ env1 = Rscons::Environment.new(echo: :command) do |env|
env.Program('program.exe', Dir['src/*.c']) env.Program('program.exe', Dir['src/*.c'])
end end
env2 = env1.clone(clone: :all) do |env| env2 = env1.clone do |env|
env.Program('program2.exe', Dir['src/*.c']) env.Program('program2.exe', Dir['src/*.c'])
end end
+3 -1
View File
@@ -3,6 +3,8 @@ Rscons::Environment.new do |env|
[], [],
"CMD" => %w[ruby gen.rb ${_TARGET}], "CMD" => %w[ruby gen.rb ${_TARGET}],
"CMD_DESC" => "Generating") "CMD_DESC" => "Generating")
env.build_after("#{env.build_root}/program.o", "inc.c") env["build_root"] = env.build_root
env["inc_c"] = "inc.c"
env.build_after("${build_root}/program.o", "${inc_c}")
env.Program("program.exe", ["program.c", "inc.c"]) env.Program("program.exe", ["program.c", "inc.c"])
end end
+14
View File
@@ -0,0 +1,14 @@
Rscons::Environment.new do |env|
env["build_root"] = env.build_root
env["inc_h"] = "inc.h"
env.Copy("copy_inc.h", "${inc_h}")
env.depends("${build_root}/program.o", "${inc_h}")
env.Program("program.exe", ["program.c", "inc.c"])
env.Command("inc.c",
[],
"CMD" => %w[ruby gen.rb ${_TARGET}],
"CMD_DESC" => "Generating")
env.produces("inc.c", "inc.h")
end
+7
View File
@@ -0,0 +1,7 @@
base_env = Rscons::Environment.new do |env|
env.n_threads = 165
end
my_env = base_env.clone
puts my_env.n_threads
@@ -0,0 +1,5 @@
Rscons::Environment.new do |env|
env.Command("foo", "bar")
env.Command("bar", "baz")
env.Command("baz", "foo")
end
@@ -0,0 +1,3 @@
Rscons::Environment.new do |env|
env.Command("foo", "foo")
end
@@ -0,0 +1,22 @@
class TestBuilder < Rscons::Builder
def run(options)
target, env, vars, cache = options.values_at(:target, :env, :vars, :cache)
if target == "two"
return false unless File.exists?("one")
end
wait_time = env.expand_varref("${wait_time}", vars)
command = ["ruby", "-e", "require 'fileutils'; sleep #{wait_time}; FileUtils.touch('#{target}');"]
standard_threaded_build("TestBuilder", target, command, [], env, cache)
end
def finalize(options)
standard_finalize(options)
end
end
Rscons::Environment.new do |env|
env.add_builder(TestBuilder.new)
env.TestBuilder("one", [], "wait_time" => "3")
env.TestBuilder("two", [], "wait_time" => "0")
env.depends("two", "one")
end
@@ -0,0 +1,20 @@
class Fail < Rscons::Builder
def run(options)
target, cache, env, vars = options.values_at(:target, :cache, :env, :vars)
wait_time = env.expand_varref("${wait_time}", vars)
ruby_command = %[sleep #{wait_time}; exit 2]
command = %W[ruby -e #{ruby_command}]
standard_threaded_build("Fail #{target}", target, command, [], env, cache)
end
def finalize(options)
standard_finalize(options)
end
end
Rscons::Environment.new do |env|
env.add_builder(Fail.new)
4.times do |i|
wait_time = i + 1
env.Fail("foo_#{wait_time}", [], "wait_time" => wait_time.to_s)
end
end
+5
View File
@@ -1,3 +1,4 @@
require_relative "rscons/ansi"
require_relative "rscons/build_target" require_relative "rscons/build_target"
require_relative "rscons/builder" require_relative "rscons/builder"
require_relative "rscons/cache" require_relative "rscons/cache"
@@ -50,6 +51,10 @@ module Rscons
# The number of threads to use when scheduling subprocesses. # The number of threads to use when scheduling subprocesses.
attr_accessor :n_threads attr_accessor :n_threads
# @return [Boolean]
# Whether to output ANSI color escape sequences.
attr_accessor :do_ansi_color
# Remove all generated files. # Remove all generated files.
# #
# @return [void] # @return [void]
+53
View File
@@ -0,0 +1,53 @@
module Rscons
# Module to contain logic to write ANSI color escape codes.
module Ansi
class << self
# Write a message to an IO with ANSI escape codes.
#
# @param io [IO]
# The IO to write to.
# @param message [Array<String, Symbol>]
# Strings to be printed, with Symbols representing ANSI escape codes.
#
# @return [void]
def write(io, *message)
do_color = Rscons.do_ansi_color
if do_color.nil?
do_color = do_ansi?(io)
end
out = ""
message.each do |m|
if m.is_a?(String)
out += m
elsif do_color
case m
when :red
out += "\e[0;31m"
when :cyan
out += "\e[0;36m"
when :reset
out += "\e[0m"
end
end
end
io.write(out)
end
private
# Determine whether to output ANSI color escape codes.
#
# @return [Boolean]
# Whether to output ANSI color escape codes.
def do_ansi?(io)
if RUBY_PLATFORM =~ /mingw/
(ENV["TERM"] == "xterm") && %w[fifo characterSpecial].include?(io.stat.ftype)
else
io.tty?
end
end
end
end
end
+1 -1
View File
@@ -231,7 +231,7 @@ module Rscons
# @param env [Environment] The Environment executing the builder. # @param env [Environment] The Environment executing the builder.
# @param cache [Cache] The Cache object. # @param cache [Cache] The Cache object.
# @param options [Hash] Options. # @param options [Hash] Options.
# @options options [String] :stdout # @option options [String] :stdout
# File name to redirect standard output to. # File name to redirect standard output to.
# #
# @return [String,ThreadedCommand] # @return [String,ThreadedCommand]
+2 -2
View File
@@ -17,10 +17,10 @@ module Rscons
if File.directory?(target) if File.directory?(target)
target target
elsif File.exists?(target) elsif File.exists?(target)
$stderr.puts "Error: `#{target}' already exists and is not a directory" Ansi.write($stderr, :red, "Error: `#{target}' already exists and is not a directory", :reset, "\n")
false false
else else
puts "Directory #{target}" env.print_builder_run_message("Directory #{target}", nil)
cache.mkdir_p(target) cache.mkdir_p(target)
target target
end end
+1 -1
View File
@@ -43,7 +43,7 @@ module Rscons
# Check the cache and copy if necessary # Check the cache and copy if necessary
unless cache.up_to_date?(dest, :Copy, [src], env) unless cache.up_to_date?(dest, :Copy, [src], env)
unless printed_message unless printed_message
puts "#{name} #{target}" env.print_builder_run_message("#{name} #{target}", nil)
printed_message = true printed_message = true
end end
cache.mkdir_p(File.dirname(dest)) cache.mkdir_p(File.dirname(dest))
+2 -2
View File
@@ -24,11 +24,11 @@ module Rscons
'SHCCFLAGS' => ['${CCFLAGS}'] + pic_flags, 'SHCCFLAGS' => ['${CCFLAGS}'] + pic_flags,
'SHCC' => '${CC}', 'SHCC' => '${CC}',
'SHCFLAGS' => [], 'SHCFLAGS' => ['${CFLAGS}'],
'SHCCCMD' => ['${SHCC}', '-c', '-o', '${_TARGET}', '${CCDEPGEN}', '${INCPREFIX}${CPPPATH}', '${CPPFLAGS}', '${SHCFLAGS}', '${SHCCFLAGS}', '${_SOURCES}'], 'SHCCCMD' => ['${SHCC}', '-c', '-o', '${_TARGET}', '${CCDEPGEN}', '${INCPREFIX}${CPPPATH}', '${CPPFLAGS}', '${SHCFLAGS}', '${SHCCFLAGS}', '${_SOURCES}'],
'SHCXX' => '${CXX}', 'SHCXX' => '${CXX}',
'SHCXXFLAGS' => [], 'SHCXXFLAGS' => ['${CXXFLAGS}'],
'SHCXXCMD' => ['${SHCXX}', '-c', '-o', '${_TARGET}', '${CXXDEPGEN}', '${INCPREFIX}${CPPPATH}', '${CPPFLAGS}', '${SHCXXFLAGS}', '${SHCCFLAGS}', '${_SOURCES}'], 'SHCXXCMD' => ['${SHCXX}', '-c', '-o', '${_TARGET}', '${CXXDEPGEN}', '${INCPREFIX}${CPPPATH}', '${CPPFLAGS}', '${SHCXXFLAGS}', '${SHCCFLAGS}', '${_SOURCES}'],
'SHDC' => 'gdc', 'SHDC' => 'gdc',
+9 -1
View File
@@ -39,6 +39,15 @@ module Rscons
Rscons.n_threads = n_threads.to_i Rscons.n_threads = n_threads.to_i
end end
opts.on("-r", "--color MODE", "Set color mode (off, auto, force)") do |color_mode|
case color_mode
when "off"
Rscons.do_ansi_color = false
when "force"
Rscons.do_ansi_color = true
end
end
opts.on_tail("--version", "Show version") do opts.on_tail("--version", "Show version") do
puts "Rscons version #{Rscons::VERSION}" puts "Rscons version #{Rscons::VERSION}"
exit 0 exit 0
@@ -70,7 +79,6 @@ module Rscons
begin begin
load rsconsfile load rsconsfile
rescue Rscons::BuildError => e rescue Rscons::BuildError => e
$stderr.puts e.message
exit 1 exit 1
end end
+103 -26
View File
@@ -17,6 +17,11 @@ module Rscons
# @return [String] The build root. # @return [String] The build root.
attr_reader :build_root attr_reader :build_root
# @return [Integer]
# The number of threads to use for this Environment. If nil (the
# default), the global Rscons.n_threads default value will be used.
attr_writer :n_threads
# Set the build root. # Set the build root.
# #
# @param build_root [String] The build root. # @param build_root [String] The build root.
@@ -40,8 +45,9 @@ module Rscons
def initialize(options = {}) def initialize(options = {})
@threaded_commands = Set.new @threaded_commands = Set.new
@registered_build_dependencies = {} @registered_build_dependencies = {}
@side_effects = {}
@varset = VarSet.new @varset = VarSet.new
@job_set = JobSet.new(@registered_build_dependencies) @job_set = JobSet.new(@registered_build_dependencies, @side_effects)
@user_deps = {} @user_deps = {}
@builders = {} @builders = {}
@build_dirs = [] @build_dirs = []
@@ -73,9 +79,9 @@ module Rscons
# following: # following:
# - :variables to clone construction variables (on by default) # - :variables to clone construction variables (on by default)
# - :builders to clone the builders (on by default) # - :builders to clone the builders (on by default)
# - :build_root to clone the build root (off by default) # - :build_root to clone the build root (on by default)
# - :build_dirs to clone the build directories (off by default) # - :build_dirs to clone the build directories (on by default)
# - :build_hooks to clone the build hooks (off by default) # - :build_hooks to clone the build hooks (on by default)
# #
# 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.
@@ -84,7 +90,7 @@ module Rscons
# #
# @return [Environment] The newly created {Environment} object. # @return [Environment] The newly created {Environment} object.
def clone(options = {}) def clone(options = {})
clone = options[:clone] || Set[:variables, :builders] clone = options[:clone] || :all
clone = Set[:variables, :builders, :build_root, :build_dirs, :build_hooks] if clone == :all clone = Set[:variables, :builders, :build_root, :build_dirs, :build_hooks] if clone == :all
clone = Set[] if clone == :none clone = Set[] if clone == :none
clone = Set.new(clone) if clone.is_a?(Array) clone = Set.new(clone) if clone.is_a?(Array)
@@ -101,7 +107,7 @@ module Rscons
env.append(@varset) if clone.include?(:variables) env.append(@varset) if clone.include?(:variables)
env.build_root = @build_root if clone.include?(:build_root) env.build_root = @build_root if clone.include?(:build_root)
if clone.include?(:build_dirs) if clone.include?(:build_dirs)
@build_dirs.each do |src_dir, obj_dir| @build_dirs.reverse.each do |src_dir, obj_dir|
env.build_dir(src_dir, obj_dir) env.build_dir(src_dir, obj_dir)
end end
end end
@@ -113,6 +119,7 @@ module Rscons
env.add_post_build_hook(&build_hook_block) env.add_post_build_hook(&build_hook_block)
end end
end end
env.instance_variable_set(:@n_threads, @n_threads)
if block_given? if block_given?
yield env yield env
@@ -222,7 +229,7 @@ module Rscons
if src_dir.is_a?(String) if src_dir.is_a?(String)
src_dir = src_dir.gsub("\\", "/").sub(%r{/*$}, "") src_dir = src_dir.gsub("\\", "/").sub(%r{/*$}, "")
end end
@build_dirs << [src_dir, obj_dir] @build_dirs.unshift([src_dir, obj_dir])
end end
# Return the file name to be built from +source_fname+ with suffix # Return the file name to be built from +source_fname+ with suffix
@@ -297,13 +304,19 @@ module Rscons
# @return [void] # @return [void]
def process def process
cache = Cache.instance cache = Cache.instance
failure = nil
begin begin
while @job_set.size > 0 or @threaded_commands.size > 0 while @job_set.size > 0 or @threaded_commands.size > 0
if failure
@job_set.clear!
job = nil
else
targets_still_building = @threaded_commands.map do |tc| targets_still_building = @threaded_commands.map do |tc|
tc.build_operation[:target] tc.build_operation[:target]
end end
job = @job_set.get_next_job_to_run(targets_still_building) job = @job_set.get_next_job_to_run(targets_still_building)
end
# TODO: have Cache determine when checksums may be invalid based on # TODO: have Cache determine when checksums may be invalid based on
# file size and/or timestamp. # file size and/or timestamp.
@@ -318,7 +331,9 @@ module Rscons
allow_delayed_execution: true, allow_delayed_execution: true,
setup_info: job[:setup_info]) setup_info: job[:setup_info])
unless result unless result
raise BuildError.new("Failed to build #{job[:target]}") failure = "Failed to build #{job[:target]}"
Ansi.write($stderr, :red, failure, :reset, "\n")
next
end end
end end
@@ -330,7 +345,8 @@ module Rscons
end end
# If needed, do a blocking wait. # If needed, do a blocking wait.
if (completed_tcs.empty? and job.nil?) or @threaded_commands.size >= Rscons.n_threads if (@threaded_commands.size > 0) and
((completed_tcs.empty? and job.nil?) or (@threaded_commands.size >= n_threads))
completed_tcs << wait_for_threaded_commands completed_tcs << wait_for_threaded_commands
end end
@@ -343,9 +359,11 @@ module Rscons
end end
else else
unless @echo == :command unless @echo == :command
$stdout.puts "Failed command was: #{command_to_s(tc.command)}" print_failed_command(tc.command)
end end
raise BuildError.new("Failed to build #{tc.build_operation[:target]}") failure = "Failed to build #{tc.build_operation[:target]}"
Ansi.write($stderr, :red, failure, :reset, "\n")
break
end end
end end
@@ -353,6 +371,9 @@ module Rscons
ensure ensure
cache.write cache.write
end end
if failure
raise BuildError.new(failure)
end
end end
# Clear all targets registered for the Environment. # Clear all targets registered for the Environment.
@@ -392,16 +413,12 @@ module Rscons
# #
# @return [true,false,nil] Return value from Kernel.system(). # @return [true,false,nil] Return value from Kernel.system().
def execute(short_desc, command, options = {}) def execute(short_desc, command, options = {})
if @echo == :command print_builder_run_message(short_desc, command)
puts command_to_s(command)
elsif @echo == :short
puts short_desc
end
env_args = options[:env] ? [options[:env]] : [] env_args = options[:env] ? [options[:env]] : []
options_args = options[:options] ? [options[:options]] : [] options_args = options[:options] ? [options[:options]] : []
system(*env_args, *Rscons.command_executer, *command, *options_args).tap do |result| system(*env_args, *Rscons.command_executer, *command, *options_args).tap do |result|
unless result or @echo == :command unless result or @echo == :command
$stdout.puts "Failed command was: #{command_to_s(command)}" print_failed_command(command)
end end
end end
end end
@@ -445,6 +462,7 @@ module Rscons
user_deps = user_deps.map {|ud| expand_varref(ud)} user_deps = user_deps.map {|ud| expand_varref(ud)}
@user_deps[target] ||= [] @user_deps[target] ||= []
@user_deps[target] = (@user_deps[target] + user_deps).uniq @user_deps[target] = (@user_deps[target] + user_deps).uniq
build_after(target, user_deps)
end end
# Manually record the given target(s) as needing to be built after the # Manually record the given target(s) as needing to be built after the
@@ -476,13 +494,35 @@ module Rscons
targets = Array(targets) targets = Array(targets)
prerequisites = Array(prerequisites) prerequisites = Array(prerequisites)
targets.each do |target| targets.each do |target|
target = expand_path(expand_varref(target))
@registered_build_dependencies[target] ||= Set.new @registered_build_dependencies[target] ||= Set.new
prerequisites.each do |prerequisite| prerequisites.each do |prerequisite|
prerequisite = expand_path(expand_varref(prerequisite))
@registered_build_dependencies[target] << prerequisite @registered_build_dependencies[target] << prerequisite
end end
end end
end end
# Manually record the given side effect file(s) as being produced when the
# named target is produced.
#
# @since 1.13.0
#
# @param target [String]
# Target of a build operation.
# @param side_effects [Array<String>]
# File(s) produced when the target file is produced.
#
# @return [void]
def produces(target, *side_effects)
target = expand_path(expand_varref(target))
side_effects = Array(side_effects).map do |side_effect|
expand_path(expand_varref(side_effect))
end.flatten
@side_effects[target] ||= []
@side_effects[target] += side_effects
end
# Return the list of user dependencies for a given target. # Return the list of user dependencies for a given target.
# #
# @param target [String] Target file name. # @param target [String] Target file name.
@@ -634,7 +674,7 @@ module Rscons
call_build_hooks[:post] call_build_hooks[:post]
else else
unless @echo == :command unless @echo == :command
$stdout.puts "Failed command was: #{command_to_s(tc.command)}" print_failed_command(tc.command)
end end
end end
end end
@@ -821,10 +861,53 @@ module Rscons
varset_hash = @varset.to_h varset_hash = @varset.to_h
varset_hash.keys.sort_by(&:to_s).each do |var| varset_hash.keys.sort_by(&:to_s).each do |var|
var_str = var.is_a?(Symbol) ? var.inspect : var var_str = var.is_a?(Symbol) ? var.inspect : var
puts "#{var_str} => #{varset_hash[var].inspect}" Ansi.write($stdout, :cyan, var_str, :reset, " => #{varset_hash[var].inspect}\n")
end end
end end
# Get the number of threads to use for parallelized builds in this
# Environment.
#
# @return [Integer]
# Number of threads to use for parallelized builds in this Environment.
def n_threads
@n_threads || Rscons.n_threads
end
# Print the builder run message, depending on the Environment's echo mode.
#
# @param short_description [String]
# Builder short description, printed if the echo mode is :short.
# @param command [Array<String>]
# Builder command, printed if the echo mode is :command.
#
# @return [void]
def print_builder_run_message(short_description, command)
case @echo
when :command
if command.is_a?(Array)
message = command_to_s(command)
elsif command.is_a?(String)
message = command
elsif short_description.is_a?(String)
message = short_description
end
when :short
message = short_description if short_description
end
Ansi.write($stdout, :cyan, message, :reset, "\n") if message
end
# Print a failed command.
#
# @param command [Array<String>]
# Builder command.
#
# @return [void]
def print_failed_command(command)
Ansi.write($stdout, :red, "Failed command was: #{command_to_s(command)}", :reset, "\n")
end
private private
# Add a build target. # Add a build target.
@@ -857,13 +940,7 @@ module Rscons
# #
# @return [void] # @return [void]
def start_threaded_command(tc) def start_threaded_command(tc)
if @echo == :command print_builder_run_message(tc.short_description, tc.command)
puts command_to_s(tc.command)
elsif @echo == :short
if tc.short_description
puts tc.short_description
end
end
env_args = tc.system_env ? [tc.system_env] : [] env_args = tc.system_env ? [tc.system_env] : []
options_args = tc.system_options ? [tc.system_options] : [] options_args = tc.system_options ? [tc.system_options] : []
+19 -11
View File
@@ -9,9 +9,14 @@ module Rscons
# @param build_dependencies [Hash] # @param build_dependencies [Hash]
# Hash mapping targets to a set of build dependencies. A job will not be # Hash mapping targets to a set of build dependencies. A job will not be
# returned as ready to run if any of its dependencies are still building. # returned as ready to run if any of its dependencies are still building.
def initialize(build_dependencies) # @param side_effects [Hash]
# Hash mapping targets to a set of side-effect files. A job will not be
# returned as ready to run if any of its dependencies is a side-effect
# of another target that has not yet been built.
def initialize(build_dependencies, side_effects)
@jobs = {} @jobs = {}
@build_dependencies = build_dependencies @build_dependencies = build_dependencies
@side_effects = side_effects
end end
# Add a job to the JobSet. # Add a job to the JobSet.
@@ -47,18 +52,16 @@ module Rscons
# @return [nil, Hash] # @return [nil, Hash]
# The next job to run. # The next job to run.
def get_next_job_to_run(targets_still_building) def get_next_job_to_run(targets_still_building)
targets_not_built_yet = targets_still_building + @jobs.keys
side_effects = targets_not_built_yet.map do |target|
@side_effects[target] || []
end.flatten
targets_not_built_yet += side_effects
@jobs.keys.each do |target| @jobs.keys.each do |target|
skip = false skip = false
(@jobs[target][0][:sources] + (@build_dependencies[target] || []).to_a).each do |src| (@jobs[target][0][:sources] + (@build_dependencies[target] || []).to_a).each do |src|
if @jobs.include?(src) if targets_not_built_yet.include?(src)
# Skip this target because it depends on another target later in
# the job set.
skip = true
break
end
if targets_still_building.include?(src)
# Skip this target because it depends on another target that is
# still being built.
skip = true skip = true
break break
end end
@@ -73,7 +76,12 @@ module Rscons
return job return job
end end
nil # If there is a job to run, and nothing is still building, but we did
# not find a job to run above, then there might be a circular dependency
# introduced by the user.
if (@jobs.size > 0) and targets_still_building.empty?
raise "Could not find a runnable job. Possible circular dependency for #{@jobs.keys.first}"
end
end end
# Remove all jobs from the JobSet. # Remove all jobs from the JobSet.
+13
View File
@@ -143,6 +143,19 @@ module Rscons
end end
end end
# Return an array containing the values associated with the given keys.
#
# @param keys [Array<String, Symbol>]
# Keys to look up in the VarSet.
#
# @return [Array]
# An array containing the values associated with the given keys.
def values_at(*keys)
keys.map do |key|
self[key]
end
end
private private
# Move all VarSet variables into the copy-on-access list. # Move all VarSet variables into the copy-on-access list.
+1 -1
View File
@@ -1,4 +1,4 @@
module Rscons module Rscons
# gem version # gem version
VERSION = "1.10.0" VERSION = "1.14.0"
end end
+99 -1
View File
@@ -336,7 +336,7 @@ EOF
] ]
end end
it 'allows cloning all attributes of an Environment object' do it 'clones all attributes of an Environment object by default' do
test_dir('clone_env') test_dir('clone_env')
result = run_test(rsconsfile: "clone_all.rb") result = run_test(rsconsfile: "clone_all.rb")
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
@@ -732,6 +732,82 @@ EOF
expect(`./test-static.exe`).to match /Hi from one/ expect(`./test-static.exe`).to match /Hi from one/
end end
it "raises an error for a circular dependency" do
test_dir("simple")
result = run_test(rsconsfile: "error_circular_dependency.rb")
expect(result.stderr).to match /Possible circular dependency for (foo|bar|baz)/
expect(result.status).to_not eq 0
end
it "raises an error for a circular dependency where a build target contains itself in its source list" do
test_dir("simple")
result = run_test(rsconsfile: "error_circular_dependency2.rb")
expect(result.stderr).to match /Possible circular dependency for foo/
expect(result.status).to_not eq 0
end
it "orders builds to respect user dependencies" do
test_dir("simple")
result = run_test(rsconsfile: "user_dep_build_order.rb", rscons_args: %w[-j4])
expect(result.stderr).to eq ""
end
it "waits for all parallelized builds to complete if one fails" do
test_dir("simple")
result = run_test(rsconsfile: "wait_for_builds_on_failure.rb", rscons_args: %w[-j4])
expect(result.status).to_not eq 0
expect(result.stderr).to match /Failed to build foo_1/
expect(result.stderr).to match /Failed to build foo_2/
expect(result.stderr).to match /Failed to build foo_3/
expect(result.stderr).to match /Failed to build foo_4/
end
it "clones n_threads attribute when cloning an Environment" do
test_dir("simple")
result = run_test(rsconsfile: "clone_n_threads.rb")
expect(result.stderr).to eq ""
expect(lines(result.stdout)).to eq ["165"]
end
it "prints a builder's short description with 'command' echo mode if there is no command" do
test_dir("build_dir")
result = run_test(rsconsfile: "echo_command_ruby_builder.rb")
expect(result.stderr).to eq ""
expect(lines(result.stdout)).to eq ["Install inst.exe"]
end
it "supports a string for a builder's echoed 'command' with Environment#print_builder_run_message" do
test_dir("build_dir")
result = run_test(rsconsfile: "echo_command_string.rb")
expect(result.stderr).to eq ""
expect(lines(result.stdout)).to eq ["MyBuilder foo command"]
end
context "colored output" do
it "does not output in color with --color=off" do
test_dir("simple")
result = run_test(rscons_args: %w[--color=off])
expect(result.stderr).to eq ""
expect(result.stdout).to_not match(/\e\[/)
end
it "displays output in color with --color=force" do
test_dir("simple")
result = run_test(rscons_args: %w[--color=force])
expect(result.stderr).to eq ""
expect(result.stdout).to match(/\e\[/)
File.open("simple.c", "wb") do |fh|
fh.write("foobar")
end
result = run_test(rscons_args: %w[--color=force])
expect(result.stderr).to match(/\e\[/)
end
end
context "backward compatibility" do context "backward compatibility" do
it "allows a builder to call Environment#run_builder in a non-threaded manner" do it "allows a builder to call Environment#run_builder in a non-threaded manner" do
test_dir("simple") test_dir("simple")
@@ -754,6 +830,21 @@ EOF
] ]
end end
it "prints the failed build command for a non-threaded builder" do
test_dir("simple")
File.open("simple.c", "wb") do |fh|
fh.write(<<EOF)
void one()
{
}
EOF
end
result = run_test(rsconsfile: "build_sources.rb")
expect(result.status).to_not eq 0
expect(result.stderr).to match /main/
expect(result.stdout).to match /Failed command was: gcc/
end
it "prints the failed build command for a threaded builder when called via Environment#run_builder without delayed execution" do it "prints the failed build command for a threaded builder when called via Environment#run_builder without delayed execution" do
test_dir("simple") test_dir("simple")
File.open("simple.c", "wb") do |fh| File.open("simple.c", "wb") do |fh|
@@ -1231,6 +1322,13 @@ EOF
result = run_test(rsconsfile: "build_after.rb", rscons_args: %w[-j 4]) result = run_test(rsconsfile: "build_after.rb", rscons_args: %w[-j 4])
expect(result.stderr).to eq "" expect(result.stderr).to eq ""
end end
it "allows the user to specify side-effect files produced by another builder" do
test_dir("custom_builder")
result = run_test(rsconsfile: "produces.rb", rscons_args: %w[-j 4])
expect(result.stderr).to eq ""
expect(File.exists?("copy_inc.h")).to be_truthy
end
end end
context "CLI" do context "CLI" do
+33
View File
@@ -0,0 +1,33 @@
module Rscons
describe Ansi do
describe ".do_ansi?" do
context "on Windows" do
it "returns true when IO is a fifo on an xterm" do
stub_const("RUBY_PLATFORM", "mingw")
expect(ENV).to receive(:[]).with("TERM").and_return("xterm")
io = double
expect(io).to receive(:stat).and_return(Struct.new(:ftype).new("fifo"))
expect(Ansi.__send__(:do_ansi?, io)).to be_truthy
end
it "returns false when TERM is not set appropriately" do
stub_const("RUBY_PLATFORM", "mingw")
expect(ENV).to receive(:[]).with("TERM").and_return(nil)
io = double
expect(Ansi.__send__(:do_ansi?, io)).to be_falsey
end
end
context "on POSIX" do
it "returns true when IO is a TTY" do
stub_const("RUBY_PLATFORM", "linux")
io = double
expect(io).to receive(:tty?).and_return(true)
expect(Ansi.__send__(:do_ansi?, io)).to be_truthy
end
end
end
end
end
-32
View File
@@ -190,38 +190,6 @@ module Rscons
end end
end end
describe "#execute" do
context "with echo: :short" do
context "with no errors" do
it "prints the short description and executes the command" do
env = Environment.new(echo: :short)
expect(env).to receive(:puts).with("short desc")
expect(env).to receive(:system).with(*Rscons.command_executer, "a", "command").and_return(true)
env.execute("short desc", ["a", "command"])
end
end
context "with errors" do
it "prints the short description, executes the command, and prints the failed command line" do
env = Environment.new(echo: :short)
expect(env).to receive(:puts).with("short desc")
expect(env).to receive(:system).with(*Rscons.command_executer, "a", "command").and_return(false)
expect($stdout).to receive(:puts).with("Failed command was: a command")
env.execute("short desc", ["a", "command"])
end
end
end
context "with echo: :command" do
it "prints the command executed and executes the command" do
env = Environment.new(echo: :command)
expect(env).to receive(:puts).with("a command '--arg=val with spaces'")
expect(env).to receive(:system).with({modified: :environment}, *Rscons.command_executer, "a", "command", "--arg=val with spaces", {opt: :val}).and_return(false)
env.execute("short desc", ["a", "command", "--arg=val with spaces"], env: {modified: :environment}, options: {opt: :val})
end
end
end
describe "#method_missing" do describe "#method_missing" do
it "calls the original method missing when the target method is not a known builder" do it "calls the original method missing when the target method is not a known builder" do
env = Environment.new env = Environment.new
+9
View File
@@ -199,5 +199,14 @@ module Rscons
end end
end end
describe "#values_at" do
it "returns an array of the values associated with the given keys" do
v = VarSet.new({"fuz" => "a string", "foo" => 42, "bar" => :baz,
"qax" => [3, 6], "qux" => {a: :b}})
expect(v.values_at).to eq []
expect(v.values_at(*%w[fuz qux qax])).to eq ["a string", {a: :b}, [3, 6]]
end
end
end end
end end