Skip to content

Commit

Permalink
🐛 :flags & :defines
Browse files Browse the repository at this point in the history
- Fixed :defines validation to allow :preprocess matchers just like :test
- Fixed :preprocess flags handling to use :compile flags unless :preprocess is set
- Fixed :preprocess flags handling to use no extra preprocessing flags if set to empty list regardless of :compile flags
- Fixed :preprocess defines handling to use :test flags unless :preprocess is set
- Fixed :preprocess defines handling to use no extra preprocessing defines if set to empty list regardless of :test defines
- Fixed default :defines to allow the preceding
  • Loading branch information
mkarlesky committed Sep 17, 2024
1 parent d9640c4 commit 8d28b33
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 100 deletions.
180 changes: 112 additions & 68 deletions lib/ceedling/configurator_setup.rb
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ def validate_defines(_config)

valid = true

# Validate that each context contains only a list of symbols or a matcher hash for :test context
# Validate that each context contains only a list of symbols or a matcher hash for :test / :preprocess context
#
# :defines:
# :<context>:
# - FOO
Expand All @@ -218,17 +219,30 @@ def validate_defines(_config)
# :<matcher>:
# - FOO
# - BAR
# :preprocess:
# :<matcher>:
# - FOO
# - BAR

defines.each_pair do |context, config|
walk = @reportinator.generate_config_walk( [:defines, context] )

# Special handling for setting, not context
# Special handling for configuration setting, not a hash context container
next if context == :use_test_definition

# Non-test contexts
if context != :test
# Handle the (probably) common case of trying to use matchers for any context other than test
# Matcher contexts (only contexts that support matcher hashes)
if context == :test or context == :preprocess
if config.class != Array and config.class != Hash
msg = "#{walk} entry '#{config}' must be a list or matcher, not #{config.class.to_s.downcase} (see docs for examples)"
@loginator.log( msg, Verbosity::ERRORS )
valid = false
end

# All other (simple) contexts
else
# Handle the (probably) common case of trying to use matchers for any context other than :test or :preprocess
if config.class == Hash
msg = "#{walk} entry '#{config}' must be a list--matcher hashes only availalbe for the :test context (see docs for details)"
msg = "#{walk} entry '#{config}' must be a list--matcher hashes only availalbe for :test & :preprocess contexts (see docs for details)"
@loginator.log( msg, Verbosity::ERRORS )
valid = false
# Catchall for any oddball entries
Expand All @@ -237,13 +251,6 @@ def validate_defines(_config)
@loginator.log( msg, Verbosity::ERRORS )
valid = false
end
# Test contexts
else
if config.class != Array and config.class != Hash
msg = "#{walk} entry '#{config}' must be a list or matcher, not #{config.class.to_s.downcase} (see docs for examples)"
@loginator.log( msg, Verbosity::ERRORS )
valid = false
end
end
end

Expand All @@ -252,8 +259,9 @@ def validate_defines(_config)
# :<context>: # :test, :release, etc.
# - FOO
# - BAR

defines.each_pair do |context, config|
# Only validate lists of compilation symbols in this block (look for test matchers in next block)
# Only validate lists of compilation symbols in this block (look for matchers in next block)
next if config.class != Array

# Ensure each item in list is a string
Expand All @@ -267,52 +275,60 @@ def validate_defines(_config)
end
end

# Validate test context matchers (hash) if they exist
# Validate :test / :preprocess context matchers (hash) if they exist
# :defines:
# :test:
# :<matcher>: # Can be wildcard, substring, or regular expression in a string or symbol
# - FOO
# - BAR
# :preprocess:
# :<matcher>: # Can be wildcard, substring, or regular expression in a string or symbol
# - FOO
# - BAR

# If there's no test context with a hash of matchers, we're done
return valid if !(defines[:test] and defines[:test].class == Hash)
contexts = [:test, :preprocess]

matchers = defines[:test]
contexts.each do |context|
matchers = defines[context]

walk = @reportinator.generate_config_walk( [:defines, :test] )
# Skip processing if context isn't present or is present but is not a matcher hash
next if matchers.nil? or matchers.class != Hash

# Inspect each test matcher
matchers.each_pair do |matcher, symbols|
walk = @reportinator.generate_config_walk( [:defines, context] )

# Ensure matcher itself is a Ruby symbol or string
if matcher.class != Symbol and matcher.class != String
msg = "#{walk} entry '#{matcher}' is not a string or symbol"
@loginator.log( msg, Verbosity::ERRORS )
valid = false
# Inspect each test matcher
matchers.each_pair do |matcher, symbols|

# Skip further validation if matcher key is not a symbol
next
end
# Ensure matcher itself is a Ruby symbol or string
if matcher.class != Symbol and matcher.class != String
msg = "#{walk} entry '#{matcher}' is not a string or symbol"
@loginator.log( msg, Verbosity::ERRORS )
valid = false

walk = @reportinator.generate_config_walk( [:defines, :test, matcher] )
# Skip further validation if matcher key is not a symbol
next
end

