diff --git a/.gitignore b/.gitignore index d2d6f360b..ffa6c98bc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.py[cod] # C extensions +*.o *.so # Packages @@ -22,9 +23,13 @@ lib64 pip-log.txt # Unit test / coverage reports +.pytest_cache/ .coverage +*.gcda +*.gcno .tox nosetests.xml +testcase # Translations *.mo diff --git a/AUTHORS.txt b/AUTHORS.txt index e60375e9f..2374d87e6 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -26,6 +26,7 @@ The following developers contributed to gcovr (ordered alphabetically): libPhipp, Lukas Atkinson, Luke Woydziak, + Marek Kurdej, Martin Mraz, Matsumoto Taichi, Matthew Stadelman, diff --git a/doc/source/guide.rst b/doc/source/guide.rst index 6c91c5589..5bc1434c8 100644 --- a/doc/source/guide.rst +++ b/doc/source/guide.rst @@ -288,6 +288,47 @@ This is useful mostly when running gcov yourself, and then invoking gcovr with :option:`-g`/:option:`--use-gcov-files`. But these filters also apply when gcov is launched by gcovr. +Speeding up coverage data search +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The :option:`--exclude-directories` filter is used +while searching for raw coverage data +(or for existing ``.gcov`` files when :option:`--use-gcov-files` is active). +This filter is matched against directory paths, not file paths. +If a directory matches, +all its contents (files and subdirectories) will be excluded from the search. +For example, consider this build directory:: + + build/ + ├─ main.o + ├─ main.gcda + ├─ main.gcno + ├─ a/ + │ ├─ awesome_code.o + │ ├─ awesome_code.gcda + │ └─ awesome_code.gcno + └─ b/ + ├─ better_code.o + ├─ better_code.gcda + └─ better_code.gcno + +If we run ``gcovr --exclude-directories 'build/a$'``, +this will exclude anything in the ``build/a`` directory +but will use the coverage data for ``better_code.o`` and ``main.o``. + +This can speed up gcovr when you have a complicated build directory structure. +Consider also using the :option:`search_paths` +or :option:`--object-directory` arguments +to specify where gcovr starts searching. +If you are unsure which directories are being searched, +run gcovr in :option:`--verbose` mode. + +For each found coverage data file gcovr will invoke the ``gcov`` tool. +This is typically the slowest part, +and other filters can only be applied *after* this step. +In some cases, parallel execution with the :option:`-j` option +might be helpful to speed up processing. + Filters for symlinks ~~~~~~~~~~~~~~~~~~~~ diff --git a/gcovr/tests/exclude-directories-relative/Makefile b/gcovr/tests/exclude-directories-relative/Makefile new file mode 100644 index 000000000..6cdbe1e22 --- /dev/null +++ b/gcovr/tests/exclude-directories-relative/Makefile @@ -0,0 +1,28 @@ +CFLAGS= -fprofile-arcs -ftest-coverage -fPIC + +all: + mkdir -p build/a build/b + $(CXX) $(CFLAGS) -c a/file1.cpp -o build/a/file1.o + $(CXX) $(CFLAGS) -c b/main.cpp -o build/b/main.o + $(CXX) $(CFLAGS) build/b/main.o build/a/file1.o -o testcase + +run: txt xml html + +GCOVR_TEST_OPTIONS = --exclude-directories 'build/a' + +txt: + ./testcase + $(GCOVR) $(GCOVR_TEST_OPTIONS) -d -o coverage.txt + +xml: + ./testcase + $(GCOVR) $(GCOVR_TEST_OPTIONS) -d -x -o coverage.xml + +html: + ./testcase + $(GCOVR) $(GCOVR_TEST_OPTIONS) -d --html-details -o coverage.html + +clean: + rm -f testcase + rm -f *.gc* *.o + rm -f coverage.txt coverage.xml coverage*.html diff --git a/gcovr/tests/exclude-directories-relative/README b/gcovr/tests/exclude-directories-relative/README new file mode 100644 index 000000000..b6c904b1c --- /dev/null +++ b/gcovr/tests/exclude-directories-relative/README @@ -0,0 +1,4 @@ +This test case was inspired by ticket #3884: + + https://software.sandia.gov/trac/fast/ticket/3884 + diff --git a/gcovr/tests/exclude-directories-relative/a/file1.cpp b/gcovr/tests/exclude-directories-relative/a/file1.cpp new file mode 100644 index 000000000..e6d3409f1 --- /dev/null +++ b/gcovr/tests/exclude-directories-relative/a/file1.cpp @@ -0,0 +1,5 @@ + +int bar() +{ +return 0; +} diff --git a/gcovr/tests/exclude-directories-relative/b/main.cpp b/gcovr/tests/exclude-directories-relative/b/main.cpp new file mode 100644 index 000000000..7704d05db --- /dev/null +++ b/gcovr/tests/exclude-directories-relative/b/main.cpp @@ -0,0 +1,18 @@ +#include + +int bar(); + +int foo(int param) { + if (param) { + return 1; //std::cout << "param not null." << std::endl; + } else { + return 0; //std::cout << "param is null." << std::endl; + } +} + + +int main(int argc, char* argv[]) { + foo(bar()); + + return 0; +} diff --git a/gcovr/tests/exclude-directories-relative/reference/coverage.html b/gcovr/tests/exclude-directories-relative/reference/coverage.html new file mode 100644 index 000000000..0afc378d1 --- /dev/null +++ b/gcovr/tests/exclude-directories-relative/reference/coverage.html @@ -0,0 +1,347 @@ + + + + + + Head + + + + + + + + + + + + + + +
GCC Code Coverage Report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Directory:b/ExecTotalCoverage
Date:2018-06-26 10:42:40Lines:7887.5 %
Legend: + low: < 75.0 % + medium: >= 75.0 % + high: >= 90.0 % + Branches:3650.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

