Class: PreprocessinatorReconstructor
- Defined in:
- lib/ceedling/preprocess/preprocessinator_reconstructor.rb
Instance Method Summary collapse
-
#compact_file_from_expansion(input_filepath:, source_filepath:, output_filepath:) ⇒ Object
File-based convenience wrapper around
compact_from_expansion. -
#compact_from_expansion(input:, filepath:, output:) ⇒ Object
Writes only C code from
inputpreprocessor expansion belonging tofilepathtooutputIO object incrementally (one logical line at a time) without building an intermediate array. -
#extract_file_as_array_from_expansion(input, filepath) ⇒ Object
inputmust have the interface of IO -- StringIO for testing or File in typical use. -
#extract_file_as_string_from_expansion(input, filepath) ⇒ Object
Simple variation of preceding that returns file contents as single string.
-
#extract_include_guard(file_contents) ⇒ Object
Find include guard in file contents as string.
-
#extract_macro_defs(file_contents, include_guard) ⇒ Object
Extract all macro definitions as a list from a file as string.
-
#extract_pragmas(file_contents) ⇒ Object
Extract all pragmas as a list from a file as string.
-
#extract_test_case_directives(file_contents) ⇒ Object
Extract TEST_CASE()/TEST_RANGE()/TEST_MATRIX() calls paired with the name of the test function each stack of calls immediately precedes.
-
#extract_test_directive_macro_calls(file_contents) ⇒ Object
Extract all test directive macros as a list from a file as string.
-
#splice_test_case_directives(contents:, directives:) ⇒ Object
Reinsert TEST_CASE()/TEST_RANGE()/TEST_MATRIX() calls (extracted above from a macro-preserving preprocessor pass) into
contents(built from a fully expanded preprocessor pass, where these macros -- real Unity macros that#defineto nothing -- have already vanished without a trace) immediately ahead of the matching test function's line.
Instance Method Details
#compact_file_from_expansion(input_filepath:, source_filepath:, output_filepath:) ⇒ Object
File-based convenience wrapper around compact_from_expansion.
Opens input_filepath for reading and output_filepath for writing,
then delegates to compact_from_expansion with the resulting IO objects.
106 107 108 109 110 111 112 113 114 |
# File 'lib/ceedling/preprocess/preprocessinator_reconstructor.rb', line 106 def compact_file_from_expansion(input_filepath:, source_filepath:, output_filepath:) # Open input in binary mode: GCC output under non-C locale contains non-ASCII bytes # (localized markers). Per-line clean_encoding in _scan_expansion_for_file handles content. File.open( input_filepath, 'rb' ) do |input| File.open( output_filepath, 'w' ) do |output| compact_from_expansion( input: input, filepath: source_filepath, output: output ) end end end |
#compact_from_expansion(input:, filepath:, output:) ⇒ Object
Writes only C code from input preprocessor expansion belonging to filepath
to output IO object incrementally (one logical line at a time) without building
an intermediate array. output must respond to puts (e.g. File or StringIO).
98 99 100 |
# File 'lib/ceedling/preprocess/preprocessinator_reconstructor.rb', line 98 def compact_from_expansion(input:, filepath:, output:) _scan_expansion_for_file(input, filepath) { |line| output.puts(line) } end |
#extract_file_as_array_from_expansion(input, filepath) ⇒ Object
input must have the interface of IO -- StringIO for testing or File in typical use
82 83 84 85 86 |
# File 'lib/ceedling/preprocess/preprocessinator_reconstructor.rb', line 82 def extract_file_as_array_from_expansion(input, filepath) lines = [] _scan_expansion_for_file(input, filepath) { |line| lines << line } return lines end |
#extract_file_as_string_from_expansion(input, filepath) ⇒ Object
Simple variation of preceding that returns file contents as single string
90 91 92 |
# File 'lib/ceedling/preprocess/preprocessinator_reconstructor.rb', line 90 def extract_file_as_string_from_expansion(input, filepath) return extract_file_as_array_from_expansion(input, filepath).join( "\n" ) end |
#extract_include_guard(file_contents) ⇒ Object
Find include guard in file contents as string
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/ceedling/preprocess/preprocessinator_reconstructor.rb', line 213 def extract_include_guard(file_contents) # Look for first occurrence of #ifndef <sring> followed by #define <string> regex = /#\s*ifndef\s+(\w+)(?:\s*\n)+\s*#\s*define\s+(\w+)/ matches = file_contents.match( regex ) # Return if no match results return nil if matches.nil? # Return if match results are not expected size return nil if matches.size != 3 # Return if #ifndef <string> does not match #define <string> return nil if matches[1] != matches[2] # Return string in common return matches[1] end |
#extract_macro_defs(file_contents, include_guard) ⇒ Object
Extract all macro definitions as a list from a file as string
233 234 235 236 237 238 239 240 |
# File 'lib/ceedling/preprocess/preprocessinator_reconstructor.rb', line 233 def extract_macro_defs(file_contents, include_guard) macro_definitions = extract_multiline_directives( file_contents, 'define' ) # Remove an include guard if provided macro_definitions.reject! {|macro| macro.include?( include_guard ) } if !include_guard.nil? return macro_definitions end |
#extract_pragmas(file_contents) ⇒ Object
Extract all pragmas as a list from a file as string
207 208 209 |
# File 'lib/ceedling/preprocess/preprocessinator_reconstructor.rb', line 207 def extract_pragmas(file_contents) return extract_multiline_directives( file_contents, 'pragma' ) end |
#extract_test_case_directives(file_contents) ⇒ Object
Extract TEST_CASE()/TEST_RANGE()/TEST_MATRIX() calls paired with the name of the test function each stack of calls immediately precedes.
Unlike extract_test_directive_macro_calls above, this cannot be built on top of
extract_tokens_by_regex_list(). That helper (via @parsing_parcels.code_lines) hands
the regex one physical/continuation-joined line at a time, so a pattern spanning
"TEST_CASE(...)\nvoid test_Foo(" could never match -- and that split-across-two-lines
layout is the overwhelmingly common real-world case (each macro call is a line of its
own, the function signature is the next line down). So we scan the whole string in one
pass instead, letting PATTERNS::TEST_CASE_DIRECTIVE's /m flag see across line breaks.
file_contents is expected to already have had comments stripped and #if/#ifdef/#include
directives (and their line markers) resolved away -- true of both callers in
PreprocessinatorFileAssembler#collect_test_file_contents: the non-fallback path reads
the directives-only GCC output (already comment-stripped in place by
PreprocessinatorCommentStripper before this ever runs, and already stripped of line
markers by _scan_expansion_for_file above), and the fallback path reads text already
run through @parsing_parcels.code_lines (which strips comments) plus conditional
filtering. So there's nothing here to confuse the adjacency test between a macro call
and the function signature that follows it.
150 151 152 153 154 155 156 157 158 |
# File 'lib/ceedling/preprocess/preprocessinator_reconstructor.rb', line 150 def extract_test_case_directives(file_contents) pairs = [] file_contents.scan( PATTERNS::TEST_CASE_DIRECTIVE ) do |directive, function| pairs << { function: function, directive: directive.strip.split( "\n" ).map( &:rstrip ) } end return pairs end |
#extract_test_directive_macro_calls(file_contents) ⇒ Object
Extract all test directive macros as a list from a file as string
118 119 120 121 122 123 124 125 126 127 |
# File 'lib/ceedling/preprocess/preprocessinator_reconstructor.rb', line 118 def extract_test_directive_macro_calls(file_contents) # Look for TEST_SOURCE_FILE("...") and TEST_INCLUDE_PATH("...") in a string (i.e. a file's contents as a string) regexes = [ /(#{PATTERNS::TEST_SOURCE_FILE})/, /(#{PATTERNS::TEST_INCLUDE_PATH})/ ] return extract_tokens_by_regex_list( file_contents, *regexes ).map(&:first) end |
#splice_test_case_directives(contents:, directives:) ⇒ Object
Reinsert TEST_CASE()/TEST_RANGE()/TEST_MATRIX() calls (extracted above from a
macro-preserving preprocessor pass) into contents (built from a fully expanded
preprocessor pass, where these macros -- real Unity macros that #define to nothing --
have already vanished without a trace) immediately ahead of the matching test function's
line. contents is a scanning artifact only (never compiled), so reinserting this raw,
unexpanded macro text back into it is safe: Unity's own runner generator is the only
consumer, and it only cares that the text is there, immediately before the function.
A single cursor walks contents forward as each directive is placed, rather than
independently searching the whole array per directive. Two reasons: it mirrors the
existing precedent for this exact original-order correlation problem elsewhere in
Ceedling (GeneratorTestRunner#parse_test_file's line-number remap, and Unity's own
find_tests() line-number lookup), and it avoids ever misattributing a directive to an
earlier stray match of the same function name (e.g. a forward declaration) instead of
the real definition that follows it in file order.
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
# File 'lib/ceedling/preprocess/preprocessinator_reconstructor.rb', line 176 def splice_test_case_directives(contents:, directives:) cursor = 0 directives.each do |pair| function_regex = /^\s*void\s+#{Regexp.escape(pair[:function])}\s*\(/ # Search forward from the cursor only. If the function isn't found -- e.g. it was # compiled out by an inactive #ifdef branch, resolved identically in both the source # this directive came from and in `contents` itself -- there's nothing to attach the # directive to, and dropping it silently is correct: an orphaned directive is inert. index = nil contents[cursor..-1].each_with_index do |line, offset| if line =~ function_regex index = cursor + offset break end end next if index.nil? contents.insert( index, *pair[:directive] ) # Advance past the directive lines just inserted and the function line itself so the # next directive's search starts after this one, preserving file order. cursor = index + pair[:directive].size + 1 end return contents end |