ceedling 1.1.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/GIT_COMMIT_SHA +1 -1
  3. data/README.md +3 -3
  4. data/assets/broken_uncovered_file.c +13 -0
  5. data/assets/test_example_file_2.c +22 -0
  6. data/assets/test_example_file_crash_sigsegv_with_param.c +32 -0
  7. data/assets/tests_with_partials_coverage/src/blanks.c +10 -0
  8. data/assets/tests_with_partials_coverage/src/blanks.h +1 -0
  9. data/assets/tests_with_partials_coverage/src/counter.c +12 -0
  10. data/assets/tests_with_partials_coverage/src/counter.h +1 -0
  11. data/assets/tests_with_partials_coverage/src/decorators.c +11 -0
  12. data/assets/tests_with_partials_coverage/src/decorators.h +4 -0
  13. data/assets/tests_with_partials_coverage/test/test_blanks.c +8 -0
  14. data/assets/tests_with_partials_coverage/test/test_counter.c +9 -0
  15. data/assets/tests_with_partials_coverage/test/test_decorators.c +8 -0
  16. data/bin/actions_wrapper.rb +7 -6
  17. data/docs/Changelog.md +25 -3
  18. data/lib/ceedling/c_extractor/c_extractor_declarations.rb +3 -1
  19. data/lib/ceedling/c_extractor/c_extractor_preprocessing.rb +20 -7
  20. data/lib/ceedling/config/configurator.rb +7 -1
  21. data/lib/ceedling/defaults.rb +8 -2
  22. data/lib/ceedling/generators/generator_partials.rb +13 -3
  23. data/lib/ceedling/generators/generator_test_results_backtrace.rb +136 -68
  24. data/lib/ceedling/generators/generator_test_runner.rb +20 -2
  25. data/lib/ceedling/objects.yml +1 -0
  26. data/lib/ceedling/preprocess/preprocessinator_file_assembler.rb +10 -2
  27. data/lib/ceedling/preprocess/preprocessinator_reconstructor.rb +23 -15
  28. data/lib/version.rb +1 -1
  29. data/plugins/gcov/lib/gcov.rb +2 -2
  30. data/site-local/sitemap.xml.gz +0 -0
  31. data/spec/support/system/system_context.rb +15 -0
  32. data/spec/system/deployment_as_gem_spec.rb +2 -0
  33. data/spec/system/deployment_as_vendor_spec.rb +2 -0
  34. data/spec/system/gcov_deployment_spec.rb +20 -6
  35. data/spec/system/support/common_test_cases.rb +90 -0
  36. data/spec/system/support/gcov_common_test_cases.rb +31 -45
  37. data/spec/system/support/gcov_partials_test_cases.rb +116 -0
  38. data/spec/units/c_extractor/c_extractor_declarations_spec.rb +128 -0
  39. data/spec/units/c_extractor/c_extractor_definitions_spec.rb +7 -0
  40. data/spec/units/c_extractor/c_extractor_integration_spec.rb +17 -0
  41. data/spec/units/c_extractor/c_extractor_preprocessing_spec.rb +46 -0
  42. data/spec/units/configurator_spec.rb +45 -0
  43. data/spec/units/generators/generator_partials_spec.rb +32 -6
  44. data/spec/units/generators/generator_test_results_backtrace_spec.rb +49 -5
  45. data/spec/units/generators/generator_test_runner_spec.rb +26 -2
  46. data/spec/units/preprocess/preprocessinator_file_assembler_spec.rb +96 -2
  47. data/spec/units/preprocess/preprocessinator_reconstructor_spec.rb +151 -1
  48. data/spec/units/test_context_extractor_spec.rb +3 -3
  49. metadata +16 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 56f073ff96051065c1f390744785a8e933cbfa700d81a585495b49d44b318f1c
4
- data.tar.gz: 3d9567c9ebb1477eb215fb9cb8cdfd42f76b7b8c5cdfd9cce1010eff89e49ea3
3
+ metadata.gz: 8aabde4baeef95fad89e87cf3bd6226ffa3f10df9e54708270c82f237f3efe49
4
+ data.tar.gz: '08693e70f80deab8cb70f11677291f368a159df04b35f7a8d9fec8025522063a'
5
5
  SHA512:
