Class: Configurator

Inherits:
Object show all
Defined in:
lib/ceedling/config/configurator.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#exclude_test_caseObject

Returns the value of attribute exclude_test_case.



19
20
21
# File 'lib/ceedling/config/configurator.rb', line 19

def exclude_test_case
  @exclude_test_case
end

#include_test_caseObject

Returns the value of attribute include_test_case.



19
20
21
# File 'lib/ceedling/config/configurator.rb', line 19

def include_test_case
  @include_test_case
end

#programmatic_pluginsObject (readonly)

Returns the value of attribute programmatic_plugins.



18
19
20
# File 'lib/ceedling/config/configurator.rb', line 18

def programmatic_plugins
  @programmatic_plugins
end

#project_config_hashObject (readonly)

Returns the value of attribute project_config_hash.



18
19
20
# File 'lib/ceedling/config/configurator.rb', line 18

def project_config_hash
  @project_config_hash
end

#project_loggingObject

Returns the value of attribute project_logging.



19
20
21
# File 'lib/ceedling/config/configurator.rb', line 19

def project_logging
  @project_logging
end

#rake_pluginsObject (readonly)

Returns the value of attribute rake_plugins.



18
19
20
# File 'lib/ceedling/config/configurator.rb', line 18

def rake_plugins
  @rake_plugins
end

#sanity_checksObject

Returns the value of attribute sanity_checks.



19
20
21
# File 'lib/ceedling/config/configurator.rb', line 19

def sanity_checks
  @sanity_checks
end

Instance Method Details

#build(ceedling_lib_path, logging_path, config, *keys) ⇒ Object

Create constants and accessors (attached to this object) from given hash



764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
# File 'lib/ceedling/config/configurator.rb', line 764

def build(ceedling_lib_path, logging_path, config, *keys)
  flattened_config = @configurator_builder.flattenify( config )

  @configurator_setup.build_project_config( ceedling_lib_path, logging_path, flattened_config )

  @configurator_setup.build_directory_structure( flattened_config )

  # Copy Unity, CMock, CException into vendor directory within build directory
  @configurator_setup.vendor_frameworks_and_support_files( ceedling_lib_path, flattened_config )

  @configurator_setup.build_project_collections( flattened_config )

  @project_config_hash = flattened_config.clone

  @configurator_setup.build_constants_and_accessors( flattened_config, binding() )

  # Top-level keys disappear when we flatten, so create global constants & accessors to any specified keys
  keys.each do |key|
    hash = { key => config[key] }
    @configurator_setup.build_constants_and_accessors( hash, binding() )
  end
end

#build_supplement(config_base, config_more) ⇒ Object

Add to constants and accessors as post build step



807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
# File 'lib/ceedling/config/configurator.rb', line 807

def build_supplement(config_base, config_more)
  # merge in our post-build additions to base configuration hash
  config_base.deep_merge!( config_more )

  # flatten our addition hash
  config_more_flattened = @configurator_builder.flattenify( config_more )

  # merge our flattened hash with built hash from previous build
  @project_config_hash.deep_merge!( config_more_flattened )

  # create more constants and accessors
  @configurator_setup.build_constants_and_accessors(config_more_flattened, binding())

  # recreate constants & update accessors with new merged, base values
  config_more.keys.each do |key|
    hash = { key => config_base[key] }
    @configurator_setup.build_constants_and_accessors(hash, binding())
  end
end

#discover_plugins(paths_hash, config) ⇒ Object



503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# File 'lib/ceedling/config/configurator.rb', line 503

def discover_plugins(paths_hash, config)
  @loginator.lazy( Verbosity::OBNOXIOUS ) do
    @reportinator.generate_progress( 'Discovering all plugins' )
  end

  # Rake-based plugins
  @rake_plugins = @configurator_plugins.find_rake_plugins( config, paths_hash )
  if !@configurator_plugins.rake_plugins.empty?
    @loginator.lazy( Verbosity::DEBUG ) do
      " > Rake plugins: " + @configurator_plugins.rake_plugins.map{|p| p[:plugin]}.join( ', ' )
    end
  end

  # Ruby `Plugin` subclass programmatic plugins
  @programmatic_plugins = @configurator_plugins.find_programmatic_plugins( config, paths_hash )
  if !@configurator_plugins.programmatic_plugins.empty?
    @loginator.lazy( Verbosity::DEBUG ) do
      " > Programmatic plugins: " + @configurator_plugins.programmatic_plugins.map{|p| p[:plugin]}.join( ', ' )
    end
  end
  
  # Config plugins
  @configurator_plugins.find_config_plugins( config, paths_hash )
  if !@configurator_plugins.config_plugins.empty?
    @loginator.lazy( Verbosity::DEBUG ) do
      " > Config plugins: " + @configurator_plugins.config_plugins.map{|p| p[:plugin]}.join( ', ' )
    end
  end
