Class: Rscons::Environment

Inherits:
BasicEnvironment show all
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

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

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.

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :echo (Symbol)

    :command, :short, or :off (default :short)

  • :exclude_builders (Boolean)

    Whether to omit adding default builders (default false)

  • :use (String, Array<String>)

    Use flag(s). If specified, any configuration flags which were saved with a corresponding `:use` value will be applied to this Environment.



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(options = {})
  unless Cache.instance["configuration_data"]["configured"]
    raise "Project must be configured before creating an Environment"
  end
  super(options)
  @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 options[: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 options[:echo]
      options[: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.

Parameters:

  • method (Symbol)

    Method name.

  • args (Array)

    Method arguments.

Returns:



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 = expand_path(expand_varref(target))
    sources = Array(sources).map do |source|
      source = source.target if source.is_a?(Builder)
      expand_path(expand_varref(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

.environmentsArray<Environment> (readonly)

Returns All Environments.

Returns:



15
16
17
# File 'lib/rscons/environment.rb', line 15

def environments
  @environments
end

Instance Attribute Details

#build_rootString (readonly)

Returns The build root.

Returns:

  • (String)

    The build root.



47
48
49
# File 'lib/rscons/environment.rb', line 47

def build_root
  @build_root
end

#buildersHash (readonly)

Returns Set of {“builder_name” => builder_object} pairs.

Returns:

  • (Hash)

    Set of {“builder_name” => builder_object} pairs.



41
42
43
# File 'lib/rscons/environment.rb', line 41

def builders
  @builders
end

#echoSymbol

Returns :command, :short, or :off.

Returns:

  • (Symbol)

    :command, :short, or :off



44
45
46
# File 'lib/rscons/environment.rb', line 44

def echo
  @echo
end

#n_threadsInteger

Returns The number of threads to use for this Environment. Defaults to the global Rscons.application.n_threads value.

Returns:

  • (Integer)

    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_initObject

Initialize class instance variables.



18
19
20
# File 'lib/rscons/environment.rb', line 18

def class_init
  @environments = []
end

.get_idInteger

Get an ID for a new Environment. This is a monotonically increasing integer.

Returns:

  • (Integer)

    Environment ID.



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.

Yields:

  • (build_op)

    Invoke the given block with the current build operation.

Yield Parameters:

  • build_op (Hash)

    Hash with keys:

    • :builder - The builder object in use.

    • :target - Target file name.

    • :sources - List of source file(s).

    • :vars - Set of construction variable values in use.

    • :env - The Environment invoking the builder.



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.

Overloads:

  • #add_builder(builder_class) ⇒ void

    Add the given builder to the Environment.

    Parameters:

    • builder_class (Class)

      A builder class to register.

  • #add_builder(name, &action) ⇒ void

    Create a new Builders::SimpleBuilder instance and add it to the environment.

    Parameters:

    • name (String, Symbol)

      The name of the builder to add.

    • action (Block)

      A block that will be called when the builder is executed to generate a target file. The provided block should have the same prototype as Builder#run.



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.

Yields:

  • (build_op)

    Invoke the given block with the current build operation.

Yield Parameters:

  • build_op (Hash)

    Hash with keys:

    • :builder - The builder object in use.

    • :target - Target file name.

    • :sources - List of source file(s).

    • :vars - Set of construction variable values in use.

    • :env - The Environment invoking the builder.



231
232
233
# File 'lib/rscons/environment.rb', line 231

def add_post_build_hook(&block)
  @build_hooks[:post] << block
end

#barrierObject

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 barrier
  @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")

Parameters:

  • targets (String, Array<String>)

    Target files to wait to build until the prerequisites are finished building.

  • prerequisites (String, Builder, Array<String, Builder>)

    Files that must be built before building the specified targets.



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 = expand_path(expand_varref(target))
    @registered_build_dependencies[target] ||= Set.new
    prerequisites.each do |prerequisite|
      if prerequisite.is_a?(Builder)
        prerequisite = prerequisite.target
      end
      prerequisite = expand_path(expand_varref(prerequisite))
      @registered_build_dependencies[target] << prerequisite
    end
  end
end

#build_steps_remainingInteger

Get the number of build steps remaining.

Returns:

  • (Integer)

    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.

Returns:



528
529
530
# File 'lib/rscons/environment.rb', line 528

def builder_for(target)
  @build_targets[target]
end

#clear_targetsvoid

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.

Returns:



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(options = {})
  clone = options[: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 options[:exclude_builders]
  env = self.class.new(
    echo: options[: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.

Parameters:

  • target (String, Builder)

    Target file.

  • user_deps (Array<String, Builder>)

    Dependency 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 = expand_varref(target.to_s)
  user_deps = user_deps.map do |ud|
    if ud.is_a?(Builder)
      ud = ud.target
    end
    expand_varref(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.

Parameters:

  • path (String, Array<String>)

    The path(s) to expand.

Returns:

  • (String, Array<String>)

    The expanded path(s).



480
481
482
483
484
485
486
487
488
489
490
# File 'lib/rscons/environment.rb', line 480

def expand_path(path)
  if Rscons.phony_target?(path)
    path
  elsif path.is_a?(Array)
    path.map do |path|
      expand_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.

Parameters:

  • source_fname (String)

    Source file name.

  • suffix (String)

    Suffix, including “.” if desired.

  • builder_class (Class)

    The builder in use.

Returns:

  • (String)

    The file name to be built from source_fname with suffix suffix.



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.

Parameters:

  • target (String)

    Target file name.

Returns:

  • (Array<String>, nil)

    List of user-specified dependencies for the target, or nil if none were specified.



439
440
441
# File 'lib/rscons/environment.rb', line 439

def get_user_deps(target)
  @user_deps[target]
end

This method returns an undefined value.

Print the builder run message, depending on the Environment's echo mode.

Parameters:

  • builder (Builder)

    The Builder that is executing.

  • short_description (String)

    Builder short description, printed if the echo mode is :short, or if there is no command.

  • command (Array<String>, nil)

    Builder command, printed if the echo mode is :command.



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 print_builder_run_message(builder, short_description, command)
  case @echo
  when :command
    if command.is_a?(Array)
      message = Util.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
  if message
    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} #{message}"), "\n")
  end
end

#processvoid

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.

Parameters:

  • target (String)

    Target of a build operation.

  • side_effects (Array<String>)

    File(s) produced when the target file 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 = expand_path(expand_varref(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.

Parameters:

  • target (String)

    The target that depends on these builds.

  • source (String)

    Source file to build.

  • suffix (String)

    Suffix to try to convert source files into.

  • vars (Hash)

    Extra variables to pass to the builders.

  • builder_class (Class)

    The builder class to use.

Returns:

  • (String)

    Output file name.



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.

Parameters:

  • side_effect (String)

    Side effect fiel name.



428
429
430
# File 'lib/rscons/environment.rb', line 428

def register_side_effect(side_effect)
  @side_effects << side_effect
end