# Ensure each item in compilation symbols list for matcher is a string
symbols.each do |symbol|
if symbol.class != String
msg = "#{walk} entry '#{symbol}' is not a string"
walk = @reportinator.generate_config_walk( [:defines, context, matcher] )

# Ensure each item in compilation symbols list for matcher is a string
symbols.each do |symbol|
if symbol.class != String
msg = "#{walk} entry '#{symbol}' is not a string"
@loginator.log( msg, Verbosity::ERRORS )
valid = false
end
end

begin
@configurator_validator.validate_matcher( matcher.to_s.strip() )
rescue Exception => ex
msg = "Matcher #{walk} contains #{ex.message}"
@loginator.log( msg, Verbosity::ERRORS )
valid = false
end
end

begin
@configurator_validator.validate_matcher( matcher.to_s.strip() )
rescue Exception => ex
msg = "Matcher #{walk} contains #{ex.message}"
@loginator.log( msg, Verbosity::ERRORS )
valid = false
end

end

return valid
Expand All @@ -339,40 +355,74 @@ def validate_flags(_config)
# :<context>: # :test, :release, etc.
# :<operation>: # :compile, :link, etc.
# ...

flags.each_pair do |context, operations|
walk = @reportinator.generate_config_walk( [:flags, context] )

if operations.nil?
msg = "#{walk} operations key / value pairs are missing"
@loginator.log( msg, Verbosity::ERRORS )

valid = false
next
end

if operations.class != Hash
walk = @reportinator.generate_config_walk( [:flags, context] )
example = @reportinator.generate_config_walk( [:flags, context, :compile] )
msg = "#{walk} context must contain :<operation> key / value pairs, not #{operations.class.to_s.downcase} (ex. #{example})"
msg = "#{walk} context must contain :<operation> key / value pairs, not #{operations.class.to_s.downcase} '#{operations}' (ex. #{example})"
@loginator.log( msg, Verbosity::ERRORS )

# Immediately bail out
return false
end
end

# Validate that each operation contains only a list of flags or a matcher hash for :test context
if !!flags[:release] and !!flags[:release][:preprocess]
walk = @reportinator.generate_config_walk( [:flags, :release, :preprocess] )
msg = "Preprocessing configured at #{walk} is only supported in the :test context"
@loginator.log( msg, Verbosity::ERRORS, LogLabels::WARNING )
end

# Validate that each <:context> ↳ <:operation> contains only a list of flags or that :test ↳ <:operation> optionally contains a matcher hash
#
# :flags:
# :<context>:
# :<operation>:
# :<context>: # :test or :release
# :<operation>: # :compile, :link, or :assemble (plus :preprocess for :test context)
# - --flag
#
# or
#
# :flags:
# :test:
# :operation:
# :<operation>:
# :<matcher>:
# - --flag

flags.each_pair do |context, operations|
operations.each_pair do |operation, config|
walk = @reportinator.generate_config_walk( [:defines, context, operation] )
walk = @reportinator.generate_config_walk( [:flags, context, operation] )

if config.nil?
msg = "#{walk} is missing a list or matcher hash"
@loginator.log( msg, Verbosity::ERRORS )

# Non-test contexts
if context != :test
# Handle the (probably) common case of trying to use matchers for any context other than test
valid = false
next
end

# :test context operations with lists or matchers (hashes)
if context == :test
if config.class != Array and config.class != Hash
msg = "#{walk} entry '#{config}' must be a list or matcher hash, not #{config.class.to_s.downcase} (see docs for examples)"
@loginator.log( msg, Verbosity::ERRORS )
valid = false
end

# Other (simple) contexts
else
# Handle the (probably) common case of trying to use matchers for operations in any context other than :test
if config.class == Hash
msg = "#{walk} entry '#{config}' must be a list--matcher hashes only availalbe for the :test context (see docs for details)"
msg = "#{walk} entry '#{config}' must be a list--matcher hashes only availalbe for :test context (see docs for details)"
@loginator.log( msg, Verbosity::ERRORS )
valid = false
# Catchall for any oddball entries
Expand All @@ -381,26 +431,20 @@ def validate_flags(_config)
@loginator.log( msg, Verbosity::ERRORS )
valid = false
end
# Test contexts
else
if config.class != Array and config.class != Hash
msg = "#{walk} entry '#{config}' must be a list or matcher, not #{config.class.to_s.downcase} (see docs for examples)"
@loginator.log( msg, Verbosity::ERRORS )
valid = false
end
end
end
end

# Validate simple option of lists of flags (strings) for :context ↳ :operation
# Validate simple option of lists of flags (strings) for <:context><:operation>
# :flags
# :<context>:
# :<operation>:
# - --flag

flags.each_pair do |context, operations|
operations.each_pair do |operation, flags|

# Only validate lists of flags in this block (look for test matchers in next block)
# Only validate lists of flags in this block (look for matchers in next block)
next if flags.class != Array

# Ensure each item in list is a string
Expand All @@ -415,15 +459,15 @@ def validate_flags(_config)
end
end

