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.
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 107 |
# File 'lib/rscons/environment.rb', line 68 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.
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 338 |
# File 'lib/rscons/environment.rb', line 311 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.
16 17 18 |
# File 'lib/rscons/environment.rb', line 16 def environments @environments end |
Instance Attribute Details
#build_root ⇒ String (readonly)
Returns The build root.
48 49 50 |
# File 'lib/rscons/environment.rb', line 48 def build_root @build_root end |
#builders ⇒ Hash (readonly)
Returns Set of {“builder_name” => builder_object} pairs.
42 43 44 |
# File 'lib/rscons/environment.rb', line 42 def builders @builders end |
#echo ⇒ Symbol
Returns :command, :short, or :off
45 46 47 |
# File 'lib/rscons/environment.rb', line 45 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.
53 54 55 |
# File 'lib/rscons/environment.rb', line 53 def n_threads @n_threads end |
Class Method Details
.class_init ⇒ Object
Initialize class instance variables.
19 20 21 |
# File 'lib/rscons/environment.rb', line 19 def class_init @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 |
# File 'lib/rscons/environment.rb', line 35 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.
210 211 212 |
# File 'lib/rscons/environment.rb', line 210 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.
181 182 183 184 185 186 187 188 189 |
# File 'lib/rscons/environment.rb', line 181 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.
232 233 234 |
# File 'lib/rscons/environment.rb', line 232 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.
539 540 541 |
# File 'lib/rscons/environment.rb', line 539 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")
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
# File 'lib/rscons/environment.rb', line 385 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.
547 548 549 550 551 |
# File 'lib/rscons/environment.rb', line 547 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.
529 530 531 |
# File 'lib/rscons/environment.rb', line 529 def builder_for(target) @build_targets[target] end |
#clear_targets ⇒ void
This method returns an undefined value.
Clear all targets registered for the Environment.
299 300 301 |
# File 'lib/rscons/environment.rb', line 299 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.
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 157 |
# File 'lib/rscons/environment.rb', line 128 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.
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
# File 'lib/rscons/environment.rb', line 346 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.
481 482 483 484 485 486 487 488 489 490 491 |
# File 'lib/rscons/environment.rb', line 481 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.
250 251 252 253 254 255 |
# File 'lib/rscons/environment.rb', line 250 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(Rscons.set_suffix(source_fname, suffix))}".gsub("\\", "/") end |
#get_user_deps(target) ⇒ Array<String>?
Return the list of user dependencies for a given target.
440 441 442 |
# File 'lib/rscons/environment.rb', line 440 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.
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 |
# File 'lib/rscons/environment.rb', line 504 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.
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 294 |
# File 'lib/rscons/environment.rb', line 263 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.
410 411 412 413 414 415 416 417 418 419 |
# File 'lib/rscons/environment.rb', line 410 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.
463 464 465 466 467 468 469 |
# File 'lib/rscons/environment.rb', line 463 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.
429 430 431 |
# File 'lib/rscons/environment.rb', line 429 def register_side_effect(side_effect) @side_effects << side_effect end |