end

#eval_defines(config) ⇒ Object

Handle any Ruby string replacement for :defines string arrays



663
664
665
666
667
668
669
670
# File 'lib/ceedling/config/configurator.rb', line 663

def eval_defines(config)
  @loginator.lazy( Verbosity::OBNOXIOUS ) do
    @reportinator.generate_progress( 'Expanding any string replacements in :defines entries' )
  end

  # Descend down to array of #define strings regardless of depth in config block
  traverse_hash_eval_string_arrays( config[:defines], source: ":defines" )
end

#eval_environment_variables(config) ⇒ Object

Process environment variables set in configuration file (Each entry within the :environment array is a hash)



579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
# File 'lib/ceedling/config/configurator.rb', line 579

def eval_environment_variables(config)
  return if config[:environment].nil?

  @loginator.lazy( Verbosity::OBNOXIOUS ) do
    @reportinator.generate_progress( 'Processing environment variables' )
  end

  config[:environment].each do |hash|
    key   = hash.keys[0] # Get first (should be only) environment variable entry
    value = hash[key]    # Get associated value
    items = []

    # Special case handling for :path environment variable entry
    # File::PATH_SEPARATOR => ':' (Unix-ish) or ';' (Windows)
    interstitial = ((key == :path) ? File::PATH_SEPARATOR : ' ')

    # Create an array container for the value of this entry
    #  - If the value is an array, get it
    #  - Otherwise, place value in a single item array
    items = ((value.class == Array) ? hash[key] : [value])

    # Process value array
    items.each do |item|
      # Process each item for Ruby string replacement
      if item.is_a? String
        item.replace( @ruby_expandinator.expand( item, source: ":environment ↳ #{key}" ) )
      end
    end

    # Join any value items (become a flattened string)
    #  - With path separator if the key was :path
    #  - With space otherwise
    hash[key] = items.join( interstitial )

    # Set the environment variable for our session
    @system_wrapper.env_set( key.to_s.upcase, hash[key] )
    @loginator.lazy( Verbosity::DEBUG ) do 
      " - #{key.to_s.upcase}: \"#{hash[key]}\""
    end
  end
end

#eval_flags(config) ⇒ Object

Handle any Ruby string replacement for :flags string arrays



652
653
654
655
656
657
658
659
# File 'lib/ceedling/config/configurator.rb', line 652

def eval_flags(config)
  @loginator.lazy( Verbosity::OBNOXIOUS ) do
    @reportinator.generate_progress( 'Expanding any string replacements in :flags entries' )
  end

  # Descend down to array of command line flags strings regardless of depth in config block
  traverse_hash_eval_string_arrays( config[:flags], source: ":flags" )
end

#eval_paths(config) ⇒ Object

Eval config path lists (convert any strings to array of size 1) and handle any Ruby string replacement



623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
# File 'lib/ceedling/config/configurator.rb', line 623

def eval_paths(config)
  # :plugins ↳ :load_paths already handled

  @loginator.lazy( Verbosity::OBNOXIOUS ) do
    @reportinator.generate_progress( 'Processing path entries and expanding any string replacements' )
  end

  eval_path_entries( config[:project][:build_root], source: ":project ↳ :build_root" )
  eval_path_entries( config[:release_build][:artifacts], source: ":release_build ↳ :artifacts" )

  config[:paths].each_pair do |entry, paths|
    # :paths sub-entries (e.g. :test) could be a single string -> make array
    reform_path_entries_as_lists( config[:paths], entry, paths )
    eval_path_entries( paths, source: ":paths ↳ #{entry}" )
  end

  config[:files].each_pair do |entry, files|
    # :files sub-entries (e.g. :test) could be a single string -> make array
    reform_path_entries_as_lists( config[:files], entry, files )
    eval_path_entries( files, source: ":files ↳ #{entry}" )
  end

  # All other paths at secondary hash key level processed by convention (`_path`):
  # ex. :toplevel ↳ :foo_path & :toplevel ↳ :bar_paths are evaluated
  config.each_pair { |key, child| eval_path_entries( collect_path_list( child ), source: ":#{key} ↳ a *_path/*_paths entry" ) }
