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
-
.running_environment ⇒ Environment
The Environment that is currently executing a construction block.
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.
-
#name ⇒ String
readonly
Environment name.
Class Method Summary collapse
-
.[](name = nil) ⇒ Object
Get an Environment by name.
-
.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).
-
#builder_for(target) ⇒ Builder?
Get the Builder for a target.
-
#clear_targets ⇒ void
Clear all targets registered for the Environment.
-
#clone(*args, &block) ⇒ 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(expr) ⇒ String+
Expand construction variable references and paths.
-
#expand_path(path) ⇒ String+
Expand paths.
-
#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(*args, &block) ⇒ 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!, #load_task_param_variables!, #merge_flags, #parse_flags, #parse_flags!, #shell
Constructor Details
#initialize(*args, &block) ⇒ Environment
Create an Environment object.
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 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/rscons/environment.rb', line 75 def initialize(*args, &block) @id = self.class.get_id if args.first.is_a?(String) base_name = args.slice!(0) else base_name = "e.#{@id}" end variant_keys = (Rscons.application.active_variants || []).map do |variant| variant[:key] end.compact @name = [base_name, *variant_keys].join("-") = args.first || {} unless Cache.instance["configuration_data"]["configured"] raise "Project must be configured before creating an Environment" end super() # 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 = "#{Rscons.application.build_dir}/#{@name}" @n_threads = Rscons.application.n_threads self.class.register(self) if block Environment.running_environment = self block[self] Environment.running_environment = nil end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method, *args) ⇒ Builder
Define a build target.
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/rscons/environment.rb', line 340 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
.running_environment ⇒ Environment
Returns The Environment that is currently executing a construction block.
15 16 17 |
# File 'lib/rscons/environment.rb', line 15 def running_environment @running_environment end |
Instance Attribute Details
#build_root ⇒ String (readonly)
Returns The build root.
63 64 65 |
# File 'lib/rscons/environment.rb', line 63 def build_root @build_root end |
#builders ⇒ Hash (readonly)
Returns Set of {“builder_name” => builder_object} pairs.
57 58 59 |
# File 'lib/rscons/environment.rb', line 57 def builders @builders end |
#echo ⇒ Symbol
Returns :command, :short, or :off.
60 61 62 |
# File 'lib/rscons/environment.rb', line 60 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.
68 69 70 |
# File 'lib/rscons/environment.rb', line 68 def n_threads @n_threads end |
#name ⇒ String (readonly)
Returns Environment name.
72 73 74 |
# File 'lib/rscons/environment.rb', line 72 def name @name end |
Class Method Details
.[](name = nil) ⇒ Object
Get an Environment by name.
46 47 48 49 50 51 52 |
# File 'lib/rscons/environment.rb', line 46 def [](name = nil) if name @environments_by_name[name] else @environments end end |
.class_init ⇒ Object
Initialize class instance variables.
18 19 20 21 |
# File 'lib/rscons/environment.rb', line 18 def class_init @environments_by_name = {} @environments = [] end |
.get_id ⇒ Integer
Get an ID for a new Environment. This is a monotonically increasing integer.
28 29 30 31 32 |
# File 'lib/rscons/environment.rb', line 28 def get_id @id ||= 0 @id += 1 @id end |
.register(env) ⇒ Object
Register an Environment.
35 36 37 38 39 40 |
# File 'lib/rscons/environment.rb', line 35 def register(env) @environments << env if env.name @environments_by_name[env.name] = env end 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.
235 236 237 |
# File 'lib/rscons/environment.rb', line 235 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.
206 207 208 209 210 211 212 213 214 |
# File 'lib/rscons/environment.rb', line 206 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.
257 258 259 |
# File 'lib/rscons/environment.rb', line 257 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.
582 583 584 |
# File 'lib/rscons/environment.rb', line 582 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")
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 |
# File 'lib/rscons/environment.rb', line 414 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 |
#builder_for(target) ⇒ Builder?
Get the Builder for a target.
572 573 574 |
# File 'lib/rscons/environment.rb', line 572 def builder_for(target) @build_targets[target] end |
#clear_targets ⇒ void
This method returns an undefined value.
Clear all targets registered for the Environment.
328 329 330 |
# File 'lib/rscons/environment.rb', line 328 def clear_targets @builder_sets.clear end |
#clone(*args, &block) ⇒ 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.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/rscons/environment.rb', line 146 def clone(*args, &block) if args.first.is_a?(String) name = args.slice!(0) end = args.first || {} = .dup 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] [:echo] ||= @echo new_args = name ? [name] : [] new_args << .merge(exclude_builders: true) env = self.class.new(*new_args) 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 Environment.running_environment = self block[env] Environment.running_environment = nil 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.
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
# File 'lib/rscons/environment.rb', line 375 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(expr) ⇒ String+
Expand construction variable references and paths.
532 533 534 |
# File 'lib/rscons/environment.rb', line 532 def (expr) ((expr)) end |
#expand_path(path) ⇒ String+
Expand paths.
Paths beginning with “^/” are expanded by replacing “^” with the Environment’s build root (e.g. “build/envname”). Paths beginning with “^^/” are expanded by replacing “^^” with the top-level build directory (e.g. “build”).
512 513 514 515 516 517 518 519 520 521 522 |
# File 'lib/rscons/environment.rb', line 512 def (path) if Rscons.phony_target?(path) path elsif path.is_a?(Array) path.map do |path| (path) end else path.sub(%r{^\^\^(?=[\\/])}, Rscons.application.build_dir).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.
275 276 277 278 279 280 |
# File 'lib/rscons/environment.rb', line 275 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.
469 470 471 |
# File 'lib/rscons/environment.rb', line 469 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.
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 |
# File 'lib/rscons/environment.rb', line 547 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 = @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.
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/rscons/environment.rb', line 288 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 = [] @build_step = 0 @build_steps = @builder_sets.reduce(0) do |result, builder_set| result + builder_set.build_steps_remaining end 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 `#{Util.command_to_execute_me} -F` to view the failed command log from the previous build operation" end raise RsconsError.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.
439 440 441 442 443 444 445 446 447 448 |
# File 'lib/rscons/environment.rb', line 439 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.
492 493 494 495 496 497 498 |
# File 'lib/rscons/environment.rb', line 492 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.
458 459 460 |
# File 'lib/rscons/environment.rb', line 458 def register_side_effect(side_effect) @side_effects << side_effect end |