aboutsummaryrefslogtreecommitdiff
path: root/src/nix-log2xml
diff options
context:
space:
mode:
Diffstat (limited to 'src/nix-log2xml')
-rw-r--r--src/nix-log2xml/Makefile.am17
-rw-r--r--src/nix-log2xml/log2html.xsl76
-rw-r--r--src/nix-log2xml/log2xml.cc172
-rw-r--r--src/nix-log2xml/logfile.css86
-rw-r--r--src/nix-log2xml/mark-errors.xsl24
-rw-r--r--src/nix-log2xml/treebits.js50
6 files changed, 425 insertions, 0 deletions
diff --git a/src/nix-log2xml/Makefile.am b/src/nix-log2xml/Makefile.am
new file mode 100644
index 000000000..4b6c76c24
--- /dev/null
+++ b/src/nix-log2xml/Makefile.am
@@ -0,0 +1,17 @@
+bin_PROGRAMS = nix-log2xml
+
+nix_log2xml_SOURCES = log2xml.cc
+
+%.xml: %.log nix-log2xml
+ ./nix-log2xml < $< > $@
+
+%.html: %.xml mark-errors.xsl log2html.xsl
+ $(xsltproc) mark-errors.xsl $< | $(xsltproc) log2html.xsl - > $@
+
+LOG2HTML = mark-errors.xsl log2html.xsl treebits.js
+
+EXTRA_DIST = $(LOG2HTML)
+
+install-data-local:
+ $(INSTALL) -d $(DESTDIR)$(datadir)/nix/log2html
+ $(INSTALL_DATA) $(LOG2HTML) $(DESTDIR)$(datadir)/nix/log2html
diff --git a/src/nix-log2xml/log2html.xsl b/src/nix-log2xml/log2html.xsl
new file mode 100644
index 000000000..0718df230
--- /dev/null
+++ b/src/nix-log2xml/log2html.xsl
@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+ <xsl:output method='html' />
+
+ <xsl:template match="logfile">
+ <html>
+ <head>
+ <script type="text/javascript" src="treebits.js" />
+ <link rel="stylesheet" href="logfile.css" type="text/css" />
+ <title>Log File</title>
+ </head>
+ <body>
+ <ul class='toplevel'>
+ <xsl:for-each select='line|nest'>
+ <li>
+ <xsl:apply-templates select='.'/>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </body>
+ </html>
+ </xsl:template>
+
+
+ <xsl:template match="nest">
+
+ <!-- The tree should be collapsed by default if all children are
+ unimportant or if the header is unimportant. -->
+<!-- <xsl:variable name="collapsed"
+ select="count(.//line[not(@priority = 3)]) = 0 or ./head[@priority = 3]" /> -->
+ <xsl:variable name="collapsed" select="count(.//*[@error]) = 0"/>
+
+ <xsl:variable name="style"><xsl:if test="$collapsed">display: none;</xsl:if></xsl:variable>
+ <xsl:variable name="arg"><xsl:choose><xsl:when test="$collapsed">true</xsl:when><xsl:otherwise>false</xsl:otherwise></xsl:choose></xsl:variable>
+
+ <script type='text/javascript'>showTreeToggle(<xsl:value-of select="$collapsed"/>)</script>
+ <xsl:apply-templates select='head'/>
+
+ <ul class='nesting' style="{$style}">
+ <xsl:for-each select='line|nest'>
+
+ <!-- Is this the last line? If so, mark it as such so that it
+ can be rendered differently. -->
+ <xsl:variable name="class"><xsl:choose><xsl:when test="position() != last()">line</xsl:when><xsl:otherwise>lastline</xsl:otherwise></xsl:choose></xsl:variable>
+
+ <li class='{$class}'>
+ <span class='lineconn' />
+ <span class='linebody'>
+ <xsl:apply-templates select='.'/>
+ </span>
+ </li>
+ </xsl:for-each>
+ </ul>
+ </xsl:template>
+
+
+ <xsl:template match="head|line">
+ <code>
+ <xsl:if test="@error">
+ <xsl:attribute name="class">error</xsl:attribute>
+ </xsl:if>
+ <xsl:apply-templates/>
+ </code>
+ </xsl:template>
+
+
+ <xsl:template match="storeref">
+ <em class='storeref'>
+ <span class='popup'><xsl:apply-templates/></span>
+ <span class='elided'>/...</span><xsl:apply-templates select='name'/><xsl:apply-templates select='path'/>
+ </em>
+ </xsl:template>
+
+</xsl:stylesheet> \ No newline at end of file
diff --git a/src/nix-log2xml/log2xml.cc b/src/nix-log2xml/log2xml.cc
new file mode 100644
index 000000000..c30fa5b02
--- /dev/null
+++ b/src/nix-log2xml/log2xml.cc
@@ -0,0 +1,172 @@
+#include <vector>
+#include <iostream>
+#include <cstdio>
+#include <string>
+
+using namespace std;
+
+
+struct Decoder
+{
+ enum { stTop, stEscape, stCSI } state;
+ string line;
+ bool inHeader;
+ int level;
+ vector<int> args;
+ bool newNumber;
+ int priority;
+ bool ignoreLF;
+ int lineNo, charNo;
+
+ Decoder()
+ {
+ state = stTop;
+ line = "";
+ inHeader = false;
+ level = 0;
+ priority = 1;
+ ignoreLF = false;
+ lineNo = 1;
+ charNo = 0;
+ }
+
+ void pushChar(char c);
+
+ void finishLine();
+};
+
+
+void Decoder::pushChar(char c)
+{
+ if (c == '\n') {
+ lineNo++;
+ charNo = 0;
+ } else charNo++;
+
+ switch (state) {
+
+ case stTop:
+ if (c == '\e') {
+ state = stEscape;
+ } else if (c == '\n' && !ignoreLF) {
+ finishLine();
+ } else line += c;
+ break;
+
+ case stEscape:
+ if (c == '[') {
+ state = stCSI;
+ args.clear();
+ newNumber = true;
+ } 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;
+ priority = args.size() >= 1 ? args[0] : 1;
+ break;
+ case 'q':
+ if (line.size()) finishLine();
+ if (level > 0) {
+ level--;
+ cout << "</nest>" << endl;
+ } else
+ cerr << "not enough nesting levels at line "
+ << lineNo << ", character " << charNo << endl;
+ break;
+ case 's':
+ if (line.size()) finishLine();
+ priority = args.size() >= 1 ? args[0] : 1;
+ break;
+ case 'a':
+ ignoreLF = true;
+ break;
+ case 'b':
+ ignoreLF = false;
+ break;
+ }
+ } else if (c >= '0' && c <= '9') {
+ int n = 0;
+ if (!newNumber) {
+ n = args.back() * 10;
+ args.pop_back();
+ }
+ n += c - '0';
+ args.push_back(n);
+ }
+ break;
+
+ }
+}
+
+
+void Decoder::finishLine()
+{
+ string storeDir = "/nix/store/";
+ int sz = storeDir.size();
+ string tag = inHeader ? "head" : "line";
+ cout << "<" << tag;
+ if (priority != 1) cout << " priority='" << priority << "'";
+ cout << ">";
+
+ for (unsigned int i = 0; i < line.size(); i++) {
+
+ if (line[i] == '<') cout << "&lt;";
+ else if (line[i] == '&') cout << "&amp;";
+ else if (line[i] < 32 && line[i] != 9) cout << "&#xfffd;";
+ else if (i + sz + 33 < line.size() &&
+ string(line, i, sz) == storeDir &&
+ line[i + sz + 32] == '-')
+ {
+ int j = i + sz + 32;
+ /* skip name */
+ while (!strchr("/\n\r\t ()[]:;?<>", line[j])) j++;
+ int k = j;
+ while (!strchr("\n\r\t ()[]:;?<>", line[k])) k++;
+ // !!! escaping
+ cout << "<storeref>"
+ << "<storedir>"
+ << string(line, i, sz)
+ << "</storedir>"
+ << "<hash>"
+ << string(line, i + sz, 32)
+ << "</hash>"
+ << "<name>"
+ << string(line, i + sz + 32, j - (i + sz + 32))
+ << "</name>"
+ << "<path>"
+ << string(line, j, k - j)
+ << "</path>"
+ << "</storeref>";
+ i = k - 1;
+ } else cout << line[i];
+ }
+
+ cout << "</" << tag << ">" << endl;
+ line = "";
+ inHeader = false;
+ priority = 1;
+}
+
+
+int main(int argc, char * * argv)
+{
+ Decoder dec;
+ int c;
+
+ cout << "<logfile>" << endl;
+
+ while ((c = getchar()) != EOF) {
+ dec.pushChar(c);
+ }
+
+ cout << "</logfile>" << endl;
+}
diff --git a/src/nix-log2xml/logfile.css b/src/nix-log2xml/logfile.css
new file mode 100644
index 000000000..ed390d64a
--- /dev/null
+++ b/src/nix-log2xml/logfile.css
@@ -0,0 +1,86 @@
+body {
+ font-family: sans-serif;
+ background: white;
+}
+
+
+ul.nesting, ul.toplevel {
+ padding: 0;
+ margin: 0;
+}
+
+ul.toplevel {
+ list-style-type: none;
+}
+
+ul.nesting li.line, ul.nesting li.lastline {
+ position: relative;
+ list-style-type: none;
+}
+
+ul.nesting li.line {
+ padding-left: 1.1em;
+}
+
+ul.nesting li.lastline {
+ padding-left: 1.2em; // for the 0.1em border-left in .lastline > .lineconn
+}
+
+li.line {
+ border-left: 0.1em solid #6185a0;
+}
+
+li.line > span.lineconn, li.lastline > span.lineconn {
+ position: absolute;
+ height: 0.65em;
+ left: 0em;
+ width: 1em;
+ border-bottom: 0.1em solid #6185a0;
+}
+
+li.lastline > span.lineconn {
+ border-left: 0.1em solid #6185a0;
+}
+
+
+em.storeref {
+ color: #500000;
+ position: relative;
+ width: 100%;
+}
+
+em.storeref:hover {
+ background-color: #eeeeee;
+}
+
+*.popup {
+ display: none;
+/* background: url('http://losser.st-lab.cs.uu.nl/~mbravenb/menuback.png') repeat; */
+ background: #ffffcd;
+ border: solid #555555 1px;
+ position: absolute;
+ top: 0em;
+ left: 0em;
+ margin: 0;
+ padding: 0;
+ z-index: 100;
+}
+
+em.storeref:hover span.popup {
+ display: inline;
+}
+
+
+.toggle {
+ text-decoration: none;
+}
+
+.showTree, .hideTree {
+ font-family: monospace;
+ font-size: larger;
+}
+
+.error {
+ color: #ff0000;
+ font-weight: bold;
+} \ No newline at end of file
diff --git a/src/nix-log2xml/mark-errors.xsl b/src/nix-log2xml/mark-errors.xsl
new file mode 100644
index 000000000..4e91913e5
--- /dev/null
+++ b/src/nix-log2xml/mark-errors.xsl
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="line">
+ <line>
+ <xsl:if test="contains(text(), ' *** ') or
+ contains(text(), 'LaTeX Error') or
+ contains(text(), 'FAIL:') or
+ contains(text(), ' error: ') or
+ true">
+ <xsl:attribute name="error"></xsl:attribute>
+ </xsl:if>
+ <xsl:apply-templates select="@*|node()"/>
+ </line>
+ </xsl:template>
+
+</xsl:stylesheet> \ No newline at end of file
diff --git a/src/nix-log2xml/treebits.js b/src/nix-log2xml/treebits.js
new file mode 100644
index 000000000..3011b391d
--- /dev/null
+++ b/src/nix-log2xml/treebits.js
@@ -0,0 +1,50 @@
+/* Acknowledgement: this is based on the Wikipedia table-of-contents
+ * toggle. */
+
+
+var idCounter = 0;
+
+
+function showTreeToggle(isHidden) {
+ if (document.getElementById) {
+ var id = "toggle_" + idCounter;
+ document.writeln(
+ '<a href="javascript:toggleTree(\'' + id + '\')" class="toggle" id="' + id + '">' +
+ '<span class="showTree" ' + (isHidden ? '' : 'style="display: none;"') + '>+</span>' +
+ '<span class="hideTree" ' + (isHidden ? 'style="display: none;"' : '') + '>-</span>' +
+ '</a>');
+ idCounter = idCounter + 1;
+ }
+}
+
+
+function toggleTree(id) {
+
+ var href = document.getElementById(id);
+
+ var node = href;
+ var tree = null;
+ while (node != null) {
+ if (node.className == "nesting") tree = node;
+ node = node.nextSibling;
+ }
+
+ node = href.firstChild;
+ var hideTree = null;
+ var showTree = null;
+ while (node != null) {
+ if (node.className == "showTree") showTree = node;
+ else if (node.className == "hideTree") hideTree = node;
+ node = node.nextSibling;
+ }
+
+ if (tree.style.display == 'none') {
+ tree.style.display = '';
+ hideTree.style.display = '';
+ showTree.style.display = 'none';
+ } else {
+ tree.style.display = 'none';
+ hideTree.style.display = 'none';
+ showTree.style.display = '';
+ }
+}