end

#get_cmock_configObject



409
410
411
412
413
# File 'lib/ceedling/config/configurator.rb', line 409

def get_cmock_config
  # Clone because test mock generation is not thread-safe;
  # The mock generator is manufactured for each use with configuration changes for each use.
  return @cmock_config.clone
end

#get_runner_configObject



402
403
404
405
406
# File 'lib/ceedling/config/configurator.rb', line 402

def get_runner_config
  # Clone because test runner generation is not thread-safe;
  # The runner generator is manufactured for each use with configuration changes for each use.
  return @runner_config.clone
end

#insert_rake_plugins(plugins) ⇒ Object



828
829
830
831
832
833
834
835
836
# File 'lib/ceedling/config/configurator.rb', line 828

def insert_rake_plugins(plugins)
  plugins.each do |hash|
    @loginator.lazy( Verbosity::OBNOXIOUS ) do
      @reportinator.generate_progress( "Adding plugin #{hash[:plugin]} to Rake load list" )
    end

    @project_config_hash[:project_rakefile_component_files] << hash[:path]
  end
end

#inspectObject

Override to prevent exception handling from walking & stringifying the object variables. Object variables are gigantic and produce a flood of output.



43
44
45
46
# File 'lib/ceedling/config/configurator.rb', line 43

def inspect
  # TODO: When identifying information is added to constructor, insert it into `inspect()` string
  return this.class.name
end

#merge_ceedling_runtime_config(config, runtime_config) ⇒ Object



282
283
284
285
# File 'lib/ceedling/config/configurator.rb', line 282

def merge_ceedling_runtime_config(config, runtime_config)
  # Merge Ceedling's internal runtime configuration settings
  config.deep_merge( runtime_config )
end

#merge_config_plugins(config) ⇒ Object



543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
# File 'lib/ceedling/config/configurator.rb', line 543

def merge_config_plugins(config)
  return if @configurator_plugins.config_plugins.empty?

  # Merge plugin configuration values (like Ceedling project file)
  @configurator_plugins.config_plugins.each do |hash|
    _config = begin
      @yaml_wrapper.load( hash[:path] )
    rescue YamlLoadException => e
      raise YamlLoadException.new(
        reason: e.reason, source: e.source, original_error: e.original_error,
        message: "Could not load configuration from plugin '#{hash[:plugin]}' ⏩️ #{e.message}"
      )
    end

    @loginator.lazy( Verbosity::OBNOXIOUS ) do
      @reportinator.generate_progress( "Merging configuration from plugin #{hash[:plugin]}" )
    end
    @loginator.lazy( Verbosity::DEBUG ) do 
      _config.to_s
    end

    # Special handling for plugin paths
    if (_config.include?( :paths ))
      _config[:paths].update( _config[:paths] ) do |k,v| 
        plugin_path = hash[:path].match( /(.*)[\/]config[\/]\w+\.yml/ )[1]
        v.map {|vv| File.expand_path( vv.gsub!( /\$PLUGIN_PATH/, plugin_path) ) }
      end
    end

    config.deep_merge( _config )
  end
end

#merge_plugins_defaults(paths_hash, config, default_config) ⇒ Object



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
# File 'lib/ceedling/config/configurator.rb', line 233

def merge_plugins_defaults(paths_hash, config, default_config)
  # Config YAML defaults plugins
  plugin_yml_defaults = @configurator_plugins.find_plugin_yml_defaults( config, paths_hash )
  
  # Config Ruby-based hash defaults plugins
  plugin_hash_defaults = @configurator_plugins.find_plugin_hash_defaults( config, paths_hash )


  if !plugin_hash_defaults.empty?
    @loginator.lazy( Verbosity::OBNOXIOUS ) do 
      @reportinator.generate_progress( 'Collecting Plugin YAML defaults' )
    end
  end

  # Load base configuration values (defaults) from YAML
  plugin_yml_defaults.each do |plugin, defaults|
    _defaults = begin
      @yaml_wrapper.load( defaults )
    rescue YamlLoadException => e
      raise YamlLoadException.new(
        reason: e.reason, source: e.source, original_error: e.original_error,
        message: "Could not load default configuration for plugin '#{plugin}' ⏩️ #{e.message}"
      )
    end

    @loginator.lazy( Verbosity::DEBUG ) do
      " - #{plugin} >> " + _defaults.to_s()
    end

    default_config.deep_merge( _defaults )
  end

  if !plugin_hash_defaults.empty?
    @loginator.lazy( Verbosity::OBNOXIOUS ) do 
      @reportinator.generate_progress( 'Collecting Plugin Ruby hash defaults' )
    end
  end

  # Load base configuration values (defaults) as hash from Ruby
  plugin_hash_defaults.each do |plugin, defaults|
    @loginator.lazy( Verbosity::DEBUG ) do 
      " - #{plugin} >> " + defaults.to_s()
    end

    default_config.deep_merge( defaults )
  end
