Index: test/tools/llvm-cov/f1.c
===================================================================
--- /dev/null
+++ test/tools/llvm-cov/f1.c
@@ -0,0 +1,8 @@
+int f1() {}
+
+// RUN: llvm-profdata merge %S/Inputs/multiple-files.proftext -o %t.profdata
+// RUN: llvm-cov show %S/Inputs/multiple-files.covmapping -format=html -o %t.dir -instr-profile %t.profdata -filename-equivalence %S/f1.c %S/f2.c %S/f3.c
+// RUN: FileCheck -check-prefixes=HTML -input-file %t.dir/coverage/tmp/coverage/f1.c.html %s
+// HTML: Index
+// HTML-NOT: >Prev<
+// HTML: Next
Index: test/tools/llvm-cov/f2.c
===================================================================
--- /dev/null
+++ test/tools/llvm-cov/f2.c
@@ -0,0 +1,8 @@
+int f2() {}
+
+// RUN: llvm-profdata merge %S/Inputs/multiple-files.proftext -o %t.profdata
+// RUN: llvm-cov show %S/Inputs/multiple-files.covmapping -format=html -o %t.dir -instr-profile %t.profdata -filename-equivalence %S/f1.c %S/f2.c %S/f3.c
+// RUN: FileCheck -check-prefixes=HTML -input-file %t.dir/coverage/tmp/coverage/a/f2.c.html %s
+// HTML: Index
+// HTML: Prev
+// HTML: Next
Index: test/tools/llvm-cov/f3.c
===================================================================
--- /dev/null
+++ test/tools/llvm-cov/f3.c
@@ -0,0 +1,8 @@
+int f3() {}
+
+// RUN: llvm-profdata merge %S/Inputs/multiple-files.proftext -o %t.profdata
+// RUN: llvm-cov show %S/Inputs/multiple-files.covmapping -format=html -o %t.dir -instr-profile %t.profdata -filename-equivalence %S/f1.c %S/f2.c %S/f3.c
+// RUN: FileCheck -check-prefixes=HTML -input-file %t.dir/coverage/tmp/coverage/b/f3.c.html %s
+// HTML: Index
+// HTML: Prev
+// HTML-NOT: >Next<
Index: test/tools/llvm-cov/showProjectSummary.cpp
===================================================================
--- test/tools/llvm-cov/showProjectSummary.cpp
+++ test/tools/llvm-cov/showProjectSummary.cpp
@@ -27,7 +27,7 @@
// Test html output.
// RUN: llvm-cov show %S/Inputs/showProjectSummary.covmapping -format=html -o %t.dir -instr-profile %t.profdata -filename-equivalence %s
-// RUN: FileCheck -check-prefixes=HTML,HTML-FILE,HTML-HEADER,HTML-FOOTER -input-file %t.dir/coverage/tmp/showProjectSummary.cpp.html %s
+// RUN: FileCheck -check-prefixes=HTML,HTML-FILE,HTML-HEADER,HTML-FOOTER,HTML-NAVIGATION -input-file %t.dir/coverage/tmp/showProjectSummary.cpp.html %s
// RUN: llvm-cov show %S/Inputs/showProjectSummary.covmapping -format=html -o %t.dir -instr-profile %t.profdata -project-title "Test Suite" -filename-equivalence %s
// RUN: FileCheck -check-prefixes=HTML-TITLE,HTML,HTML-FILE,HTML-HEADER,HTML-FOOTER -input-file %t.dir/coverage/tmp/showProjectSummary.cpp.html %s
// RUN: FileCheck -check-prefixes=HTML-TITLE,HTML,HTML-FOOTER -input-file %t.dir/index.html %s
@@ -36,6 +36,7 @@
// HTML-TITLE:
Test Suite
// HTML: Coverage Report
// HTML: Created:{{.*}}
+// HTML-NAVIGATION: Index
// HTML-FILE: {{.*}}showProjectSummary.cpp (Binary: showProjectSummary.covmapping)
// HTML-FUNCTION: main
// HTML-HEADER: Line No. |
Index: tools/llvm-cov/CodeCoverage.cpp
===================================================================
--- tools/llvm-cov/CodeCoverage.cpp
+++ tools/llvm-cov/CodeCoverage.cpp
@@ -691,8 +691,9 @@
ThreadCount = std::thread::hardware_concurrency();
ThreadPool Pool(ThreadCount);
- for (StringRef SourceFile : SourceFiles) {
- Pool.async([this, SourceFile, &Coverage, &Printer, ShowFilenames] {
+ for (size_t I = 0, E = SourceFiles.size(); I < E; ++I) {
+ StringRef SourceFile = SourceFiles[I];
+ Pool.async([this, SourceFile, &Coverage, &Printer, ShowFilenames, I] {
auto View = createSourceFileView(SourceFile, *Coverage);
if (!View) {
warning("The file '" + SourceFile.str() + "' isn't covered.");
@@ -707,7 +708,8 @@
auto OS = std::move(OSOrErr.get());
View->print(*OS.get(), /*Wholefile=*/true,
- /*ShowSourceName=*/ShowFilenames);
+ /*ShowSourceName=*/ShowFilenames, /*ViewDepth=*/0,
+ SourceFiles, I);
Printer->closeViewFile(std::move(OS));
});
}
Index: tools/llvm-cov/CoverageViewOptions.h
===================================================================
--- tools/llvm-cov/CoverageViewOptions.h
+++ tools/llvm-cov/CoverageViewOptions.h
@@ -63,6 +63,9 @@
VersionString += LLVM_VERSION_STRING;
return VersionString;
}
+
+ /// \brief Return the sub-directory name for file coverage reports.
+ static StringRef getCoverageDir() { return "coverage"; }
};
}
Index: tools/llvm-cov/SourceCoverageView.h
===================================================================
--- tools/llvm-cov/SourceCoverageView.h
+++ tools/llvm-cov/SourceCoverageView.h
@@ -122,9 +122,6 @@
Expected createOutputStream(StringRef Path, StringRef Extension,
bool InToplevel) const;
- /// \brief Return the sub-directory name for file coverage reports.
- static StringRef getCoverageDir() { return "coverage"; }
-
public:
static std::unique_ptr
create(const CoverageViewOptions &Opts);
@@ -242,7 +239,9 @@
/// \brief Render the project title, the report title \p CellText and the
/// created time for the view.
- virtual void renderCellInTitle(raw_ostream &OS, StringRef CellText) = 0;
+ virtual void renderCellInTitle(raw_ostream &OS, StringRef CellText,
+ ArrayRef SourceFiles,
+ size_t Index) = 0;
/// \brief Render the table header for a given source file.
virtual void renderTableHeader(raw_ostream &OS, unsigned FirstUncoveredLineNo,
@@ -294,7 +293,9 @@
/// \brief Print the code coverage information for a specific portion of a
/// source file to the output stream.
void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName,
- unsigned ViewDepth = 0);
+ unsigned ViewDepth = 0,
+ ArrayRef SourceFiles = ArrayRef(),
+ size_t Index = 0);
};
} // namespace llvm
Index: tools/llvm-cov/SourceCoverageView.cpp
===================================================================
--- tools/llvm-cov/SourceCoverageView.cpp
+++ tools/llvm-cov/SourceCoverageView.cpp
@@ -39,7 +39,7 @@
FullPath.append(Opts.ShowOutputDirectory);
if (!InToplevel)
- sys::path::append(FullPath, getCoverageDir());
+ sys::path::append(FullPath, Opts.getCoverageDir());
SmallString<256> ParentPath = sys::path::parent_path(Path);
sys::path::remove_dots(ParentPath, /*remove_dot_dots=*/true);
@@ -167,9 +167,10 @@
}
void SourceCoverageView::print(raw_ostream &OS, bool WholeFile,
- bool ShowSourceName, unsigned ViewDepth) {
+ bool ShowSourceName, unsigned ViewDepth,
+ ArrayRef SourceFiles, size_t Index) {
if (WholeFile)
- renderCellInTitle(OS, "Coverage Report");
+ renderCellInTitle(OS, "Coverage Report", SourceFiles, Index);
renderViewHeader(OS);
Index: tools/llvm-cov/SourceCoverageViewHTML.h
===================================================================
--- tools/llvm-cov/SourceCoverageViewHTML.h
+++ tools/llvm-cov/SourceCoverageViewHTML.h
@@ -78,11 +78,42 @@
void renderRegionMarkers(raw_ostream &OS, CoverageSegmentArray Segments,
unsigned ViewDepth) override;
- void renderCellInTitle(raw_ostream &OS, StringRef CellText) override;
+ void renderCellInTitle(raw_ostream &OS, StringRef CellText,
+ ArrayRef SourceFiles,
+ size_t Index) override;
void renderTableHeader(raw_ostream &OS, unsigned FirstUncoveredLineNo,
unsigned IndentLevel) override;
+ /// \brief Return a relative filename from \p FirstFilename to \p
+ /// SecondFileName.
+ ///
+ /// Typical usage:
+ /// \code
+ /// case 1:
+ /// first input file1: /coverage/dir1/test.cpp
+ /// seconds input file2: /coverage/dir1/dir2/test1.cpp
+ /// return string: ../../../coverage/dir1/dir2/test1.cpp.html
+ /// case 2:
+ /// first input file1: /output/dir1/test.cpp
+ /// seconds input file2: /output/dir1/test1.cpp
+ /// return string: ../../../output/dir1/test1.cpp.html
+ /// case 3:
+ /// first input file1: /output/dir1/dir2/test.cpp
+ /// seconds input file2: /output/dir1/test1.cpp
+ /// return string: ../../../../output/dir1/test1.cpp.html
+ /// case 4:
+ /// first input file1: /coverage/dir1/test.cpp
+ /// seconds input file2: index.html
+ /// return string: ../../../index.html
+ /// \endcode
+ std::string getRelativePath(const StringRef FirstFilename,
+ const StringRef SecondFileName);
+
+ /// \brief Render the page navigation in the project summary.
+ void renderPageNavigation(raw_ostream &OS, ArrayRef SourceFiles,
+ unsigned Index);
+
public:
SourceCoverageViewHTML(StringRef SourceName, const MemoryBuffer &File,
const CoverageViewOptions &Options,
Index: tools/llvm-cov/SourceCoverageViewHTML.cpp
===================================================================
--- tools/llvm-cov/SourceCoverageViewHTML.cpp
+++ tools/llvm-cov/SourceCoverageViewHTML.cpp
@@ -590,13 +590,18 @@
}
void SourceCoverageViewHTML::renderCellInTitle(raw_ostream &OS,
- StringRef CellText) {
+ StringRef CellText,
+ ArrayRef SourceFiles,
+ size_t Index) {
if (getOptions().hasProjectTitle())
OS << tag(ProjectTitleTag, escape(getOptions().ProjectTitle, getOptions()));
OS << tag(ReportTitleTag, escape(CellText, getOptions()));
if (getOptions().hasCreatedTime())
OS << tag(CreatedTimeTag,
escape(getOptions().CreatedTimeStr, getOptions()));
+
+ if (getOptions().hasOutputDirectory())
+ renderPageNavigation(OS, SourceFiles, Index);
}
void SourceCoverageViewHTML::renderTableHeader(raw_ostream &OS,
@@ -618,3 +623,56 @@
<< SourceLabel;
renderLineSuffix(OS, ViewDepth);
}
+
+std::string
+SourceCoverageViewHTML::getRelativePath(const StringRef FirstFilename,
+ const StringRef SecondFileName) {
+ // Resolve the dots in the file paths.
+ SmallString<256> FileOne(FirstFilename);
+ sys::path::remove_dots(FileOne, /* remove_dot_dot */ true);
+ SmallString<256> FileTwo(SecondFileName);
+ sys::path::remove_dots(FileTwo, /* remove_dot_dot */ true);
+ // HTML uses '/' as the separator
+#ifdef LLVM_ON_WIN32
+ std::replace(FileTwo.begin(), FileTwo.end(), '\\', '/');
+#endif
+ std::string RelativePath;
+ for (auto PathChar : FileOne.str())
+ if (sys::path::is_separator(PathChar))
+ RelativePath += "../";
+ if (SecondFileName == "index.html")
+ RelativePath += SecondFileName.str();
+ else {
+ RelativePath += getOptions().getCoverageDir().str() + '/';
+ RelativePath += sys::path::relative_path(FileTwo).str() + ".html";
+ }
+ return RelativePath;
+}
+
+void SourceCoverageViewHTML::renderPageNavigation(
+ raw_ostream &OS, ArrayRef SourceFiles,
+ unsigned Index) {
+ assert(((Index >= 0) && (Index < SourceFiles.size())) &&
+ "Source file doesn't find");
+ StringRef SourceFileName = SourceFiles[Index];
+ std::string RelativePathToIndex =
+ getRelativePath(SourceFileName, "index.html");
+ StringRef PrevSourceFile;
+ if (Index > 0)
+ PrevSourceFile = SourceFiles[Index - 1];
+ StringRef NextSourceFile;
+ if (Index < (SourceFiles.size() - 1))
+ NextSourceFile = SourceFiles[Index + 1];
+
+ std::string RelativePathToPrev;
+ if (!PrevSourceFile.empty())
+ RelativePathToPrev = getRelativePath(SourceFileName, PrevSourceFile);
+ std::string RelativePathToNext;
+ if (!NextSourceFile.empty())
+ RelativePathToNext = getRelativePath(SourceFileName, NextSourceFile);
+ OS << a(RelativePathToIndex, "Index");
+ if (!RelativePathToPrev.empty())
+ OS << " | " << a(RelativePathToPrev, "Prev");
+ if (!RelativePathToNext.empty())
+ OS << " | " << a(RelativePathToNext, "Next");
+}
Index: tools/llvm-cov/SourceCoverageViewText.h
===================================================================
--- tools/llvm-cov/SourceCoverageViewText.h
+++ tools/llvm-cov/SourceCoverageViewText.h
@@ -71,7 +71,9 @@
void renderRegionMarkers(raw_ostream &OS, CoverageSegmentArray Segments,
unsigned ViewDepth) override;
- void renderCellInTitle(raw_ostream &OS, StringRef CellText) override;
+ void renderCellInTitle(raw_ostream &OS, StringRef CellText,
+ ArrayRef SourceFiles,
+ size_t Index) override;
void renderTableHeader(raw_ostream &OS, unsigned FirstUncoveredLineNo,
unsigned IndentLevel) override;
Index: tools/llvm-cov/SourceCoverageViewText.cpp
===================================================================
--- tools/llvm-cov/SourceCoverageViewText.cpp
+++ tools/llvm-cov/SourceCoverageViewText.cpp
@@ -222,7 +222,9 @@
}
void SourceCoverageViewText::renderCellInTitle(raw_ostream &OS,
- StringRef CellText) {
+ StringRef CellText,
+ ArrayRef SourceFiles,
+ size_t Index) {
if (getOptions().hasProjectTitle())
getOptions().colored_ostream(OS, raw_ostream::CYAN)
<< getOptions().ProjectTitle << "\n";