about summary refs log tree commit diff
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 000000000000..4b6c76c24355
--- /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 000000000000..0718df230f26
--- /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 000000000000..c30fa5b02667
--- /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 000000000000..ed390d64a9ef
--- /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 000000000000..4e91913e570a
--- /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 000000000000..3011b391d88b
--- /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 = '';
+    }
+}