end

#merge_tools_defaults(config, default_config) ⇒ Object

The default tools (eg. DEFAULT_TOOLS_TEST) are merged into default config hash



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
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/ceedling/config/configurator.rb', line 153

def merge_tools_defaults(config, default_config)
  @loginator.lazy( Verbosity::OBNOXIOUS ) do 
    @reportinator.generate_progress( 'Collecting default tool configurations' )
  end

  # config[:project] is guaranteed to exist / validated to exist but may not include elements referenced below
  # config[:test_build] and config[:release_build] are optional in a user project configuration


  release_build, _      = @config_walkinator.fetch_value( :project, :release_build, 
                            hash:config,
                            default: DEFAULT_CEEDLING_PROJECT_CONFIG[:project][:release_build]
                          )

  test_preprocessing, _ = @config_walkinator.fetch_value( :project, :use_test_preprocessor,
                            hash:config,
                            default: DEFAULT_CEEDLING_PROJECT_CONFIG[:project][:use_test_preprocessor]
                          )

  backtrace, _          = @config_walkinator.fetch_value( :project, :use_backtrace,
                            hash:config,
                            default: DEFAULT_CEEDLING_PROJECT_CONFIG[:project][:use_backtrace]
                          )

  release_assembly, _   = @config_walkinator.fetch_value( :release_build, :use_assembly,
                            hash:config,
                            default: DEFAULT_CEEDLING_PROJECT_CONFIG[:release_build][:use_assembly]
                          )

  test_assembly, _      = @config_walkinator.fetch_value( :test_build, :use_assembly,
                            hash:config,
                            default: DEFAULT_CEEDLING_PROJECT_CONFIG[:test_build][:use_assembly]
                          )

  default_config.deep_merge( DEFAULT_TOOLS_TEST.deep_clone() )

  default_config.deep_merge( DEFAULT_TOOLS_TEST_PREPROCESSORS.deep_clone() ) if (test_preprocessing != :none)
  default_config.deep_merge( DEFAULT_TOOLS_TEST_ASSEMBLER.deep_clone() )     if test_assembly
  default_config.deep_merge( DEFAULT_TOOLS_TEST_GDB_BACKTRACE.deep_clone() ) if (backtrace == :gdb)

  default_config.deep_merge( DEFAULT_TOOLS_RELEASE.deep_clone() )            if release_build
  default_config.deep_merge( DEFAULT_TOOLS_RELEASE_ASSEMBLER.deep_clone() )  if (release_build and release_assembly)
end

#populate_cmock_config(config) ⇒ Object



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
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/ceedling/config/configurator.rb', line 313

def populate_cmock_config(config)
  # Save CMock config reference
  @cmock_config = config[:cmock]

  cmock = config[:cmock]

  # Do no more prep if we're not using mocks
  if !config[:project][:use_mocks]
    @loginator.lazy( Verbosity::DEBUG ) do 
      "CMock configuration >> #{cmock}"
    end
    return
  end

  @loginator.lazy( Verbosity::OBNOXIOUS ) do
    @reportinator.generate_progress( 'Processing CMock configuration' )
  end

  # Plugins housekeeping
  cmock[:plugins].map! { |plugin| plugin.to_sym() }
  cmock[:plugins].uniq!

  # Reformulate CMock helper path value as array of one element if it's a string in config
  cmock[:unity_helper_path] = [cmock[:unity_helper_path]] if cmock[:unity_helper_path].is_a?( String )

  # CMock Unity helper handling
  cmock[:unity_helper_path].each do |path|
    cmock[:includes] << File.basename( path )
  end

  cmock[:includes].uniq!

  # Add mocking prefix symbol for all test compilation
  cmock[:defines] << "CMOCK_MOCK_PREFIX=#{cmock[:mock_prefix]}"

  @loginator.lazy( Verbosity::DEBUG ) do
    "CMock configuration >> #{cmock}"
  end
end