FileLinesBranches
main.cpp +
+
+
+
87.5 %7 / 850.0 %3 / 6

+
+ + + + +
+
+ + + + + diff --git a/gcovr/tests/exclude-directories-relative/reference/coverage.txt b/gcovr/tests/exclude-directories-relative/reference/coverage.txt new file mode 100644 index 000000000..1a4e663fe --- /dev/null +++ b/gcovr/tests/exclude-directories-relative/reference/coverage.txt @@ -0,0 +1,10 @@ +------------------------------------------------------------------------------ + GCC Code Coverage Report +Directory: . +------------------------------------------------------------------------------ +File Lines Exec Cover Missing +------------------------------------------------------------------------------ +b/main.cpp 8 7 87% 7 +------------------------------------------------------------------------------ +TOTAL 8 7 87% +------------------------------------------------------------------------------ diff --git a/gcovr/tests/exclude-directories-relative/reference/coverage.xml b/gcovr/tests/exclude-directories-relative/reference/coverage.xml new file mode 100644 index 000000000..8fff212d3 --- /dev/null +++ b/gcovr/tests/exclude-directories-relative/reference/coverage.xml @@ -0,0 +1,36 @@ + + + + +. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gcovr/tests/exclude-relative/Makefile b/gcovr/tests/exclude-relative/Makefile new file mode 100644 index 000000000..f850d1855 --- /dev/null +++ b/gcovr/tests/exclude-relative/Makefile @@ -0,0 +1,27 @@ +CFLAGS= -fprofile-arcs -ftest-coverage -fPIC + +all: + $(CXX) $(CFLAGS) -c file1.cpp -o file1.o + $(CXX) $(CFLAGS) -c main.cpp -o main.o + $(CXX) $(CFLAGS) main.o file1.o -o testcase + +run: txt xml html + +GCOVR_TEST_OPTIONS = -e 'file1.cpp' # use a relative filter here + +txt: + ./testcase + $(GCOVR) $(GCOVR_TEST_OPTIONS) -d -o coverage.txt + +xml: + ./testcase + $(GCOVR) $(GCOVR_TEST_OPTIONS) -d -x -o coverage.xml + +html: + ./testcase + $(GCOVR) $(GCOVR_TEST_OPTIONS) -d --html-details -o coverage.html + +clean: + rm -f testcase + rm -f *.gc* *.o + rm -f coverage.txt coverage.xml coverage*.html diff --git a/gcovr/tests/exclude-relative/README b/gcovr/tests/exclude-relative/README new file mode 100644 index 000000000..b6c904b1c --- /dev/null +++ b/gcovr/tests/exclude-relative/README @@ -0,0 +1,4 @@ +This test case was inspired by ticket #3884: + + https://software.sandia.gov/trac/fast/ticket/3884 + diff --git a/gcovr/tests/exclude-relative/file1.cpp b/gcovr/tests/exclude-relative/file1.cpp new file mode 100644 index 000000000..e6d3409f1 --- /dev/null +++ b/gcovr/tests/exclude-relative/file1.cpp @@ -0,0 +1,5 @@ + +int bar() +{ +return 0; +} diff --git a/gcovr/tests/exclude-relative/main.cpp b/gcovr/tests/exclude-relative/main.cpp new file mode 100644 index 000000000..7704d05db --- /dev/null +++ b/gcovr/tests/exclude-relative/main.cpp @@ -0,0 +1,18 @@ +#include + +int bar(); + +int foo(int param) { + if (param) { + return 1; //std::cout << "param not null." << std::endl; + } else { + return 0; //std::cout << "param is null." << std::endl; + } +} + + +int main(int argc, char* argv[]) { + foo(bar()); + + return 0; +} diff --git a/gcovr/tests/exclude-relative/reference/coverage.html b/gcovr/tests/exclude-relative/reference/coverage.html new file mode 100644 index 000000000..f20f25b04 --- /dev/null +++ b/gcovr/tests/exclude-relative/reference/coverage.html @@ -0,0 +1,347 @@ + + + + + + Head + + + + + + + + + + + + + + +
GCC Code Coverage Report
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Directory:.ExecTotalCoverage
Date:0000-00-00 00:00:00Lines:7887.5 %
Legend: + low: < 75.0 % + medium: >= 75.0 % + high: >= 90.0 % + Branches:3650.0 %
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

