]> git.lizzy.rs Git - rust.git/blob - src/test/run-make/coverage-reports/Makefile
partially fix src/test/run-make/coverage-reports when cross-compiling
[rust.git] / src / test / run-make / coverage-reports / Makefile
1 # needs-profiler-support
2 # ignore-windows-gnu
3
4 # FIXME(pietroalbini): this test currently does not work on cross-compiled
5 # targets because remote-test is not capable of sending back the *.profraw
6 # files generated by the LLVM instrumentation.
7 # ignore-cross-compile
8
9 # Rust coverage maps support LLVM Coverage Mapping Format versions 5 and 6,
10 # corresponding with LLVM versions 12 and 13, respectively.
11 # When upgrading LLVM versions, consider whether to enforce a minimum LLVM
12 # version during testing, with an additional directive at the top of this file
13 # that sets, for example: `min-llvm-version: 12.0`
14
15 # FIXME(mati865): MinGW GCC miscompiles compiler-rt profiling library but with Clang it works
16 # properly. Since we only have GCC on the CI ignore the test for now.
17
18 include ../coverage/coverage_tools.mk
19
20 BASEDIR=../coverage-reports
21 SOURCEDIR=../coverage
22
23 # The `llvm-cov show` flag `--debug`, used to generate the `counters` output files, is only
24 # enabled if LLVM assertions are enabled. This requires Rust config `llvm/optimize` and not
25 # `llvm/release_debuginfo`. Note that some CI builds disable debug assertions (by setting
26 # `NO_LLVM_ASSERTIONS=1`), so the tests must still pass even if the `--debug` flag is
27 # not supported. (Note that `counters` files are only produced in the `$(TMPDIR)`
28 # directory, for inspection and debugging support. They are *not* copied to `expected_*`
29 # files when `--bless`ed.)
30 LLVM_COV_DEBUG := $(shell \
31                 "$(LLVM_BIN_DIR)"/llvm-cov show --debug 2>&1 | \
32                 grep -q "Unknown command line argument '--debug'"; \
33                 echo $$?)
34 ifeq ($(LLVM_COV_DEBUG), 1)
35 DEBUG_FLAG=--debug
36 endif
37
38 # FIXME(richkadel): I'm adding `--ignore-filename-regex=` line(s) for specific test(s) that produce
39 # `llvm-cov` results for multiple files (for example `uses_crate.rs` and `used_crate/mod.rs`) as a
40 # workaround for two problems causing tests to fail on Windows:
41 #
42 # 1. When multiple files appear in the `llvm-cov show` results, each file's coverage results can
43 #    appear in different a different order. Whether this is random or, somehow, platform-specific,
44 #    the Windows output flips the order of the files, compared to Linux. In the `uses_crate.rs`
45 #    test, the only test-unique (interesting) results we care about are the results for only one
46 #    of the two files, `mod/uses_crate.rs`, so the workaround is to ignore all but this one file.
47 #    In the future, we may want a more sophisticated solution that splits apart `llvm-cov show`
48 #    results into separate results files for each result (taking care not to create new file
49 #    paths that might be too long for Windows MAX_PATH limits when creating these new sub-results,
50 #    as well).
51 # 2. When multiple files appear in the `llvm-cov show` results, the results for each file are
52 #    prefixed with their filename, including platform-specific path separators (`\` for Windows,
53 #    and `/` everywhere else). This could be filtered or normalized of course, but by ignoring
54 #    coverage results for all but one of the file, the filenames are no longer included anyway.
55 #    If this changes (if/when we decide to support `llvm-cov show` results for multiple files),
56 #    the file path separator differences may need to be addressed.
57 #
58 # Since this is only a workaround, I decided to implement the override by adding an option for
59 # each file to be ignored, using a `--ignore-filename-regex=` entry for each one, rather than
60 # implement some more sophisticated solution with a new custom test directive in the test file
61 # itself (similar to `expect-exit-status`) because that would add a lot of complexity and still
62 # be a workaround, with the same result, with no benefit.
63 #
64 # Yes these `--ignore-filename-regex=` options are included in all invocations of `llvm-cov show`
65 # for now, but it is effectively ignored for all tests that don't include this file anyway.
66 #
67 # (Note that it's also possible the `_counters.<test>.txt` and `<test>.json` files (if generated)
68 # may order results from multiple files inconsistently, which might also have to be accommodated
69 # if and when we allow `llvm-cov` to produce results for multiple files. Note, the path separators
70 # appear to be normalized to `/` in those files, thankfully.)
71 LLVM_COV_IGNORE_FILES=\
72         --ignore-filename-regex='(uses_crate.rs|uses_inline_crate.rs|unused_mod.rs)'
73
74 all: $(patsubst $(SOURCEDIR)/lib/%.rs,%,$(wildcard $(SOURCEDIR)/lib/*.rs)) $(patsubst $(SOURCEDIR)/%.rs,%,$(wildcard $(SOURCEDIR)/*.rs))
75
76 # Ensure there are no `expected` results for tests that may have been removed or renamed
77 .PHONY: clear_expected_if_blessed
78 clear_expected_if_blessed:
79 ifdef RUSTC_BLESS_TEST
80         rm -f expected_*
81 endif
82
83 include clear_expected_if_blessed
84
85 %: $(SOURCEDIR)/lib/%.rs
86         # Compile the test library with coverage instrumentation
87         $(RUSTC) $(SOURCEDIR)/lib/$@.rs \
88                         $$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/lib/$@.rs ) \
89                         --crate-type rlib -Cinstrument-coverage --target $(TARGET)
90
91 %: $(SOURCEDIR)/%.rs
92         # Compile the test program with coverage instrumentation
93         $(RUSTC) $(SOURCEDIR)/$@.rs \
94                         $$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/$@.rs ) \
95                         -L "$(TMPDIR)" -Cinstrument-coverage --target $(TARGET)
96
97         # Run it in order to generate some profiling data,
98         # with `LLVM_PROFILE_FILE=<profdata_file>` environment variable set to
99         # output the coverage stats for this run.
100         LLVM_PROFILE_FILE="$(TMPDIR)"/$@.profraw \
101                         $(call RUN,$@) || \
102                         ( \
103                                 status=$$?; \
104                                 grep -q "^\/\/ expect-exit-status-$$status" $(SOURCEDIR)/$@.rs || \
105                                 ( >&2 echo "program exited with an unexpected exit status: $$status"; \
106                                         false \
107                                 ) \
108                         )
109
110         # Run it through rustdoc as well to cover doctests.
111         # `%p` is the pid, and `%m` the binary signature. We suspect that the pid alone
112         # might result in overwritten files and failed tests, as rustdoc spawns each
113         # doctest as its own process, so make sure the filename is as unique as possible.
114         LLVM_PROFILE_FILE="$(TMPDIR)"/$@-%p-%m.profraw \
115                         $(RUSTDOC) --crate-name workaround_for_79771 --test $(SOURCEDIR)/$@.rs \
116                         $$( sed -n 's/^\/\/ compile-flags: \([^#]*\).*/\1/p' $(SOURCEDIR)/$@.rs ) \
117                         -L "$(TMPDIR)" -Cinstrument-coverage \
118                         -Z unstable-options --persist-doctests=$(TMPDIR)/rustdoc-$@
119
120         # Postprocess the profiling data so it can be used by the llvm-cov tool
121         "$(LLVM_BIN_DIR)"/llvm-profdata merge --sparse \
122                         "$(TMPDIR)"/$@*.profraw \
123                         -o "$(TMPDIR)"/$@.profdata
124
125         # Generate a coverage report using `llvm-cov show`.
126         "$(LLVM_BIN_DIR)"/llvm-cov show \
127                         $(DEBUG_FLAG) \
128                         $(LLVM_COV_IGNORE_FILES) \
129                         --compilation-dir=. \
130                         --Xdemangler="$(RUST_DEMANGLER)" \
131                         --show-line-counts-or-regions \
132                         --instr-profile="$(TMPDIR)"/$@.profdata \
133                         $(call BIN,"$(TMPDIR)"/$@) \
134                         $$( \
135                                 for file in $(TMPDIR)/rustdoc-$@/*/rust_out; do \
136                                 [ -x "$$file" ] && printf "%s %s " -object $$file; \
137                                 done \
138                         ) \
139                 2> "$(TMPDIR)"/show_coverage_stderr.$@.txt \
140                 | "$(PYTHON)" $(BASEDIR)/normalize_paths.py \
141                 > "$(TMPDIR)"/actual_show_coverage.$@.txt || \
142         ( status=$$? ; \
143                 >&2 cat "$(TMPDIR)"/show_coverage_stderr.$@.txt ; \
144                 exit $$status \
145         )
146
147 ifdef DEBUG_FLAG
148         # The first line (beginning with "Args:" contains hard-coded, build-specific
149         # file paths. Strip that line and keep the remaining lines with counter debug
150         # data.
151         tail -n +2 "$(TMPDIR)"/show_coverage_stderr.$@.txt \
152                 > "$(TMPDIR)"/actual_show_coverage_counters.$@.txt
153 endif
154
155 ifdef RUSTC_BLESS_TEST
156         cp "$(TMPDIR)"/actual_show_coverage.$@.txt \
157                         expected_show_coverage.$@.txt
158 else
159         # Compare the show coverage output (`--bless` refreshes `typical` files).
160         #
161         # FIXME(richkadel): None of the Rust test source samples have the
162         # `// ignore-llvm-cov-show-diffs` anymore. This directive exists to work around a limitation
163         # with `llvm-cov show`. When reporting coverage for multiple instantiations of a generic function,
164         # with different type substitutions, `llvm-cov show` prints these in a non-deterministic order,
165         # breaking the `diff` comparison.
166         #
167         # A partial workaround is implemented below, with `diff --ignore-matching-lines=RE`
168         # to ignore each line prefixing each generic instantiation coverage code region.
169         #
170         # This workaround only works if the coverage counts are identical across all reported
171         # instantiations. If there is no way to ensure this, you may need to apply the
172         # `// ignore-llvm-cov-show-diffs` directive, and check for differences using the
173         # `.json` files to validate that results have not changed. (Until then, the JSON
174         # files are redundant, so there is no need to generate `expected_*.json` files or
175         # compare actual JSON results.)
176
177         $(DIFF) --ignore-matching-lines='^  | .*::<.*>.*:$$' --ignore-matching-lines='^  | <.*>::.*:$$' \
178                 expected_show_coverage.$@.txt "$(TMPDIR)"/actual_show_coverage.$@.txt || \
179                 ( grep -q '^\/\/ ignore-llvm-cov-show-diffs' $(SOURCEDIR)/$@.rs && \
180                         >&2 echo 'diff failed, but suppressed with `// ignore-llvm-cov-show-diffs` in $(SOURCEDIR)/$@.rs' \
181                 ) || \
182                 ( >&2 echo 'diff failed, and not suppressed without `// ignore-llvm-cov-show-diffs` in $(SOURCEDIR)/$@.rs'; \
183                         false \
184                 )
185 endif