#populate_cmock_defaults(config, default_config) ⇒ Object



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/ceedling/config/configurator.rb', line 198

def populate_cmock_defaults(config, default_config)
  # Cmock has its own internal defaults handling, but we need to set these specific values
  # so they're guaranteed values and present for the Ceedling environment to access

  @loginator.lazy( Verbosity::OBNOXIOUS ) do 
    @reportinator.generate_progress( 'Collecting CMock defaults' )
  end

  # Begin populating defaults with CMock defaults as set by Ceedling
  default_cmock = default_config[:cmock]

  # Fill in default settings programmatically
  default_cmock[:mock_path] = File.join(config[:project][:build_root], TESTS_BASE_PATH, 'mocks')
  default_cmock[:verbosity] = project_verbosity()
end

#populate_exceptions_config(config) ⇒ Object



386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/ceedling/config/configurator.rb', line 386

def populate_exceptions_config(config)
  # Automagically set exception handling if CMock is configured for it
  if config[:cmock][:plugins] && config[:cmock][:plugins].include?(:cexception)
    @loginator.lazy( Verbosity::OBNOXIOUS ) do
      @reportinator.generate_progress( 'Enabling CException use based on CMock plugins settings' )
    end   

    config[:project][:use_exceptions] = true
  end

  @loginator.lazy( Verbosity::DEBUG ) do
    "CException configuration >> #{config[:cexception]}"
  end
end

#populate_plugins_config(paths_hash, config) ⇒ Object



534
535
536
537
538
539
540
# File 'lib/ceedling/config/configurator.rb', line 534

def populate_plugins_config(paths_hash, config)
  # Set special plugin setting for results printing if unset
  config[:plugins][:display_raw_test_results] = true if (config[:plugins][:display_raw_test_results].nil?)

  # Add corresponding path to each plugin's configuration
  paths_hash.each_pair { |name, path| config[:plugins][name] = path }
end

#populate_test_runner_generation_config(config) ⇒ Object



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
# File 'lib/ceedling/config/configurator.rb', line 354

def populate_test_runner_generation_config(config)
  @loginator.lazy( Verbosity::OBNOXIOUS ) do
    @reportinator.generate_progress( 'Populating test runner generation settings' )
  end    

  use_backtrace = config[:project][:use_backtrace]

  # Force command line argument option for any backtrace option
  if use_backtrace != :none
    if config[:test_runner][:cmdline_args] == false
      config[:test_runner][:cmdline_args] = true
      @loginator.log( "Enabled :test_runner ↳ :cmdline_args because :project ↳ :use_backtrace is enabled.", Verbosity::COMPLAIN, LogLabels::NOTICE )
    end
  end

  # Copy CMock options used by test runner generation
  config[:test_runner][:mock_prefix] = config[:cmock][:mock_prefix]
  config[:test_runner][:mock_suffix] = config[:cmock][:mock_suffix]
  config[:test_runner][:enforce_strict_ordering] = config[:cmock][:enforce_strict_ordering]

  # Merge Unity options used by test runner generation
  config[:test_runner][:defines] += config[:unity][:defines]
  config[:test_runner][:use_param_tests] = config[:unity][:use_param_tests]

  @runner_config = config[:test_runner]

  @loginator.lazy( Verbosity::DEBUG ) do
    "Test Runner configuration >> #{config[:test_runner]}"
  end
end

#populate_tools_config(config) ⇒ Object

Process our tools

  • :tools entries
    • Insert missing names for
    • Handle needed defaults
  • Configure test runner from backtrace configuration


421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
# File 'lib/ceedling/config/configurator.rb', line 421

def populate_tools_config(config)
  @loginator.lazy( Verbosity::OBNOXIOUS ) do
    @reportinator.generate_progress( 'Populating tool definition settings and expanding any string replacements' )
  end

  config[:tools].each_key do |name|
    tool = config[:tools][name]

    if not tool.is_a?(Hash)
      raise CeedlingException.new( "Expected configuration for tool :#{name} is a Hash but found #{tool.class}" )
    end

    # Populate name if not given
    tool[:name] = name.to_s if (tool[:name].nil?)

    # Populate $stderr redirect option
    tool[:stderr_redirect] = StdErrRedirect::NONE if (tool[:stderr_redirect].nil?)

    # Populate optional option to control verification of executable in search paths
    tool[:optional] = false if (tool[:optional].nil?)
  end
end

#populate_tools_shortcuts(config) ⇒ Object

