* Added a utility that can be used to produce nice HTML pages from Nix
build logs. The program `log2xml' converts a Nix build log (read from standard input) into XML file that can then be converted to XHTML by the `log2html.xsl' stylesheet. The CSS stylesheet `logfile.css' is necessary to make it look good. This is primarily useful if the log file has a *tree structure*, i.e., that sub-tasks such as the various phases of a build (unpack, configure, make, etc.) or recursive invocations of Make are represented as such. While a log file is in principle an unstructured plain text file, builders can communicate this tree structure to `log2xml' by using escape sequences: - "\e[p" starts a new nesting level; the first line following the escape code is the header; - "\e[q" ends the current nesting level. The generic builder in nixpkgs (not yet committed) uses this. It shouldn't be to hard to patch GNU Make to speak this protocol. Further improvements to the generated HTML pages are to allow collapsing/expanding of subtrees, and to abbreviate store paths (but to show the full path by hovering the mouse over it).
This commit is contained in:
parent
beda10f5a2
commit
9d2669d218
6 changed files with 240 additions and 1 deletions
|
@ -118,6 +118,7 @@ AC_CONFIG_FILES([Makefile
|
|||
src/libexpr/Makefile
|
||||
src/nix-instantiate/Makefile
|
||||
src/nix-env/Makefile
|
||||
src/log2xml/Makefile
|
||||
scripts/Makefile
|
||||
corepkgs/Makefile
|
||||
corepkgs/fetchurl/Makefile
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
SUBDIRS = bin2c boost libutil libstore libmain nix-store nix-hash \
|
||||
libexpr nix-instantiate nix-env
|
||||
libexpr nix-instantiate nix-env log2xml
|
||||
|
|
9
src/log2xml/Makefile.am
Normal file
9
src/log2xml/Makefile.am
Normal file
|
@ -0,0 +1,9 @@
|
|||
bin_PROGRAMS = log2xml
|
||||
|
||||
log2xml_SOURCES = log2xml.cc
|
||||
|
||||
%.xml: %.log log2xml
|
||||
./log2xml < $< > $@
|
||||
|
||||
%.html: %.xml log2html.xsl
|
||||
xsltproc log2html.xsl $< > $@
|
61
src/log2xml/log2html.xsl
Normal file
61
src/log2xml/log2html.xsl
Normal file
|
@ -0,0 +1,61 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
|
||||
<xsl:template match="logfile">
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="logfile.css" type="text/css" />
|
||||
<title>Log File</title>
|
||||
</head>
|
||||
<body>
|
||||
<xsl:apply-templates/>
|
||||
</body>
|
||||
</html>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="nest">
|
||||
<div class='nesting'>
|
||||
<div class='head'>
|
||||
<code>
|
||||
<xsl:value-of select="head"/>
|
||||
</code>
|
||||
</div>
|
||||
<blockquote class='body'>
|
||||
<xsl:for-each select='line|nest'>
|
||||
<xsl:if test="position() != last()">
|
||||
<table class='x'>
|
||||
<tr class='x'>
|
||||
<td class='dummy'>
|
||||
<div class='dummy' />
|
||||
</td>
|
||||
<td>
|
||||
<xsl:apply-templates select='.'/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</xsl:if>
|
||||
<xsl:if test="position() = last()">
|
||||
<table class='y'>
|
||||
<tr class='y'>
|
||||
<td class='dummy'>
|
||||
<div class='dummy' />
|
||||
</td>
|
||||
<td>
|
||||
<xsl:apply-templates select='.'/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</xsl:if>
|
||||
</xsl:for-each>
|
||||
</blockquote>
|
||||
</div>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="line">
|
||||
<code class='line'>
|
||||
<xsl:value-of select="."/>
|
||||
</code>
|
||||
</xsl:template>
|
||||
|
||||
</xsl:stylesheet>
|
102
src/log2xml/log2xml.cc
Normal file
102
src/log2xml/log2xml.cc
Normal file
|
@ -0,0 +1,102 @@
|
|||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
struct Decoder
|
||||
{
|
||||
enum { stTop, stEscape, stCSI } state;
|
||||
string line;
|
||||
bool inHeader;
|
||||
int level;
|
||||
|
||||
Decoder()
|
||||
{
|
||||
state = stTop;
|
||||
line = "";
|
||||
inHeader = false;
|
||||
level = 0;
|
||||
}
|
||||
|
||||
void pushChar(char c);
|
||||
|
||||
void finishLine();
|
||||
};
|
||||
|
||||
|
||||
void Decoder::pushChar(char c)
|
||||
{
|
||||
switch (state) {
|
||||
|
||||
case stTop:
|
||||
if (c == '\e') {
|
||||
state = stEscape;
|
||||
} else if (c == '\n') {
|
||||
finishLine();
|
||||
} else if (c == '<')
|
||||
line += "<";
|
||||
else if (c == '&')
|
||||
line += "&";
|
||||
else
|
||||
line += c;
|
||||
break;
|
||||
|
||||
case stEscape:
|
||||
if (c == '[')
|
||||
state = stCSI;
|
||||
else
|
||||
state = stTop; /* !!! wrong */
|
||||
break;
|
||||
|
||||
case stCSI:
|
||||
if (c >= 0x40 && c != 0x7e) {
|
||||
state = stTop;
|
||||
switch (c) {
|
||||
case 'p':
|
||||
if (line.size()) finishLine();
|
||||
level++;
|
||||
inHeader = true;
|
||||
cout << "<nest>" << endl;
|
||||
break;
|
||||
case 'q':
|
||||
if (line.size()) finishLine();
|
||||
if (level > 0) {
|
||||
level--;
|
||||
cout << "</nest>" << endl;
|
||||
} else
|
||||
cerr << "not enough nesting levels" << endl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Decoder::finishLine()
|
||||
{
|
||||
string tag = inHeader ? "head" : "line";
|
||||
cout << "<" << tag << ">";
|
||||
cout << line;
|
||||
cout << "</" << tag << ">" << endl;
|
||||
line = "";
|
||||
inHeader = false;
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char * * argv)
|
||||
{
|
||||
Decoder dec;
|
||||
int c;
|
||||
|
||||
cout << "<logfile>" << endl;
|
||||
|
||||
while ((c = getchar()) != EOF) {
|
||||
dec.pushChar(c);
|
||||
}
|
||||
|
||||
cout << "</logfile>" << endl;
|
||||
}
|
66
src/log2xml/logfile.css
Normal file
66
src/log2xml/logfile.css
Normal file
|
@ -0,0 +1,66 @@
|
|||
body
|
||||
{
|
||||
font-family: sans-serif;
|
||||
background: white;
|
||||
}
|
||||
|
||||
|
||||
blockquote.body
|
||||
{
|
||||
padding: 6px 0px;
|
||||
margin: 0px 1px;
|
||||
}
|
||||
|
||||
|
||||
table.x, tr.x
|
||||
{
|
||||
border-collapse: separate;
|
||||
border-spacing: 0pt;
|
||||
margin: 0em 0em 0em 0em;
|
||||
padding: 0em 0em 0em 0em;
|
||||
}
|
||||
|
||||
|
||||
tr.x > td.dummy
|
||||
{
|
||||
vertical-align: top;
|
||||
margin: 0em 0em 0em 0em;
|
||||
padding: 0.5em 0em 0em 0em;
|
||||
border-left: 3px solid #6185a0;
|
||||
}
|
||||
|
||||
|
||||
tr.x > td.dummy > div.dummy
|
||||
{
|
||||
width: 1.5em;
|
||||
height: 3px;
|
||||
margin: 0em 0em 0em 0em;
|
||||
border-top: 3px solid #6185a0;
|
||||
}
|
||||
|
||||
|
||||
table.y, tr.y
|
||||
{
|
||||
border-collapse: separate;
|
||||
border-spacing: 0pt;
|
||||
margin: 0em 0em 0em 0em;
|
||||
padding: 0em 0em 0em 0em;
|
||||
}
|
||||
|
||||
|
||||
tr.y > td.dummy
|
||||
{
|
||||
vertical-align: top;
|
||||
margin: 0em 0em 0em 0em;
|
||||
padding: 0em 0em 0em 0em;
|
||||
}
|
||||
|
||||
|
||||
tr.y > td.dummy > div.dummy
|
||||
{
|
||||
width: 1.5em;
|
||||
height: 6px;
|
||||
margin: 0em 0em 0em 0em;
|
||||
border-left: 3px solid #6185a0;
|
||||
border-bottom: 3px solid #6185a0;
|
||||
}
|
Loading…
Reference in a new issue