mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-02 15:06:06 +00:00
[llvm-cov] Use relative paths to the stylesheet (for html reports)
This makes it easy to swap out the default stylesheet for a custom one. It also shaves ~6.62 MB out of the report directory for a full coverage build of llvm+clang. While we're at it, prune the CSS and add tests for it. llvm-svn: 276359
This commit is contained in:
parent
31fd506623
commit
c076c49076
22
llvm/test/tools/llvm-cov/style.test
Normal file
22
llvm/test/tools/llvm-cov/style.test
Normal file
@ -0,0 +1,22 @@
|
||||
RUN: llvm-cov show %S/Inputs/templateInstantiations.covmapping -instr-profile %S/Inputs/templateInstantiations.profdata -filename-equivalence %S/showTemplateInstantiations.cpp -format html -o %t.dir
|
||||
|
||||
RUN: llvm-cov show %S/Inputs/templateInstantiations.covmapping -instr-profile %S/Inputs/templateInstantiations.profdata -filename-equivalence -name=_Z4funcIbEiT_ %S/showTemplateInstantiations.cpp -format html -o %t.dir
|
||||
|
||||
RUN: FileCheck %s -input-file=%t.dir/style.css -check-prefix=STYLE
|
||||
RUN: FileCheck %s -input-file=%t.dir/functions.html -check-prefix=FUNCTIONS
|
||||
RUN: FileCheck %s -input-file=%t.dir/coverage/tmp/showTemplateInstantiations.cpp.html -check-prefix=FILEVIEW
|
||||
|
||||
STYLE-DAG: .red
|
||||
STYLE-DAG: .cyan
|
||||
STYLE-DAG: .source-name-title
|
||||
STYLE-DAG: .centered
|
||||
STYLE-DAG: .expansion-view
|
||||
STYLE-DAG: .line-number
|
||||
STYLE-DAG: .covered-line
|
||||
STYLE-DAG: .uncovered-line
|
||||
STYLE-DAG: .tooltip
|
||||
STYLE-DAG: .tooltip span.tooltip-content
|
||||
|
||||
FUNCTIONS: <link rel='stylesheet' type='text/css' href='style.css'>
|
||||
|
||||
FILEVIEW: <link rel='stylesheet' type='text/css' href='..{{.*}}..{{.*}}style.css'>
|
@ -99,8 +99,6 @@ struct LineCoverageStats {
|
||||
|
||||
/// \brief A file manager that handles format-aware file creation.
|
||||
class CoveragePrinter {
|
||||
const CoverageViewOptions &Opts;
|
||||
|
||||
public:
|
||||
struct StreamDestructor {
|
||||
void operator()(raw_ostream *OS) const;
|
||||
@ -109,6 +107,8 @@ public:
|
||||
using OwnedStream = std::unique_ptr<raw_ostream, StreamDestructor>;
|
||||
|
||||
protected:
|
||||
const CoverageViewOptions &Opts;
|
||||
|
||||
CoveragePrinter(const CoverageViewOptions &Opts) : Opts(Opts) {}
|
||||
|
||||
/// \brief Return `OutputDir/ToplevelDir/Path.Extension`. If \p InToplevel is
|
||||
|
@ -21,33 +21,51 @@ using namespace llvm;
|
||||
|
||||
namespace {
|
||||
|
||||
// Return a string with the special characters in \p Str escaped.
|
||||
std::string escape(StringRef Str) {
|
||||
std::string Result;
|
||||
for (char C : Str) {
|
||||
if (C == '&')
|
||||
Result += "&";
|
||||
else if (C == '<')
|
||||
Result += "<";
|
||||
else if (C == '>')
|
||||
Result += ">";
|
||||
else if (C == '\"')
|
||||
Result += """;
|
||||
else
|
||||
Result += C;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Create a \p Name tag around \p Str, and optionally set its \p ClassName.
|
||||
std::string tag(const std::string &Name, const std::string &Str,
|
||||
const std::string &ClassName = "") {
|
||||
std::string Tag = "<" + Name;
|
||||
if (ClassName != "")
|
||||
Tag += " class='" + ClassName + "'";
|
||||
return Tag + ">" + Str + "</" + Name + ">";
|
||||
}
|
||||
|
||||
// Create an anchor to \p Link with the label \p Str.
|
||||
std::string a(const std::string &Link, const std::string &Str,
|
||||
const std::string &TargetType = "href") {
|
||||
return "<a " + TargetType + "='" + Link + "'>" + Str + "</a>";
|
||||
}
|
||||
|
||||
const char *BeginHeader =
|
||||
"<head>"
|
||||
"<meta name='viewport' content='width=device-width,initial-scale=1'>"
|
||||
"<meta charset='UTF-8'>";
|
||||
|
||||
const char *CSSForCoverage =
|
||||
"<style>"
|
||||
R"(
|
||||
|
||||
.red {
|
||||
R"(.red {
|
||||
background-color: #FFD0D0;
|
||||
}
|
||||
.cyan {
|
||||
background-color: cyan;
|
||||
}
|
||||
.black {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
.green {
|
||||
background-color: #98FFA6;
|
||||
color: white;
|
||||
}
|
||||
.magenta {
|
||||
background-color: #F998FF;
|
||||
color: white;
|
||||
}
|
||||
body {
|
||||
font-family: -apple-system, sans-serif;
|
||||
}
|
||||
@ -140,9 +158,7 @@ td:first-child {
|
||||
td:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
)"
|
||||
"</style>";
|
||||
)";
|
||||
|
||||
const char *EndHeader = "</head>";
|
||||
|
||||
@ -170,11 +186,28 @@ const char *BeginTable = "<table>";
|
||||
|
||||
const char *EndTable = "</table>";
|
||||
|
||||
void emitPrelude(raw_ostream &OS) {
|
||||
std::string getPathToStyle(StringRef ViewPath) {
|
||||
std::string PathToStyle = "";
|
||||
std::string PathSep = sys::path::get_separator();
|
||||
unsigned NumSeps = ViewPath.count(PathSep);
|
||||
for (unsigned I = 0, E = NumSeps; I < E; ++I)
|
||||
PathToStyle += ".." + PathSep;
|
||||
return PathToStyle + "style.css";
|
||||
}
|
||||
|
||||
void emitPrelude(raw_ostream &OS, const std::string &PathToStyle = "") {
|
||||
OS << "<!doctype html>"
|
||||
"<html>"
|
||||
<< BeginHeader << CSSForCoverage << EndHeader << "<body>"
|
||||
<< BeginCenteredDiv;
|
||||
<< BeginHeader;
|
||||
|
||||
// Link to a stylesheet if one is available. Otherwise, use the default style.
|
||||
if (PathToStyle.empty())
|
||||
OS << "<style>" << CSSForCoverage << "</style>";
|
||||
else
|
||||
OS << "<link rel='stylesheet' type='text/css' href='" << escape(PathToStyle)
|
||||
<< "'>";
|
||||
|
||||
OS << EndHeader << "<body>" << BeginCenteredDiv;
|
||||
}
|
||||
|
||||
void emitEpilog(raw_ostream &OS) {
|
||||
@ -182,39 +215,6 @@ void emitEpilog(raw_ostream &OS) {
|
||||
"</html>";
|
||||
}
|
||||
|
||||
// Return a string with the special characters in \p Str escaped.
|
||||
std::string escape(StringRef Str) {
|
||||
std::string Result;
|
||||
for (char C : Str) {
|
||||
if (C == '&')
|
||||
Result += "&";
|
||||
else if (C == '<')
|
||||
Result += "<";
|
||||
else if (C == '>')
|
||||
Result += ">";
|
||||
else if (C == '\"')
|
||||
Result += """;
|
||||
else
|
||||
Result += C;
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
// Create a \p Name tag around \p Str, and optionally set its \p ClassName.
|
||||
std::string tag(const std::string &Name, const std::string &Str,
|
||||
const std::string &ClassName = "") {
|
||||
std::string Tag = "<" + Name;
|
||||
if (ClassName != "")
|
||||
Tag += " class='" + ClassName + "'";
|
||||
return Tag + ">" + Str + "</" + Name + ">";
|
||||
}
|
||||
|
||||
// Create an anchor to \p Link with the label \p Str.
|
||||
std::string a(const std::string &Link, const std::string &Str,
|
||||
const std::string &TargetType = "href") {
|
||||
return "<a " + TargetType + "='" + Link + "'>" + Str + "</a>";
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
Expected<CoveragePrinter::OwnedStream>
|
||||
@ -224,7 +224,14 @@ CoveragePrinterHTML::createViewFile(StringRef Path, bool InToplevel) {
|
||||
return OSOrErr;
|
||||
|
||||
OwnedStream OS = std::move(OSOrErr.get());
|
||||
emitPrelude(*OS.get());
|
||||
|
||||
if (!Opts.hasOutputDirectory()) {
|
||||
emitPrelude(*OS.get());
|
||||
} else {
|
||||
std::string ViewPath = getOutputPath(Path, "html", InToplevel);
|
||||
emitPrelude(*OS.get(), getPathToStyle(ViewPath));
|
||||
}
|
||||
|
||||
return std::move(OS);
|
||||
}
|
||||
|
||||
@ -252,6 +259,14 @@ Error CoveragePrinterHTML::createIndexFile(ArrayRef<StringRef> SourceFiles) {
|
||||
OSRef << EndTable;
|
||||
emitEpilog(OSRef);
|
||||
|
||||
// Emit the default stylesheet.
|
||||
auto CSSOrErr = createOutputStream("style", "css", /*InToplevel=*/true);
|
||||
if (Error E = CSSOrErr.takeError())
|
||||
return E;
|
||||
|
||||
OwnedStream CSS = std::move(CSSOrErr.get());
|
||||
CSS->operator<<(CSSForCoverage);
|
||||
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user