Process any tool definition shortcuts

  • Append extra arguments
  • Redefine executable

:tools_ :arguments: [...] :executable: '...'



452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
# File 'lib/ceedling/config/configurator.rb', line 452

def populate_tools_shortcuts(config)
  @loginator.lazy( Verbosity::OBNOXIOUS ) do
    @reportinator.generate_progress( 'Processing tool definition shortcuts' )
  end

  prefix = 'tools_'
  config[:tools].each do |name, tool|
    # Lookup shortcut tool definition (:tools_<name>)
    shortcut = (prefix + name.to_s).to_sym

    # Try to lookup the executable from user config
    executable, _  = @config_walkinator.fetch_value(shortcut, :executable, 
                       hash:config
                     )

    # Try to lookup arguments from user config
    args_to_add, _ = @config_walkinator.fetch_value(shortcut, :arguments, 
                       hash:config,
                       default: []
                     )

    # Redefine the tool config
    if !executable.nil?
      tool[:executable] = executable
    end

    # Add to the tool config
    if !args_to_add.empty?
      tool[:arguments].concat( args_to_add )
    end

    # Log
    if !args_to_add.empty? or !executable.nil?
      @loginator.lazy( Verbosity::DEBUG ) do 
        msg = " > #{name}\n"

        if !executable.nil?
          msg += "   executable: \"#{executable}\"\n"
        end

        if !args_to_add.empty?
          msg += "   arguments: " + args_to_add.map{|arg| "\"#{arg}\""}.join( ', ' ) + "\n"
        end

        msg
      end
    end
  end
end

#populate_unity_config(config) ⇒ Object



297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/ceedling/config/configurator.rb', line 297

def populate_unity_config(config)
  @loginator.lazy( Verbosity::OBNOXIOUS ) do 
    @reportinator.generate_progress( 'Processing Unity configuration' )
  end

  if config[:unity][:use_param_tests]
    config[:unity][:defines] << 'UNITY_SUPPORT_TEST_CASES'
    config[:unity][:defines] << 'UNITY_SUPPORT_VARIADIC_MACROS'
  end

  @loginator.lazy( Verbosity::DEBUG ) do 
    "Unity configuration >> #{config[:unity]}"
  end
end

#populate_with_defaults(config_hash, defaults_hash) ⇒ Object



288
289
290
291
292
293
294
# File 'lib/ceedling/config/configurator.rb', line 288

def populate_with_defaults( config_hash, defaults_hash )
  @loginator.lazy( Verbosity::OBNOXIOUS ) do 
    @reportinator.generate_progress( 'Populating project configuration with collected default values' )
  end

  @configurator_builder.populate_with_defaults( config_hash, defaults_hash )
end

#prepare_plugins_load_paths(plugins_load_path, config) ⇒ Object



215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/ceedling/config/configurator.rb', line 215

def prepare_plugins_load_paths(plugins_load_path, config)
  # Plugins must be loaded before generic path evaluation & magic that happen later.
  # So, perform path magic here as discrete step.
  # (String replacement and standardization require DI objects not available in bin/ scope.)
  config[:plugins][:load_paths].each do |path|
    path.replace( @ruby_expandinator.expand( path, source: ":plugins ↳ :load_paths" ) )
    FilePathUtils::standardize_in_place( path )
  end

  # Delegate list construction to the shared helper (user paths first, built-in last).
  # This mirrors what RakefileComponentResolver.resolve() does in the bin/ CLI scope,
  # ensuring both scopes search plugins in the same priority order.
  config[:plugins][:load_paths] = RakefileComponentResolver.prepare_plugin_load_paths( config, plugins_load_path )

  return @configurator_plugins.process_aux_load_paths( config )
end

#redefine_element(elem, value) ⇒ Object



788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
# File 'lib/ceedling/config/configurator.rb', line 788

def redefine_element(elem, value)
  # Ensure elem is a symbol
  elem = elem.to_sym if elem.class != Symbol

  # Ensure element already exists
  if not @project_config_hash.include?(elem)
    error = "Could not redefine #{elem} in configurator ⏩️ Element does not exist"
    raise CeedlingException.new(error)
  end

  # Update internal hash
  @project_config_hash[elem] = value

  # Update global constant
  @configurator_builder.build_global_constant(elem, value)
end

#replace_flattened_config(config) ⇒ Object



48
49
50
51
# File 'lib/ceedling/config/configurator.rb', line 48

