Skip to content

Commit b88a351

Browse files
committed
🐛 Emit absolute #line paths in generated Partials to fix Windows gcov misattribution
Some GCC/mingw-w64 builds corrupt gcov's --json-format line attribution for #line-remapped source when given a relative path compiled from a sufficiently long/deep working directory, silently misattributing coverage hits to the wrong line (observed on Windows CI). Absolute #line paths sidestep the buggy resolution step entirely; gcovr's own --root handling relativizes them back to clean paths in reports, so there's no user-visible change to output. Reverts the GCC 14 pin added to Windows CI as an earlier, now-disproven fix attempt -- the real defect isn't compiler-version-specific.
1 parent 616d226 commit b88a351

3 files changed

Lines changed: 25 additions & 15 deletions

File tree

.github/workflows/ci.yml

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -198,17 +198,6 @@ jobs:
198198
with:
199199
ruby: ${{ matrix.ruby }}
200200

201-
# Pin Windows CI to a stable GCC 14 release instead of the MSYS2/ridk-bundled
202-
# toolchain's rolling ucrt64 gcc package (drifts forward automatically -- was
203-
# observed at 16.1.0 and mis-attributing gcov coverage lines for #line-remapped
204-
# Partials source with blank lines).
205-
- name: Install Pinned GCC 14 Toolchain (winlibs)
206-
shell: powershell
207-
run: |
208-
Invoke-WebRequest -Uri "https://github.com/brechtsanders/winlibs_mingw/releases/download/14.2.0posix-19.1.1-12.0.0-ucrt-r2/winlibs-x86_64-posix-seh-gcc-14.2.0-mingw-w64ucrt-12.0.0-r2.zip" -OutFile winlibs-gcc14.zip
209-
Expand-Archive -Path winlibs-gcc14.zip -DestinationPath "$env:RUNNER_TEMP\winlibs-gcc14"
210-
echo "$env:RUNNER_TEMP\winlibs-gcc14\mingw64\bin" >> $env:GITHUB_PATH
211-
212201
# Install Gem Dependencies
213202
- name: Install Gem Dependencies
214203
# Ensure local installation to prevent system directory permission problems

lib/ceedling/generators/generator_partials.rb

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,24 @@ def generate_source(io, includes, function_definitions, c_module)
149149
# Blank line before a function when preceded by a non-function item
150150
io << "\n" if anything_emitted && !last_was_func
151151
if func.line_num and func.source_filepath
152-
io << "#line #{func.line_num} \"#{func.source_filepath}\"\n"
152+
# #line is the only thing tying this generated file's code back to its
153+
# original source location -- it's what makes debuggers, error messages, and
154+
# (critically) gcov coverage attribution point at the real file/line instead
155+
# of this generated stand-in. That mapping has to be unambiguous: a relative
156+
# path forces every downstream consumer to first resolve it against whatever
157+
# working directory *it* believes was in effect, and any mismatch or bug in
158+
# that resolution silently corrupts the mapping instead of failing loudly.
159+
# An absolute path removes that resolution step entirely.
160+
#
161+
# This isn't theoretical: some GCC/mingw-w64 builds corrupt gcov's
162+
# --json-format line attribution for #line-remapped source when given a
163+
# relative path compiled from a sufficiently long/deep working directory
164+
# (observed on Windows CI -- correct source line misattributed to a line
165+
# deep in trailing comments). Absolute paths here sidestep that resolution
166+
# step and the bug with it. gcovr's own `--root` handling relativizes
167+
# absolute paths back down to clean relative ones in reports, so this has no
168+
# user-visible effect on report output.
169+
io << "#line #{func.line_num} \"#{File.expand_path(func.source_filepath)}\"\n"
153170
end
154171
io << func.code_block << "\n\n"
155172
emitted_funcs[func.name] = true

spec/units/generators/generator_partials_spec.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -602,16 +602,20 @@ def empty_module
602602
expect( buf.string.strip() ).to eq file_contents.strip()
603603
end
604604

605-
it "should generate a source file with functions and #line directives" do
605+
it "should generate a source file with functions and #line directives using absolute source paths" do
606+
# Absolute paths sidestep a GCC/mingw-w64 gcov --json-format line-attribution bug
607+
# triggered by relative #line paths compiled from a sufficiently long/deep working
608+
# directory (observed on Windows CI). gcovr's own --root handling relativizes these
609+
# back to clean paths in reports, so this has no user-visible effect on output.
606610
file_contents = <<~CONTENTS
607611
// Ceeding generated file
608612
609-
#line 9 "../foo/bar/fubar.c"
613+
#line 9 "#{File.expand_path('../foo/bar/fubar.c')}"
610614
void foobarbaz(int x, int y) {
611615
int z = x+y;
612616
}
613617
614-
#line 123 "src/code/ABC.c"
618+
#line 123 "#{File.expand_path('src/code/ABC.c')}"
615619
int
616620
razzleDazzle(void* ptr)
617621
{

0 commit comments

Comments
 (0)