FileLinesBranches
main.cpp +
+
+
+
87.5 %7 / 850.0 %3 / 6

+
+ + + + +
+
+ + + + + diff --git a/gcovr/tests/exclude-relative/reference/coverage.txt b/gcovr/tests/exclude-relative/reference/coverage.txt new file mode 100644 index 000000000..8881e357b --- /dev/null +++ b/gcovr/tests/exclude-relative/reference/coverage.txt @@ -0,0 +1,10 @@ +------------------------------------------------------------------------------ + GCC Code Coverage Report +Directory: . +------------------------------------------------------------------------------ +File Lines Exec Cover Missing +------------------------------------------------------------------------------ +main.cpp 8 7 87% 7 +------------------------------------------------------------------------------ +TOTAL 8 7 87% +------------------------------------------------------------------------------ diff --git a/gcovr/tests/exclude-relative/reference/coverage.xml b/gcovr/tests/exclude-relative/reference/coverage.xml new file mode 100644 index 000000000..46ecf15bc --- /dev/null +++ b/gcovr/tests/exclude-relative/reference/coverage.xml @@ -0,0 +1,36 @@ + + + + +. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gcovr/utils.py b/gcovr/utils.py index d10d89b39..9efb1f4b0 100644 --- a/gcovr/utils.py +++ b/gcovr/utils.py @@ -134,11 +134,9 @@ def set_preferred(self, master, preferred): # def link_walker(path, exclude_dirs): for root, dirs, files in os.walk(os.path.abspath(path), followlinks=True): - for exc in exclude_dirs: - for d in dirs: - m = exc.search(d) - if m is not None: - dirs[:] = [d for d in dirs if d is not m.group()] + dirs[:] = [d for d in dirs + if not any(exc.match(os.path.join(root, d)) + for exc in exclude_dirs)] yield (os.path.abspath(os.path.realpath(root)), dirs, files) @@ -153,7 +151,7 @@ def search_file(expr, path, exclude_dirs): path = os.getcwd() elif not os.path.exists(path): raise IOError("Unknown directory '" + path + "'") - for root, dirs, files in link_walker(path, exclude_dirs): + for root, _, files in link_walker(path, exclude_dirs): for name in files: if pattern.match(name): name = os.path.join(root, name) @@ -165,15 +163,14 @@ def search_file(expr, path, exclude_dirs): def commonpath(files): - if len(files) == 1: return os.path.join(os.path.relpath(os.path.split(files[0])[0]), '') common_path = os.path.realpath(files[0]) common_dirs = common_path.split(os.path.sep) - for f in files[1:]: - path = os.path.realpath(f) + for filepath in files[1:]: + path = os.path.realpath(filepath) dirs = path.split(os.path.sep) common = [] for a, b in zip(dirs, common_dirs): @@ -200,13 +197,13 @@ def get_global_stats(covdata): keys = list(covdata.keys()) for key in keys: - (t, n, txt) = covdata[key].coverage(show_branch=False) - lines_total += t - lines_covered += n + (total, covered, _) = covdata[key].coverage(show_branch=False) + lines_total += total + lines_covered += covered - (t, n, txt) = covdata[key].coverage(show_branch=True) - branches_total += t - branches_covered += n + (total, covered, _) = covdata[key].coverage(show_branch=True) + branches_total += total + branches_covered += covered percent = calculate_coverage(lines_covered, lines_total) percent_branches = calculate_coverage(branches_covered, branches_total)