6
- metadata.gz: 3e108e84bc09d28d53ca336b49cea22e970d6319810b8b68dab3f1dadb9793eb40948589fb771f8f1d6f731ce2319f9ee32b9cc8a12a8f54d32061e4a052c948
7
- data.tar.gz: 6e21e05a55622e2836e52feccd2db2c99a2827db4c6b1179f6ed30c702db8a59dc888fd460d23976d07b3c36237e400b0e553ee9493b3f287456bd10099eae29
6
+ metadata.gz: ab5f32d7896199850b29f6328c9e3fbaea63c0f5bf3dff665434ee0bd488992f971b18afd1bcf194e5c6f2ca66636cd72e38ab26fed3e74904cb374094f9c2b7
7
+ data.tar.gz: fab7cf5fe911d2dbc24bdb16add93551bfe8d9b7a668b888735ccfe199d04431707d5e01ec2d5ce345ee518db9b8f34376e16b92788ba1780e2ed20b8a32832c
data/GIT_COMMIT_SHA CHANGED
@@ -1 +1 @@
1
- e7770cc
1
+ 049a9d7
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Ceedling ![CI](https://github.com/ThrowTheSwitch/Ceedling/workflows/CI/badge.svg)
2
2
  ========
3
3
 
4
- **Ceedling 1.1.0** is the latest and greatest.
4
+ **Ceedling 1.1.3** is the latest and greatest.
5
5
 
6
6
  See [_Release Notes_][release-notes], [_Changelog_](docs/Changelog.md),
7
7
  [_Breaking Changes_][breaking-changes], and [_Known Issues_][known-issues].
@@ -9,7 +9,7 @@ See [_Release Notes_][release-notes], [_Changelog_](docs/Changelog.md),
9
9
  See [🚀 Getting Started](#-getting-started) for installation and new
10
10
  project set up.
11
11
 
12
- **New goodies:**
12
+ **New goodies in 1.1.x:**
13
13
  * A [Partial][partials-docs] allows a test to mix mocked and real functions
14
14
  from the same source module in a single test file and enables testing of
15
15
  `static` and `inline` functions without modifying source code.
@@ -22,7 +22,7 @@ from the same source module in a single test file and enables testing of
22
22
  * New stock plugins for [Valgrind memory checking][valgrind-plugin] and
23
23
  [Cppcheck static analysis][cppcheck-plugin].
24
24
  * The [GCov plugin][gcov-plugin]:
25
- * _Finally_ supports coverage reports for all sources.
25
+ * Now supports coverage reports for all sources.
26
26
  * Adds support for Modified Condition / Decision Coverage.
27
27
  * Mixins improvements including an option for [inline YAML at the command line][mixins-inline-yaml].
28
28
  * Ceedling’s test preprocessing is once again compatible with Unity’s
@@ -0,0 +1,13 @@
1
+ /* =========================================================================
2
+ Ceedling - Test-Centered Build System for C
3
+ ThrowTheSwitch.org
4
+ Copyright (c) 2010-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
5
+ SPDX-License-Identifier: MIT
6
+ ========================================================================= */
7
+
8
+ // Untested source with a syntax error -- fails to compile regardless of any
9
+ // :defines/:flags, deliberately triggering the :untested_sources guidance
10
+ // notice when :gcov :untested_sources is set to :compile.
11
+
12
+ int broken_uncovered_function(int a, int b) {
13
+ return a + b // Missing semicolon and closing brace: guaranteed compile error.
@@ -0,0 +1,22 @@
1
+ /* =========================================================================
2
+ Ceedling - Test-Centered Build System for C
3
+ ThrowTheSwitch.org
4
+ Copyright (c) 2010-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
5
+ SPDX-License-Identifier: MIT
6
+ ========================================================================= */
7
+
8
+ // A second test file covering the same source as test_example_file_success.c.
9
+ // Ceedling builds one executable per test file, and both link against
10
+ // example_file.c, producing two sets of .gcda coverage data for the same
11
+ // functions -- used to exercise gcovr's merge-mode-functions behavior.
12
+
13
+ #include "unity.h"
14
+ #include "example_file.h"
15
+
16
+ void setUp(void) {}
17
+ void tearDown(void) {}
18
+
19
+ void test_difference_between_two_numbers(void)
20
+ {
21
+ TEST_ASSERT_EQUAL_INT(0, difference_between_numbers(1, 1));
22
+ }
@@ -0,0 +1,32 @@
1
+ /* =========================================================================
2
+ Ceedling - Test-Centered Build System for C
3
+ ThrowTheSwitch.org
4
+ Copyright (c) 2010-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
5
+ SPDX-License-Identifier: MIT
6
+ ========================================================================= */
7
+
8
+ #include <signal.h>
9
+ #include "unity.h"
10
+ #include "example_file.h"
11
+
12
+
13
+ void setUp(void) {}
14
+ void tearDown(void) {}
15
+
16
+ void test_add_numbers_will_fail(void) {
17
+ // Platform-independent way of forcing a crash
18
+ // NOTE: Avoid `nullptr` as it is a keyword in C23
19
+ uint32_t* a_null_pointer = (void*)0;
20
+ uint32_t i = *a_null_pointer;
21
+ TEST_ASSERT_EQUAL_INT(2, add_numbers(i,2));
22
+ }
23
+
24
+ // Regression coverage for issue #1185: a parameterized test defined after a
25
+ // crashing test in the same file must still report its own real PASS/FAIL
26
+ // results rather than being swept up as "crashed" alongside the actual crash.
27
+ TEST_CASE(5, 3, 2)
28
+ TEST_CASE(10, 4, 6)
29
+ TEST_CASE(0, 0, 0)
30
+ void test_difference_between_numbers_is_correct(int a, int b, int expected) {
31
+ TEST_ASSERT_EQUAL_INT(expected, difference_between_numbers(a, b));
32
+ }
@@ -0,0 +1,10 @@
1
+ #include "blanks.h"
2
+ void helloBlanks(void) {
3
+
4
+
5
+ return; // Must be the reported hit line -- the whole point of this fixture
6
+ }
7
+
8
+ // Lines 3-4 above are deliberately blank -- standing in for a stripped
9
+ // multi-line comment. They must never absorb the hit that belongs to
10
+ // `return` on line 5; see gcov_partials_coverage_blank_lines_in_function_body.
@@ -0,0 +1 @@
1
+ void helloBlanks(void);
@@ -0,0 +1,12 @@
1
+ #include "counter.h"
2
+ int helloCounter(void) {
3
+ static int count; // Promoted to module scope and replaced with a no-op by Partials
4
+
5
+ count++; // Must independently report as hit
6
+
7
+ return count; // Must independently report as hit
8
+ }
9
+
10
+ // Lines 4 and 6 above are deliberately blank; they must never absorb the
11
+ // hits that belong to `count++;` (line 5) and `return count;` (line 7);
12
+ // see gcov_partials_coverage_function_scope_static_promotion.
@@ -0,0 +1 @@
1
+ int helloCounter(void);
@@ -0,0 +1,11 @@
1
+ #include "decorators.h"
2
+ static // Own line -- exercises the Partials #line adjustment for stripped decorators
3
+ inline // Own line -- same as above
4
+ void helloDecorators(void)
5
+ {
6
+
7
+ return; // Must be the reported hit line
8
+ }
9
+
10
+ // Line 6 above is deliberately blank; it must never absorb the hit that
11
+ // belongs to `return` on line 7; see gcov_partials_coverage_decorators_on_own_lines.
@@ -0,0 +1,4 @@
1
+ // `helloDecorators` is `static` in decorators.c, so -- like any private
2
+ // helper -- it has no prototype here. Its declaration for the test file
3
+ // comes from the generated Partial's own header via TEST_PARTIAL_ALL_MODULE,
4
+ // not from this file.
@@ -0,0 +1,8 @@
1
+ #include "unity.h"
2
+ #include "ceedling.h"
3
+ #include TEST_PARTIAL_ALL_MODULE(blanks)
4
+ #include "blanks.h"
5
+
6
+ void test_helloBlanks(void) {
7
+ helloBlanks();
8
+ }
@@ -0,0 +1,9 @@
1
+ #include "unity.h"
2
+ #include "ceedling.h"
3
+ #include TEST_PARTIAL_ALL_MODULE(counter)
4
+ #include "counter.h"
5
+
6
+ void test_helloCounter(void) {
7
+ TEST_ASSERT_EQUAL(1, helloCounter());
8
+ TEST_ASSERT_EQUAL(2, helloCounter());
9
+ }
@@ -0,0 +1,8 @@
1
+ #include "unity.h"
2
+ #include "ceedling.h"
3
+ #include TEST_PARTIAL_ALL_MODULE(decorators)
4
+ #include "decorators.h"
5
+
6
+ void test_helloDecorators(void) {
7
+ helloDecorators();
8
+ }
@@ -13,16 +13,17 @@ class ActionsWrapper
13
13
  include Thor::Base
14
14
  include Thor::Actions
15
15
 
16
- JUNK_FILE_EXCLUDE_REGEX =
16
+ JUNK_FILE_EXCLUDE_REGEX = /(\.DS_Store)|(thumbs\.db)/
17
17
 
18
18
  # Most important mixin method is Thor::Actions class method `source_root()` we call externally
19
19
 
20
20
  def _directory(src, *args)
21
- # Insert exclusion of macOS and Windows preview junk files if an exclude pattern is not present
22
- # Thor's use of args is an array of call arguments, some of which can be single key/value hash options
23
- if !args.any? {|h| h.class != Hash ? false : !h[:exclude_pattern].nil?}
24
- args << {:exclude_pattern => /(\.DS_Store)|(thumbs\.db)/}
25
- end
21
+ # Build a single trailing options hash -- Thor's directory() only reads
22
+ # the last argument as options, so any caller-supplied hash (e.g.
23
+ # :force) and this default :exclude_pattern must live in the same hash.
24
+ options = args.last.is_a?(Hash) ? args.pop : {}
25
+ options[:exclude_pattern] ||= JUNK_FILE_EXCLUDE_REGEX
26
+ args << options
26
27
 
27
28
  directory( src, *args )
28
29
  end
data/docs/Changelog.md CHANGED
@@ -10,6 +10,28 @@ This changelog is complemented by three other documents:
10
10
 
11
11
  ---
12
12
 
13
+ # [1.1.3] — 2026-08-06
14
+
15
+ ## 💪 Fixed
16
+ - [#1185](https://github.com/ThrowTheSwitch/Ceedling/issues/1185) Fixed / improved crash handling for parameterized Unity test cases.
17
+ - [#1186](https://github.com/ThrowTheSwitch/Ceedling/issues/1186) Fixed `gcc` version lookup for the Gcov plugin running on Windows.
18
+ - Ensure compilation symbols needed for Partials are injected when `:preprocess` defines are configured.
19
+ - Fixed use of Thor’s Actions#directory to ensure a single options hash.
20
+
21
+ # [1.1.2] — 2026-07-27
22
+
23
+ ## 💪 Fixed
24
+
25
+ - Fixed line end handling for certain circumstances in Windows that could accidentally create extra white space lines that affect file line numbering (particularly in coverage reporting).
26
+
27
+ ### Partials
28
+
29
+ - [#1182](https://github.com/ThrowTheSwitch/Ceedling/issues/1182) Fixed termination newline handling for certain variable declarations. Robustified all similar cases in C feature extraction and added further test coverage.
30
+ - [#1184](https://github.com/ThrowTheSwitch/Ceedling/issues/1184) Fixed escaped character handling in `#define` string literals.
31
+ - [#1183](https://github.com/ThrowTheSwitch/Ceedling/issues/1183) Fixed postprocessed preprocessed files (ha) to preserve blank lines in function definitions where comments were stripped out by preprocessing. Without preserving blank lines, line numbering for coverage reporting purposes becomes corrupted.
32
+
33
+ ---
34
+
13
35
  # [1.1.1] — 2026-07-23
14
36
 
15
37
  ## 🌟 Added
@@ -18,15 +40,15 @@ This changelog is complemented by three other documents:
18
40
 
19
41
  ## 💪 Fixed
20
42
 
21
- New since 1.1.0:
43
+ ### New since 1.1.0
22
44
 
23
45
  - [#1180](https://github.com/ThrowTheSwitch/Ceedling/issues/1180) Fixed `gcovr` illegal `--exclude` flag when Gcovr configuration file in use.
24
46
  - [#1181](https://github.com/ThrowTheSwitch/Ceedling/issues/1181) Fixed linker error when test files contain no test cases (fix was implemented in Unity and vendored into Ceedling).
25
47
 
26
- Backlog bug fixes predating 1.1.0:
48
+ ### Backlog bug fixes predating 1.1.0
27
49
 
28
50
  - [#1064](https://github.com/ThrowTheSwitch/Ceedling/issues/1064) Fixed include guard handling that in a certain combination of preprocessing and CMock options led to duplicated symbols breaking a test build.
29
- - [#1098](https://github.com/ThrowTheSwitch/Ceedling/issues/1098) Fixed :test_compiler used instead of :test_assembler for assembly file.
51
+ - [#1098](https://github.com/ThrowTheSwitch/Ceedling/issues/1098) Fixed `:test_compiler` used instead of `:test_assembler` for assembly file.
30
52
  - [#1009](https://github.com/ThrowTheSwitch/Ceedling/issues/1009) Fixed tool name handling for `command_hooks` plugin.
31
53
 
32
54
  ## ⚠️ Changed
@@ -167,7 +167,9 @@ class CExtractorDeclarations
167
167
  # Verify this looks like a valid declaration
168
168
  # Must have at least a type and identifier
169
169
  # Can end with: word character, ], ), }, or " (for string initializers)
170
- if declaration =~ /\w+.*[\w\]\)\}"']\s*;$/
170
+ # `/m` flag: declaration may span multiple lines (e.g. a multiline brace
171
+ # initializer), so '.' must match embedded newlines too
172
+ if declaration =~ /\w+.*[\w\]\)\}"']\s*;$/m
171
173
  return [true, expand_and_parse(declaration)]
172
174
  else
173
175
  scanner.pos = start_pos
@@ -142,8 +142,9 @@ class CExtractorPreprocessing
142
142
  return [false, nil] unless success
143
143
  text << args
144
144
 
145
- # Consume optional whitespace before ';'
146
- text << (scanner.scan(/[ \t]*/) || '')
145
+ # Consume optional whitespace before ';' (including newlines, for statements formatted
146
+ # with the terminating ';' on its own line)
147
+ text << (scanner.scan(/\s*/) || '')
147
148
 
148
149
  # Consume the required terminating ';'
149
150
  return [false, nil] unless scanner.scan(/;/)
@@ -231,20 +232,32 @@ class CExtractorPreprocessing
231
232
 
232
233
  # Collect and return the full text of a preprocessing directive starting at '#'.
233
234
  # Returns nil if not positioned at '#'. Handles backslash-newline continuations.
235
+ # String/char literals are consumed verbatim via skip_c_string() so an in-string
236
+ # escape (e.g. "\n", "\"", "\\") is never mistaken for a line continuation.
234
237
  # Trailing whitespace and newlines are stripped from the returned text.
235
238
  def _collect_directive(scanner)
236
239
  return nil unless scanner.check(/#/)
237
240
 
238
241
  text = scanner.scan(/#/)
239
242
 
240
- loop do
241
- text += scanner.scan(/[^\n\\]*/) || ''
243
+ until scanner.eos?
244
+ text << (scanner.scan(/[^"'\\\n]*/) || '')
245
+
246
+ if (ch = scanner.peek(1)) == '"' || ch == "'"
247
+ before = scanner.pos
248
+ @c_extractor_code_text.skip_c_string(scanner, ch)
249
+ text << scanner.string[before...scanner.pos]
250
+
251
+ elsif scanner.scan(/\\\n/)
252
+ text << "\\\n"
242
253
 
243
- if scanner.scan(/\\\n/)
244
- text += "\\\n"
245
254
  elsif scanner.scan(/\n/)
246
- text += "\n"
255
+ text << "\n"
247
256
  break
257
+
258
+ elsif scanner.scan(/\\/)
259
+ text << '\\'
260
+
248
261
  else
249
262
  break # EOS — no trailing newline
250
263
  end
@@ -88,10 +88,16 @@ class Configurator
88
88
  @loginator.log( "Reverted :cmock ↳ :treat_inlines to :exclude because this CMock feature is superseded by Partials.", Verbosity::COMPLAIN, LogLabels::NOTICE )
89
89
  end
90
90
 
91
- # If partials enabled, inject partials name prefix symbol to all test compilation.
91
+ # If partials enabled, inject partials name prefix symbol to all test compilation
92
+ # and, if a project defines its own preprocess-scoped defines matcher, to
93
+ # preprocessing as well -- `:preprocess:` defines are looked up independently
94
+ # of `:test:` defines and only fall back to the latter when `:preprocess:`
95
+ # is absent entirely, so a project-defined `:preprocess:` matcher needs this
96
+ # symbol appended directly or Partials' own macro-based #includes break.
92
97
  # Handle both the simple list and matcher hash config formats.
93
98
  _partials_prefix_symbol = "CEEDLING_PARTIALS_PREFIX=#{PARTIAL_FILENAME_PREFIX}"
94
99
  ConfigMatchinator.append_matcher_entries( config[:defines][:test], _partials_prefix_symbol )
100
+ ConfigMatchinator.append_matcher_entries( config[:defines][:preprocess], _partials_prefix_symbol )
95
101
  end
96
102
 
97
103
 
@@ -69,7 +69,10 @@ DEFAULT_TEST_FIXTURE_SIMPLE_BACKTRACE_TOOL = {
69
69
  :name => 'default_test_fixture_simple_backtrace'.freeze,
70
70
  :optional => false.freeze,
71
71
  :arguments => [
72
- '-n ${2}'.freeze # Exact test case name matching flag
72
+ # Test case matching flag and (quoted) value, fully composed by the caller as
73
+ # either `-n "<exact name>"` or `-f "<base name>"` -- see
74
+ # GeneratorTestResultsBacktrace#unity_filter_arg.
75
+ '${2}'.freeze
73
76
  ].freeze
74
77
  }
75
78
 
@@ -202,7 +205,10 @@ DEFAULT_TEST_BACKTRACE_GDB_TOOL = {
202
205
  "--command \"${1}\"".freeze, # Debug script file to run
203
206
  '--args'.freeze,
204
207
  '${2}'.freeze, # Test executable
205
- '-n ${3}'.freeze # Exact test case name matching flag
208
+ # Test case matching flag and (quoted) value, fully composed by the caller as
209
+ # either `-n "<exact name>"` or `-f "<base name>"` -- see
210
+ # GeneratorTestResultsBacktrace#unity_filter_arg.
211
+ '${3}'.freeze
206
212
  ].freeze
207
213
  }
208
214
 
@@ -28,11 +28,15 @@ class GeneratorPartials
28
28
  header_filepath = File.join(output_path, header)
29
29
  source_filepath = File.join(output_path, source)
30
30
 
31
- @file_wrapper.open(header_filepath, 'w') do |file|
31
+ # Binary mode: the function bodies written below may already contain their
32
+ # own line endings verbatim. Windows text mode rewrites every "\n" on
33
+ # write, which would alter any line ending already present in that
34
+ # content instead of passing it through unchanged.
35
+ @file_wrapper.open(header_filepath, 'wb') do |file|
32
36
  generate_header(file, header, header_includes, function_definitions, c_module, true)
33
37
  end
34
38
 
35
- @file_wrapper.open(source_filepath, 'w') do |file|
39
+ @file_wrapper.open(source_filepath, 'wb') do |file|
36
40
  generate_source(file, source_includes, function_definitions, c_module)
37
41
  end
38
42
 
@@ -43,7 +47,8 @@ class GeneratorPartials
43
47
  header = @file_path_utils.form_partial_interface_header_filename(name)
44
48
  filepath = File.join(output_path, header)
45
49
 
46
- @file_wrapper.open(filepath, 'w') do |file|
50
+ # Binary mode: see generate_implementation above.
51
+ @file_wrapper.open(filepath, 'wb') do |file|
47
52
  generate_header(file, header, includes, function_declarations, c_module, false)
48
53
  end
49
54
 
@@ -149,6 +154,11 @@ class GeneratorPartials
149
154
  # Blank line before a function when preceded by a non-function item
150
155
  io << "\n" if anything_emitted && !last_was_func
151
156
  if func.line_num and func.source_filepath
157
+ # #line ties this generated file's code back to its original source
158
+ # location -- debuggers, error messages, and gcov coverage attribution
159
+ # all point at the real file/line instead of this generated stand-in.
160
+ # That mapping only holds if this file's line count matches what
161
+ # #line promises, which is why it's opened in binary mode above.
152
162
  io << "#line #{func.line_num} \"#{func.source_filepath}\"\n"
153
163
  end
154
164
  io << func.code_block << "\n\n"