Class: Rscons::Environment
- Inherits:
-
BasicEnvironment
- Object
- BasicEnvironment
- Rscons::Environment
- Defined in:
- lib/rscons/environment.rb
Overview
The Environment class is the main programmatic interface to Rscons. It contains a collection of construction variables, options, builders, and rules for building targets.
Class Attribute Summary collapse
-
.environments ⇒ Array<Environment>
readonly
All Environments.
Instance Attribute Summary collapse
-
#build_root ⇒ String
readonly
The build root.
-
#builders ⇒ Hash
readonly
Set of {“builder_name” => builder_object} pairs.
-
#echo ⇒ Symbol
:command, :short, or :off.
-
#n_threads ⇒ Integer
The number of threads to use for this Environment.
Class Method Summary collapse
-
.class_init ⇒ Object
Initialize class instance variables.
-
.get_id ⇒ Integer
Get an ID for a new Environment.
-
.register(env) ⇒ Object
Register an Environment.
Instance Method Summary collapse
-
#add_build_hook {|build_op| ... } ⇒ void
Add a build hook to the Environment.
-
#add_builder(builder_class, &action) ⇒ void
Add a Builder to the Environment.
-
#add_post_build_hook {|build_op| ... } ⇒ void
Add a post build hook to the Environment.
-
#barrier ⇒ Object
Mark a “barrier” point.
-
#build_after(targets, prerequisites) ⇒ void
Manually record the given target(s) as needing to be built after the given prerequisite(s).
-
#build_steps_remaining ⇒ Integer
Get the number of build steps remaining.
-
#builder_for(target) ⇒ Builder?
Get the Builder for a target.
-
#clear_targets ⇒ void
Clear all targets registered for the Environment.
-
#clone(options = {}) ⇒ Environment
Make a copy of the Environment object.
-
#depends(target, *user_deps) ⇒ void
Manually record a given target as depending on the specified files.
-
#expand_path(path) ⇒ String+
Expand a path to be relative to the Environment's build root.
-
#get_build_fname(source_fname, suffix, builder_class) ⇒ String
Return the file name to be built from
source_fname
with suffixsuffix
. -
#get_user_deps(target) ⇒ Array<String>?
Return the list of user dependencies for a given target.
-
#initialize(options = {}) ⇒ Environment
constructor
Create an Environment object.
-
#method_missing(method, *args) ⇒ Builder
Define a build target.
-
#print_builder_run_message(builder, short_description, command) ⇒ void
Print the builder run message, depending on the Environment's echo mode.
-
#process ⇒ void
Build all build targets specified in the Environment.
-
#produces(target, *side_effects) ⇒ void
Manually record the given side effect file(s) as being produced when the named target is produced.
-
#register_dependency_build(target, source, suffix, vars, builder_class) ⇒ String
Register a builder to build a source file into an output with the given suffix.
-
#register_side_effect(side_effect) ⇒ Object
private
Register a side effect file.
Methods inherited from BasicEnvironment
#[], #[]=, #append, #apply_configuration_data!, #dump, #expand_varref, #get_var, #load_configuration_data!, #merge_flags, #parse_flags, #parse_flags!, #shell
Constructor Details
#initialize(options = {}) ⇒ Environment
Create an Environment object.
If a block is given, the Environment object is yielded to the block and when the block returns, the #process method is automatically called.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/rscons/environment.rb', line 67 def initialize( = {}) unless Cache.instance["configuration_data"]["configured"] raise "Project must be configured before creating an Environment" end super() @id = self.class.get_id self.class.register(self) # Hash of Thread object => {Command} or {Builder}. @threads = {} @registered_build_dependencies = {} # Set of side-effect files that have not yet been built. @side_effects = Set.new @builder_sets = [] @build_targets = {} @user_deps = {} # Hash of builder name (String) => builder class (Class). @builders = {} @build_hooks = {pre: [], post: []} unless [:exclude_builders] DEFAULT_BUILDERS.each do |builder_class_name| builder_class = Builders.const_get(builder_class_name) builder_class or raise "Could not find builder class #{builder_class_name}" add_builder(builder_class) end end @echo = if [:echo] [:echo] elsif Rscons.application.verbose :command else :short end @build_root = "#{Cache.instance["configuration_data"]["build_dir"]}/e.#{@id}" @n_threads = Rscons.application.n_threads if block_given? yield self end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args) ⇒ Builder
Define a build target.
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 |
# File 'lib/rscons/environment.rb', line 310 def method_missing(method, *args) if @builders.has_key?(method.to_s) target, sources, vars, *rest = args vars ||= {} unless vars.is_a?(Hash) or vars.is_a?(VarSet) raise "Unexpected construction variable set: #{vars.inspect}" end target = ((target)) sources = Array(sources).map do |source| source = source.target if source.is_a?(Builder) ((source)) end.flatten builder = @builders[method.to_s].new( target: target, sources: sources, cache: Cache.instance, env: self, vars: vars) if @builder_sets.empty? @builder_sets << build_builder_set end @builder_sets.last << builder @build_targets[target] = builder builder else super end end |
Class Attribute Details
.environments ⇒ Array<Environment> (readonly)
Returns All Environments.
15 16 17 |
# File 'lib/rscons/environment.rb', line 15 def environments @environments end |
Instance Attribute Details
#build_root ⇒ String (readonly)
Returns The build root.
47 48 49 |
# File 'lib/rscons/environment.rb', line 47 def build_root @build_root end |
#builders ⇒ Hash (readonly)
Returns Set of {“builder_name” => builder_object} pairs.
41 42 43 |
# File 'lib/rscons/environment.rb', line 41 def builders @builders end |
#echo ⇒ Symbol
Returns :command, :short, or :off.
44 45 46 |
# File 'lib/rscons/environment.rb', line 44 def echo @echo end |
#n_threads ⇒ Integer
Returns The number of threads to use for this Environment. Defaults to the global Rscons.application.n_threads value.
52 53 54 |
# File 'lib/rscons/environment.rb', line 52 def n_threads @n_threads end |
Class Method Details
.class_init ⇒ Object
Initialize class instance variables.
18 19 20 |
# File 'lib/rscons/environment.rb', line 18 def class_init @environments = [] end |
.get_id ⇒ Integer
Get an ID for a new Environment. This is a monotonically increasing integer.
27 28 29 30 31 |
# File 'lib/rscons/environment.rb', line 27 def get_id @id ||= 0 @id += 1 @id end |
.register(env) ⇒ Object
Register an Environment.
34 35 36 37 |
# File 'lib/rscons/environment.rb', line 34 def register(env) @environments ||= [] @environments << env end |
Instance Method Details
#add_build_hook {|build_op| ... } ⇒ void
This method returns an undefined value.
Add a build hook to the Environment.
Build hooks are Ruby blocks which are invoked immediately before a build operation takes place. Build hooks have an opportunity to modify the construction variables in use for the build operation based on the builder in use, target file name, or sources. Build hooks can also register new build targets.
209 210 211 |
# File 'lib/rscons/environment.rb', line 209 def add_build_hook(&block) @build_hooks[:pre] << block end |
#add_builder(builder_class) ⇒ void #add_builder(name, &action) ⇒ void
This method returns an undefined value.
Add a Builder to the Environment.
180 181 182 183 184 185 186 187 188 |
# File 'lib/rscons/environment.rb', line 180 def add_builder(builder_class, &action) if builder_class.is_a?(String) or builder_class.is_a?(Symbol) name = builder_class.to_s builder_class = BuilderBuilder.new(Rscons::Builders::SimpleBuilder, name, &action) else name = builder_class.name end @builders[name] = builder_class end |
#add_post_build_hook {|build_op| ... } ⇒ void
This method returns an undefined value.
Add a post build hook to the Environment.
Post-build hooks are Ruby blocks which are invoked immediately after a build operation takes place. Post-build hooks are only invoked if the build operation succeeded. Post-build hooks can register new build targets.
231 232 233 |
# File 'lib/rscons/environment.rb', line 231 def add_post_build_hook(&block) @build_hooks[:post] << block end |
#barrier ⇒ Object
Mark a “barrier” point.
Rscons will wait for all build targets registered before the barrier to be built before beginning to build any build targets registered after the barrier. In other words, Rscons will not parallelize build operations across a barrier.
538 539 540 |
# File 'lib/rscons/environment.rb', line 538 def @builder_sets << build_builder_set end |
#build_after(targets, prerequisites) ⇒ void
This method returns an undefined value.
Manually record the given target(s) as needing to be built after the given prerequisite(s).
For example, consider a builder registered to generate gen.c which also generates gen.h as a side-effect. If program.c includes gen.h, then it should not be compiled before gen.h has been generated. When using multiple threads to build, Rscons may attempt to compile program.c before gen.h has been generated because it does not know that gen.h will be generated along with gen.c. One way to prevent that situation would be to first process the Environment with just the code-generation builders in place and then register the compilation builders. Another way is to use this method to record that a certain target should not be built until another has completed. For example, for the situation previously described:
env.build_after("program.o", "gen.c")
384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 |
# File 'lib/rscons/environment.rb', line 384 def build_after(targets, prerequisites) targets = Array(targets) prerequisites = Array(prerequisites) targets.each do |target| target = ((target)) @registered_build_dependencies[target] ||= Set.new prerequisites.each do |prerequisite| if prerequisite.is_a?(Builder) prerequisite = prerequisite.target end prerequisite = ((prerequisite)) @registered_build_dependencies[target] << prerequisite end end end |
#build_steps_remaining ⇒ Integer
Get the number of build steps remaining.
546 547 548 549 550 |
# File 'lib/rscons/environment.rb', line 546 def build_steps_remaining @builder_sets.reduce(0) do |result, builder_set| result + builder_set.build_steps_remaining end end |
#builder_for(target) ⇒ Builder?
Get the Builder for a target.
528 529 530 |
# File 'lib/rscons/environment.rb', line 528 def builder_for(target) @build_targets[target] end |
#clear_targets ⇒ void
This method returns an undefined value.
Clear all targets registered for the Environment.
298 299 300 |
# File 'lib/rscons/environment.rb', line 298 def clear_targets @builder_sets.clear end |
#clone(options = {}) ⇒ Environment
Make a copy of the Environment object.
By default, a cloned environment will contain a copy of all environment options, construction variables, and builders, but not a copy of the targets, build hooks, build directories, or the build root.
Exactly which items are cloned are controllable via the optional :clone parameter, which can be :none, :all, or a set or array of any of the following:
-
:variables to clone construction variables (on by default)
-
:builders to clone the builders (on 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 when the block returns, the #process method is automatically called.
Any options that #initialize receives can also be specified here.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 |
# File 'lib/rscons/environment.rb', line 127 def clone( = {}) clone = [:clone] || :all clone = Set[:variables, :builders, :build_hooks] if clone == :all clone = Set[] if clone == :none clone = Set.new(clone) if clone.is_a?(Array) clone.delete(:builders) if [:exclude_builders] env = self.class.new( echo: [:echo] || @echo, exclude_builders: true) if clone.include?(:builders) @builders.each do |builder_name, builder| env.add_builder(builder) end end env.append(@varset) if clone.include?(:variables) if clone.include?(:build_hooks) @build_hooks[:pre].each do |build_hook_block| env.add_build_hook(&build_hook_block) end @build_hooks[:post].each do |build_hook_block| env.add_post_build_hook(&build_hook_block) end end env.instance_variable_set(:@n_threads, @n_threads) if block_given? yield env end env end |
#depends(target, *user_deps) ⇒ void
This method returns an undefined value.
Manually record a given target as depending on the specified files.
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
# File 'lib/rscons/environment.rb', line 345 def depends(target, *user_deps) if target.is_a?(Builder) target = target.target end target = (target.to_s) user_deps = user_deps.map do |ud| if ud.is_a?(Builder) ud = ud.target end (ud) end @user_deps[target] ||= [] @user_deps[target] = (@user_deps[target] + user_deps).uniq build_after(target, user_deps) end |
#expand_path(path) ⇒ String+
Expand a path to be relative to the Environment's build root.
Paths beginning with “^/” are expanded by replacing “^” with the Environment's build root.
480 481 482 483 484 485 486 487 488 489 490 |
# File 'lib/rscons/environment.rb', line 480 def (path) if Rscons.phony_target?(path) path elsif path.is_a?(Array) path.map do |path| (path) end else path.sub(%r{^\^(?=[\\/])}, @build_root).gsub("\\", "/") end end |
#get_build_fname(source_fname, suffix, builder_class) ⇒ String
Return the file name to be built from source_fname
with suffix suffix
.
This method takes into account the Environment's build directories.
249 250 251 252 253 254 |
# File 'lib/rscons/environment.rb', line 249 def get_build_fname(source_fname, suffix, builder_class) if extra_path = builder_class.extra_path extra_path = "/#{extra_path}" end "#{@build_root}#{extra_path}/#{Util.make_relative_path("#{source_fname}#{suffix}")}".gsub("\\", "/") end |
#get_user_deps(target) ⇒ Array<String>?
Return the list of user dependencies for a given target.
439 440 441 |
# File 'lib/rscons/environment.rb', line 439 def get_user_deps(target) @user_deps[target] end |
#print_builder_run_message(builder, short_description, command) ⇒ void
This method returns an undefined value.
Print the builder run message, depending on the Environment's echo mode.
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 |
# File 'lib/rscons/environment.rb', line 503 def (builder, short_description, command) case @echo when :command if command.is_a?(Array) = Util.command_to_s(command) elsif command.is_a?(String) = command elsif short_description.is_a?(String) = short_description end when :short = short_description if short_description end if 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} #{}"), "\n") end end |
#process ⇒ void
This method returns an undefined value.
Build all build targets specified in the Environment.
When a block is passed to Environment.new, this method is automatically called after the block returns.
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
# File 'lib/rscons/environment.rb', line 262 def process Cache.instance.clear_checksum_cache! @process_failures = [] @process_blocking_wait = false @process_commands_waiting_to_run = [] @process_builder_waits = {} @process_builders_to_run = [] begin while @builder_sets.size > 0 or @threads.size > 0 or @process_commands_waiting_to_run.size > 0 process_step if @builder_sets.size > 0 and @builder_sets.first.empty? and @threads.empty? and @process_commands_waiting_to_run.empty? and @process_builders_to_run.empty? # Remove empty BuilderSet when all other operations have completed. @builder_sets.slice!(0) end unless @process_failures.empty? # On a build failure, do not start any more builders or commands, # but let the threads that have already been started complete. @builder_sets.clear @process_commands_waiting_to_run.clear end end ensure Cache.instance.write end unless @process_failures.empty? msg = @process_failures.join("\n") if Cache.instance["failed_commands"].size > 0 msg += "\nUse -F to view the failed command log from the previous build operation" end raise BuildError.new(msg) end end |
#produces(target, *side_effects) ⇒ void
This method returns an undefined value.
Manually record the given side effect file(s) as being produced when the named target is produced.
409 410 411 412 413 414 415 416 417 418 |
# File 'lib/rscons/environment.rb', line 409 def produces(target, *side_effects) target = ((target)) @builder_sets.reverse.each do |builder_set| if builders = builder_set[target] builders.last.produces(*side_effects) return end end raise "Could not find a registered build target #{target.inspect}" end |
#register_dependency_build(target, source, suffix, vars, builder_class) ⇒ String
Register a builder to build a source file into an output with the given suffix.
This method is used internally by Rscons builders. It can be called from the builder's #initialize method.
462 463 464 465 466 467 468 |
# File 'lib/rscons/environment.rb', line 462 def register_dependency_build(target, source, suffix, vars, builder_class) output_fname = get_build_fname(source, suffix, builder_class) self.__send__(builder_class.name, output_fname, source, vars) @registered_build_dependencies[target] ||= Set.new @registered_build_dependencies[target] << output_fname output_fname end |
#register_side_effect(side_effect) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Register a side effect file.
This is an internally used method.
428 429 430 |
# File 'lib/rscons/environment.rb', line 428 def register_side_effect(side_effect) @side_effects << side_effect end |