def replace_flattened_config(config)
  @project_config_hash.merge!(config)
  @configurator_setup.build_constants_and_accessors(@project_config_hash, binding())
end

#resolve_directives_only_preprocessing(config, tool_executor) ⇒ Object



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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/ceedling/config/configurator.rb', line 97

def resolve_directives_only_preprocessing(config, tool_executor)
  # Nothing to probe if preprocessing is disabled
  preprocessing = config[:project][:use_test_preprocessor]
  config[:test_build][:preprocess_directives_only_available] = false
  return if preprocessing == :none

  # When forced fallback is enabled, skip the probe and mark directives-only unavailable
  if config[:test_build][:preprocess_force_fallback]
    @loginator.log(
      "Forcing fallback text-based preprocessing in place of directives-only (:test_build ↳ :preprocess_force_fallback is enabled).",
      Verbosity::COMPLAIN,
      LogLabels::NOTICE
    )
    return
  end

  # Probe whether the configured C preprocessor supports -fdirectives-only.
  # Use the Unity header as it is always-available, self-contained, no include paths needed.
  # Output goes to stdout (no -o flag) to avoid platform-specific null device paths.
  probe_filepath = File.join( CEEDLING_VENDOR, UNITY_LIB_PATH, UNITY_H_FILE )

  # Minimal subset of the directives-only preprocessor tool: no defines, no include paths,
  # no output file. Sufficient to detect `-fdirectives-only` support via warning or exit code.
  probe_tool = {
    executable: config[:tools][:test_file_directives_only_preprocessor][:executable],
    name:       'directives_only_probe',
    arguments:  [
      '-E',
      '-fdirectives-only',
      '-x c',
      "\"${1}\""
    ]
  }

  command = tool_executor.build_command_line( probe_tool, [], probe_filepath )
  # Exception if something goes wrong; we want to detect errors here
  command[:options][:boom] = false
  results = tool_executor.exec( command )

  # Clang and some older GCC emit a warning (not an error) when -fdirectives-only is unsupported
  warning_detected = results[:output].match?( /warning[^\n]+-fdirectives-only/ )

  if warning_detected || results[:exit_code] != 0
    @loginator.log(
      "Preprocessor lacks -fdirectives-only support ➡️ Ceedling will use text-based fallback for preprocessing.",
      Verbosity::COMPLAIN,
      LogLabels::NOTICE
    )
    # :preprocess_directives_only_available already set to false above
  else
    config[:test_build][:preprocess_directives_only_available] = true
  end
end

#set_partials_derived_config(config) ⇒ Object



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/ceedling/config/configurator.rb', line 72

def set_partials_derived_config(config)
  return if !config[:project][:use_partials]

  # If partials enabled, enable mocking
  config[:project][:use_mocks] = true
  @loginator.log( " > Enabled mocking." )

  # If partials enabled, enable full test preprocessing
  config[:project][:use_test_preprocessor] = :all
  @loginator.log( " > Enabled preprocessing." )

  # If partials enabled and CMock's `:treat_inlines` is enabled, quietly disable.
  # Enabling Partials and this feature causes extra processing for Ceedling and CMock that is never used.
  if config[:cmock][:treat_inlines] == :include
    config[:cmock][:treat_inlines] = :exclude
    @loginator.log( "Reverted :cmock ↳ :treat_inlines to :exclude because this CMock feature is superseded by Partials.", Verbosity::COMPLAIN, LogLabels::NOTICE )
  end

  # If partials enabled, inject partials name prefix symbol to all test compilation.
  # Handle both the simple list and matcher hash config formats.
  _partials_prefix_symbol = "CEEDLING_PARTIALS_PREFIX=#{PARTIAL_FILENAME_PREFIX}"
  ConfigMatchinator.append_matcher_entries( config[:defines][:test], _partials_prefix_symbol )
end

#set_verbosity(config) ⇒ Object

Set up essential flattened config related to verbosity. We do this because early config validation failures may need access to verbosity, but the accessors won't be available until after configuration is validated.



57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/ceedling/config/configurator.rb', line 57

def set_verbosity(config)
  # PROJECT_VERBOSITY and PROJECT_DEBUG set at command line processing before Ceedling is loaded

  if (!!defined?(PROJECT_DEBUG) and PROJECT_DEBUG)
    eval("def project_debug() return true end", binding())
  else
    eval("def project_debug() return false end", binding())      
  end

  if !!defined?(PROJECT_VERBOSITY)
    eval("def project_verbosity() return #{PROJECT_VERBOSITY} end", binding())
  end