# Validate test context matchers (hash) if they exist
# Validate :test ↳ <:operation> matchers (hash) if they exist
# :flags:
# :test:
# :<operation>: # :preprocess, :compile, :assemble, :link
# :<matcher>: # Can be wildcard, substring, or regular expression as a Ruby string or symbol
# - FOO
# - BAR

# If there's no test context with an operation having a hash of matchers, we're done
# If there's no test context, we're done
test_context = flags[:test]
return valid if test_context.nil?

Expand All @@ -435,13 +479,13 @@ def validate_flags(_config)
end
end

# We found no matchers, so bail out
# If there's no matchers for :test ↳ <:operation>, we're done
return valid if !matchers_present

# Inspect each test operation matcher
# Inspect each :test ↳ <:operation> matcher
test_context.each_pair do |operation, matchers|
# Only validate matchers (skip simple lists of flags)
next if !matchers.class == Hash
next if matchers.class != Hash

matchers.each_pair do |matcher, flags|
# Ensure matcher itself is a Ruby symbol or string
Expand All @@ -451,7 +495,7 @@ def validate_flags(_config)
@loginator.log( msg, Verbosity::ERRORS )
valid = false

# Skip further validation if matcher key is not a symbol
# Skip further validation if matcher key is not a string or symbol
next
end

Expand Down
7 changes: 4 additions & 3 deletions lib/ceedling/constants.rb
Original file line number Diff line number Diff line change
Expand Up @@ -125,9 +125,10 @@ class StdErrRedirect
UTILS_TASK_ROOT = UTILS_ROOT_NAME + ':' unless defined?(UTILS_TASK_ROOT)
UTILS_SYM = UTILS_ROOT_NAME.to_sym unless defined?(UTILS_SYM)

OPERATION_COMPILE_SYM = :compile unless defined?(OPERATION_COMPILE_SYM)
OPERATION_ASSEMBLE_SYM = :assemble unless defined?(OPERATION_ASSEMBLE_SYM)
OPERATION_LINK_SYM = :link unless defined?(OPERATION_LINK_SYM)
OPERATION_PREPROCESS_SYM = :preprocess unless defined?(OPERATION_PREPROCESS_SYM)
OPERATION_COMPILE_SYM = :compile unless defined?(OPERATION_COMPILE_SYM)
OPERATION_ASSEMBLE_SYM = :assemble unless defined?(OPERATION_ASSEMBLE_SYM)
OPERATION_LINK_SYM = :link unless defined?(OPERATION_LINK_SYM)


# Match presence of any glob pattern characters
Expand Down
10 changes: 5 additions & 5 deletions lib/ceedling/defaults.rb
Original file line number Diff line number Diff line change
Expand Up @@ -333,15 +333,15 @@

:defines => {
:use_test_definition => false,
:test => [], # A hash/sub-hashes in config file can include operations and test executable matchers as keys
:preprocess => [], # A hash/sub-hashes in config file can include operations and test executable matchers as keys
:release => []
:test => [], # List of symbols or matcher hashes with test executables as keys
# :preprocess is identical to :test but lacks a default here as missing vs. empty values have additional meaning
:release => [] # List of symbols only
},

:flags => {
# Test & release flags are validated for presence--empty flags causes an error
# :test => [], # A hash/sub-hashes in config file can include operations and test executable matchers as keys
# :release => [] # A hash/sub-hashes in config file can include arrays for operations
# :test => {}, # hash/sub-hash of operations containing lists of flags or matcher hashes with test executables as keys
# :release => {} # hash/sub-hashes of operations containing lists of flags
},

:libraries => {
Expand Down
4 changes: 2 additions & 2 deletions lib/ceedling/defineinator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ def defines_defined?(context:)

# Defaults to inspecting configurations beneath top-level :defines
# (But, we can also lookup defines symbol lists within framework configurations--:unity, :cmock, :cexception)
def defines(topkey:@topkey, subkey:, filepath:nil)
def defines(topkey:@topkey, subkey:, filepath:nil, default:[])
defines = @config_matchinator.get_config(primary:topkey, secondary:subkey)

if defines == nil then return []
if defines == nil then return default
elsif defines.is_a?(Array) then return defines.flatten # Flatten to handle list-nested YAML aliases
elsif defines.is_a?(Hash)
arg_hash = {
Expand Down
4 changes: 2 additions & 2 deletions lib/ceedling/flaginator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ def flags_defined?(context:, operation:nil)
return @config_matchinator.config_include?(primary:@section, secondary:context, tertiary:operation)
end

def flag_down(context:, operation:, filepath:nil)
def flag_down(context:, operation:, filepath:nil, default:[])
flags = @config_matchinator.get_config(primary:@section, secondary:context, tertiary:operation)

if flags == nil then return []
if flags == nil then return default
elsif flags.is_a?(Array) then return flags.flatten # Flatten to handle list-nested YAML aliases
elsif flags.is_a?(Hash)
arg_hash = {
Expand Down
Loading

0 comments on commit 8d28b33

Please sign in to comment.