Class: PreprocessinatorFileAssembler
- Defined in:
- lib/ceedling/preprocess/preprocessinator_file_assembler.rb
Instance Method Summary collapse
- #assemble_preprocessed_code_file(filename:, preprocessed_filepath:, contents:, extras:, includes:) ⇒ Object
- #assemble_preprocessed_header_file(filename:, preprocessed_filepath:, contents:, extras:, includes:) ⇒ Object
- #collect_file_contents_fallback(source_filepath:, defines: []) ⇒ Object
- #collect_file_contents_from_directives_only_preprocessing(source_filepath:, test:) ⇒ Object
- #collect_file_contents_from_full_expansion(source_filepath:, test:) ⇒ Object
-
#collect_macros_and_pragmas_fallback(source_filepath:, defines: []) ⇒ Object
Extract macro definitions and pragmas from a file using text scanning (fallback mode).
- #collect_mockable_header_file_contents(test:, filepath:, directives_only_filepath:, fallback:, flags:, defines:, include_paths:, extras:) ⇒ Object
- #collect_test_file_contents(test:, filepath:, directives_only_filepath:, fallback:, flags:, defines:, include_paths:) ⇒ Object
Instance Method Details
#assemble_preprocessed_code_file(filename:, preprocessed_filepath:, contents:, extras:, includes:) ⇒ Object
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/ceedling/preprocess/preprocessinator_file_assembler.rb', line 347 def assemble_preprocessed_code_file(filename:, preprocessed_filepath:, contents:, extras:, includes:) # Write contents of final preprocessed file a line at a time # ---------------------------------------------------------- @file_wrapper.open( preprocessed_filepath, 'w' ) do |file| # Reinsert #include statements into stripped down file # Rely on Include object stringification for formatting of incudes includes.each { |include| file << "#{include}\n" } # Blank line file << "\n" unless includes.empty? # Add in any extras like test directive macros or preserved macro definitions extras.each do |ex| if ex.class == String file << ex + "\n" elsif ex.class == Array ex.each { |line| file << line + "\n" } end end # Blank line file << "\n" unless extras.empty? # Add extracted contents from preprocessed file contents.each { |line| file << line + "\n" } # Blank line file << "\n" unless contents.empty? end end |
#assemble_preprocessed_header_file(filename:, preprocessed_filepath:, contents:, extras:, includes:) ⇒ Object
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 |
# File 'lib/ceedling/preprocess/preprocessinator_file_assembler.rb', line 200 def assemble_preprocessed_header_file(filename:, preprocessed_filepath:, contents:, extras:, includes:) # Generate #include guard name for header files guardname = FileWrapper.generate_include_guard( filename ) # Write contents of final preprocessed file a line at a time # ---------------------------------------------------------- @file_wrapper.open( preprocessed_filepath, 'w' ) do |file| # Add include guards and extra blank lines to beginning of file contents file << "#ifndef #{guardname}\n" file << "#define #{guardname}\n\n" # Reinsert #include statements into stripped down file # Rely on Include object stringification for formatting of incudes includes.each { |include| file << "#{include}\n" } # Blank line file << "\n" unless includes.empty? # Add in any macro defintions or prgamas extras.each do |ex| if ex.class == String file << ex + "\n" elsif ex.class == Array ex.each { |line| file << line + "\n" } end # Blank line file << "\n" end # Add extracted contents from preprocessed file contents.each { |line| file << line + "\n" } # Add final rear guard with extra blank lines file << "\n#endif // #{guardname}\n" end end |
#collect_file_contents_fallback(source_filepath:, defines: []) ⇒ Object
146 147 148 149 150 151 152 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 |
# File 'lib/ceedling/preprocess/preprocessinator_file_assembler.rb', line 146 def collect_file_contents_fallback(source_filepath:, defines: []) # Open in binary mode + clean encoding: source files may have non-ASCII bytes # in comments (e.g. © symbols) that raise encoding errors under locale-dependent # text-mode encoding on non-C-locale systems. # Call line.chomp before clean_encoding: readlines(chomp:true) in binary mode # removes the \n separator but leaves \r on Windows CRLF lines. clean_encoding's # :universal_newline option would then convert that lone \r → \n, producing lines # with a spurious trailing \n that causes double-newlines in assembled output. # String#chomp (no-arg) removes \r\n, \r, or \n — safe on all platforms. cond_tracker = CPreprocessorConditionals.new( defines ) result = [] @file_wrapper.open( source_filepath, 'rb' ) do |file| file.readlines( chomp: true ).each do |raw_line| line = raw_line.chomp.clean_encoding # Strip inline // comment from a temporary copy for directive detection only. # We preserve the original line (with comments) in output per design intent. stripped = line.sub( /\s*\/\/.*$/, '' ).lstrip # Feed the comment-stripped line to the conditional tracker. # The tracker only reacts to lines beginning with '#'. cond_tracker.process_directive( stripped ) # Skip all preprocessor directive lines from output (they appear in the # assembled file via the includes list and extras rather than raw content). # The non-fallback path produces GCC expansion output which contains no # literal #include/#define/#pragma lines either. next if stripped.start_with?( '#' ) # Skip lines inside inactive conditional blocks next unless cond_tracker.active? result << line end end return result end |
#collect_file_contents_from_directives_only_preprocessing(source_filepath:, test:) ⇒ Object
111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/ceedling/preprocess/preprocessinator_file_assembler.rb', line 111 def collect_file_contents_from_directives_only_preprocessing(source_filepath:, test:) contents = [] preprocessed_filepath = @file_path_utils.form_preprocessed_file_raw_directives_only_filepath( source_filepath, test ) @file_wrapper.open( preprocessed_filepath, 'r' ) do |file| contents = @preprocessinator_reconstructor.extract_file_as_array_from_expansion( file, source_filepath ) end return contents end |
#collect_file_contents_from_full_expansion(source_filepath:, test:) ⇒ Object
187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/ceedling/preprocess/preprocessinator_file_assembler.rb', line 187 def collect_file_contents_from_full_expansion(source_filepath:, test:) contents = [] full_expansion_filepath = @file_path_utils.form_preprocessed_file_full_expansion_filepath( source_filepath, test ) @file_wrapper.open( full_expansion_filepath, 'r' ) do |file| contents = @preprocessinator_reconstructor.extract_file_as_array_from_expansion( file, source_filepath ) end return contents end |
#collect_macros_and_pragmas_fallback(source_filepath:, defines: []) ⇒ Object
Extract macro definitions and pragmas from a file using text scanning (fallback mode). Used to preserve #define macros and #pragma directives in partial file reconstruction when the directives-only preprocessor (-fdirectives-only) is not available. Mirrors the fallback branch of collect_mockable_header_file_contents.
127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/ceedling/preprocess/preprocessinator_file_assembler.rb', line 127 def collect_macros_and_pragmas_fallback(source_filepath:, defines: []) include_guard = @preprocessinator_reconstructor.extract_include_guard( @file_wrapper.read( source_filepath, 2048 ).clean_encoding ) pragmas = [] macro_defs = [] @file_wrapper.open( source_filepath, 'rb' ) do |file| _contents = file.read.clean_encoding _contents = _filter_conditionals( _contents, defines ) pragmas = @preprocessinator_reconstructor.extract_pragmas( _contents ) macro_defs = @preprocessinator_reconstructor.extract_macro_defs( _contents, include_guard ) end return pragmas + macro_defs end |
#collect_mockable_header_file_contents(test:, filepath:, directives_only_filepath:, fallback:, flags:, defines:, include_paths:, extras:) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 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 107 108 |
# File 'lib/ceedling/preprocess/preprocessinator_file_assembler.rb', line 26 def collect_mockable_header_file_contents( test:, filepath:, directives_only_filepath:, fallback:, flags:, defines:, include_paths:, extras: ) contents = [] # Our extra file content to be preserved # Leave these empty if :extras is false pragmas = [] macro_defs = [] preprocessed_filepath = @file_path_utils.form_preprocessed_file_full_expansion_filepath( filepath, test ) # Run GCC with full preprocessor expansion command = @tool_executor.build_command_line( @configurator.tools_test_file_full_preprocessor, # Additional arguments flags, # Argument replacement filepath, preprocessed_filepath, defines, include_paths ) @tool_executor.exec( command ) @file_wrapper.open( preprocessed_filepath, 'r' ) do |file| contents = @preprocessinator_reconstructor.extract_file_as_array_from_expansion( file, filepath ) end # Bail out if no extras are required return contents, [] if !extras # Try to find an #include guard in the first 2k of the file text. # An #include guard is one macro from the original file we don't want to preserve if we can help it. # We create our own #include guard in the header file we create. # It's possible preserving the macro from the original file's #include guard could trip something up. # Of course, it's also possible some header conditional compilation feature is dependent on it. # ¯\_(ツ)_/¯ include_guard = @preprocessinator_reconstructor.extract_include_guard( @file_wrapper.read( filepath, 2048 ).clean_encoding ) if fallback msg = @reportinator.generate_module_progress( operation: "Using fallback method to extract pragmas and macros from", module_name: test, filename: File.basename(filepath) ) @loginator.log( msg, Verbosity::OBNOXIOUS, LogLabels::WARNING ) # Open in binary mode + clean encoding: source files may contain non-ASCII bytes # in comments (e.g. © symbols) that cause encoding errors under non-C locale. @file_wrapper.open( filepath, 'rb' ) do |file| # TODO: Modify to process line-at-a-time for memory savings & performance boost _contents = file.read.clean_encoding # Filter out inactive conditional blocks before extraction so that only # pragmas and macros that are active for the current defines list are returned. _contents = _filter_conditionals( _contents, defines ) # Extract pragmas and macros from pragmas = @preprocessinator_reconstructor.extract_pragmas( _contents ) macro_defs = @preprocessinator_reconstructor.extract_macro_defs( _contents, include_guard ) end else @file_wrapper.open( directives_only_filepath, 'r' ) do |file| # Get code contents of preprocessed directives-only file as a string # TODO: Modify to process line-at-a-time for memory savings & performance boost _contents = @preprocessinator_reconstructor.extract_file_as_string_from_expansion( file, filepath ) # Extract pragmas and macros from pragmas = @preprocessinator_reconstructor.extract_pragmas( _contents ) macro_defs = @preprocessinator_reconstructor.extract_macro_defs( _contents, include_guard ) end end return contents, (pragmas + macro_defs) end |
#collect_test_file_contents(test:, filepath:, directives_only_filepath:, fallback:, flags:, defines:, include_paths:) ⇒ Object
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 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/ceedling/preprocess/preprocessinator_file_assembler.rb', line 240 def collect_test_file_contents( test:, filepath:, directives_only_filepath:, fallback:, flags:, defines:, include_paths: ) contents = [] # TEST_SOURCE_FILE() and TEST_INCLUDE_PATH() test_directives = [] # TEST_CASE() / TEST_RANGE() / TEST_MATRIX(), paired with the test function name each precedes test_case_directives = [] preprocessed_filepath = @file_path_utils.form_preprocessed_file_full_expansion_filepath( filepath, test ) # Run GCC with full preprocessor expansion command = @tool_executor.build_command_line( @configurator.tools_test_file_full_preprocessor, # Additional arguments flags, # Argument replacement filepath, preprocessed_filepath, defines, include_paths ) @tool_executor.exec( command ) @file_wrapper.open( preprocessed_filepath, 'r' ) do |file| contents = @preprocessinator_reconstructor.extract_file_as_array_from_expansion( file, filepath ) end if fallback msg = @reportinator.generate_module_progress( operation: "Using fallback method to extract test directive macros from", module_name: test, filename: File.basename(filepath) ) @loginator.log( msg, Verbosity::OBNOXIOUS, LogLabels::WARNING ) # Open in binary mode + clean encoding: source files may contain non-ASCII bytes # in comments (e.g. © symbols) that cause encoding errors under non-C locale. @file_wrapper.open( filepath, 'rb' ) do |file| _contents = file.read.clean_encoding # Filter out inactive conditional blocks before extraction so that only # test directives that are active for the current defines list are returned. _contents = _filter_conditionals( _contents, defines ) # Extract TEST_SOURCE_FILE() and TEST_INCLUDE_PATH() test_directives = @preprocessinator_reconstructor.extract_test_directive_macro_calls( _contents ) # Extract TEST_CASE()/TEST_RANGE()/TEST_MATRIX() calls paired with the test function # they immediately precede -- these vanish from `contents` above because they're real # (empty-expanding) Unity macros erased by full preprocessor expansion, so we must # recover them from this macro-preserving text instead. test_case_directives = @preprocessinator_reconstructor.extract_test_case_directives( _contents ) end else @file_wrapper.open( directives_only_filepath, 'r' ) do |file| # Get code contents of preprocessed directives-only file as a string _contents = @preprocessinator_reconstructor.extract_file_as_string_from_expansion( file, filepath ) # Extract TEST_SOURCE_FILE() and TEST_INCLUDE_PATH() test_directives = @preprocessinator_reconstructor.extract_test_directive_macro_calls( _contents ) # Extract TEST_CASE()/TEST_RANGE()/TEST_MATRIX() calls paired with the test function # they immediately precede (see comment in the fallback branch above) test_case_directives = @preprocessinator_reconstructor.extract_test_case_directives( _contents ) end end # Reinsert TEST_CASE()/TEST_RANGE()/TEST_MATRIX() calls immediately ahead of their test # function in `contents` -- full expansion erased them in place with no trace (not even a # blank line), so position must be recovered by matching on the function name they modify. contents = @preprocessinator_reconstructor.splice_test_case_directives( contents: contents, directives: test_case_directives ) return contents, test_directives end |