end

#setupObject



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/ceedling/config/configurator.rb', line 23

def setup()
  # Cmock config reference to provide to CMock for mock generation
  @cmock_config = {} # Default empty hash, replaced by reference below

  # Runner config reference to provide to runner generation
  @runner_config = {} # Default empty hash, replaced by reference below

  # Note: project_config_hash is an instance variable so constants and accessors created
  # in eval() statements in build() have something of proper scope and persistence to reference
  @project_config_hash = {}

  @programmatic_plugins = []
  @rake_plugins   = []

  @project_logging = false
  @sanity_checks   = TestResultsSanityChecks::NORMAL
end

#standardize_paths(config) ⇒ Object



673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
# File 'lib/ceedling/config/configurator.rb', line 673

def standardize_paths(config)
  @loginator.lazy( Verbosity::OBNOXIOUS ) do
    @reportinator.generate_progress( 'Standardizing all paths' )
  end

  # :project ↳ :build_root and :release_build ↳ :artifacts are individual paths
  # that don't follow the _path/_paths key convention — handle them explicitly.
  # :release_build may be absent in minimal configs; guard prevents NoMethodError.
  paths = [config[:project][:build_root]]
  if config[:release_build]
    paths << config[:release_build][:artifacts]
  else
    @loginator.log(
      ":release_build section absent from config ➡️ skipping :artifacts path standardization.",
      Verbosity::COMPLAIN,
      LogLabels::NOTICE
    )
  end

  paths.flatten.each { |path| FilePathUtils::standardize_in_place( path ) }

  config[:paths].each_pair do |collection, paths|
    # Flatten to handle single strings or nested arrays; reject nils (non-String passthrough
    # from standardize) and empty strings left after stripping whitespace-only entries.
    config[:paths][collection] = [paths].flatten
      .map    { |path| FilePathUtils::standardize_in_place( path ) }
      .reject { |path| path.nil? || (path.is_a?( String ) && path.empty?) }
  end

  config[:files].each_pair do |collection, files|
    # Same sanitization as :paths — replace array to remove any nil or empty entries
    # produced by standardize on non-String or whitespace-only values.
    config[:files][collection] = [files].flatten
      .map    { |path| FilePathUtils::standardize_in_place( path ) }
      .reject { |path| path.nil? || (path.is_a?( String ) && path.empty?) }
  end

  config[:tools].each_pair { |_, config| FilePathUtils::standardize_in_place( config[:executable] ) if (config.include? :executable) }

  # All other paths at secondary hash key level processed by convention (`_path`):
  # ex. :toplevel ↳ :foo_path & :toplevel ↳ :bar_paths are standardized
  config.each_pair do |_, child|
    collect_path_list( child ).each { |path| FilePathUtils::standardize_in_place( path ) }
  end
end

#validate_essential(config) ⇒ Object



720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
# File 'lib/ceedling/config/configurator.rb', line 720

def validate_essential(config)
  # Collect all infractions, everybody on probation until final adjudication
  blotter = true

  blotter &= @configurator_setup.validate_required_sections( config )
  blotter &= @configurator_setup.validate_required_section_values( config )

  # Configuration sections can reference environment variables that are evaluated early on.
  # So, we validate :environment early as an essential section.
  blotter &= @configurator_setup.validate_environment_vars( config )

  if !blotter
    raise CeedlingException.new("Ceedling configuration failed validation")
  end
end

#validate_final(config, app_cfg) ⇒ Object



737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
# File 'lib/ceedling/config/configurator.rb', line 737

def validate_final(config, app_cfg)
  # Collect all infractions, everybody on probation until final adjudication
  blotter = true
  blotter &= @configurator_setup.validate_paths( config )
  blotter &= @configurator_setup.validate_tools( config )
  blotter &= @configurator_setup.validate_test_runner_generation(
               config,
               app_cfg[:include_test_case],
               app_cfg[:exclude_test_case]
             )
  blotter &= @configurator_setup.validate_defines( config )
  blotter &= @configurator_setup.validate_flags( config )
  blotter &= @configurator_setup.validate_test_preprocessor( config )
  blotter &= @configurator_setup.validate_backtrace( config )
  blotter &= @configurator_setup.validate_threads( config )
  blotter &= @configurator_setup.validate_plugins( config )

  # Informational notices
  @configurator_setup.warnings_for_problematic_configs( config )

  if !blotter
    raise CeedlingException.new( "Ceedling configuration failed validation" )
  end
end