From 1f90d94331784c4186933aed53a443361e4cd4d2 Mon Sep 17 00:00:00 2001
From: Eelco Dolstra <e.dolstra@tudelft.nl>
Date: Tue, 18 Nov 2008 16:45:23 +0000
Subject: [PATCH] * Pretty-print the logs.

---
 .../lib/HydraFrontend/Controller/Root.pm      | 16 ++--
 src/HydraFrontend/root/layout.tt              |  4 +-
 src/HydraFrontend/root/log.tt                 |  4 +-
 .../root/{ => static/css}/hydra.css           |  5 ++
 src/HydraFrontend/root/static/css/logfile.css | 84 +++++++++++++++++++
 src/HydraFrontend/root/static/js/treebits.js  | 50 +++++++++++
 src/HydraFrontend/xsl/log2html.xsl            | 74 ++++++++++++++++
 src/HydraFrontend/xsl/mark-errors.xsl         | 24 ++++++
 8 files changed, 250 insertions(+), 11 deletions(-)
 rename src/HydraFrontend/root/{ => static/css}/hydra.css (98%)
 create mode 100644 src/HydraFrontend/root/static/css/logfile.css
 create mode 100644 src/HydraFrontend/root/static/js/treebits.js
 create mode 100644 src/HydraFrontend/xsl/log2html.xsl
 create mode 100644 src/HydraFrontend/xsl/mark-errors.xsl

diff --git a/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm b/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm
index 05b8ce9b..bb668242 100644
--- a/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm
+++ b/src/HydraFrontend/lib/HydraFrontend/Controller/Root.pm
@@ -340,12 +340,12 @@ sub nixlog :Local {
 
 sub loadLog {
     my ($path) = @_;
-    # !!! all a quick hack
-    if ($path =~ /.bz2$/) {
-        return `cat $path | bzip2 -d`;
-    } else {
-        return `cat $path`;
-    }
+
+    # !!! quick hack
+    my $pipeline = ($path =~ /.bz2$/ ? "cat $path | bzip2 -d" : "cat $path")
+        . " | nix-log2xml | xsltproc xsl/mark-errors.xsl - | xsltproc xsl/log2html.xsl - | tail -n +2";
+
+    return `$pipeline`;
 }
 
 
@@ -400,8 +400,8 @@ sub closure :Local {
     $c->stash->{storePath} = $product->path;
     $c->stash->{name} = $build->nixname;
 }
-    
-    
+
+
 sub end : ActionClass('RenderView') {}
 
 
diff --git a/src/HydraFrontend/root/layout.tt b/src/HydraFrontend/root/layout.tt
index 90f3e57e..ff2bac99 100644
--- a/src/HydraFrontend/root/layout.tt
+++ b/src/HydraFrontend/root/layout.tt
@@ -14,9 +14,11 @@
   
   <head>
     <title>[% title %]</title>
-    <link rel="stylesheet" href="/hydra.css" type="text/css" />
+    <link rel="stylesheet" href="/static/css/hydra.css" type="text/css" />
+    <link rel="stylesheet" href="/static/css/logfile.css" type="text/css" />
     <script type="text/javascript" src="/static/js/jquery-pack.js"></script> 
     <script type="text/javascript" src="/static/js/tablesorter/jquery.tablesorter.js"></script>
+    <script type="text/javascript" src="/static/js/treebits.js"></script>
     <script type="text/javascript">
       $(document).ready(function() {
         $("table.tablesorter").tablesorter();
diff --git a/src/HydraFrontend/root/log.tt b/src/HydraFrontend/root/log.tt
index 72702aa8..0874742f 100644
--- a/src/HydraFrontend/root/log.tt
+++ b/src/HydraFrontend/root/log.tt
@@ -3,8 +3,8 @@
 <h1>Build log [% IF step %] of step [% step.stepnr %] [% ELSE %]<tt>[% log.logphase %]</tt>[% END %] of build ID [% id %]</h1>
 
 <!-- !!! escaping -->
-<pre class="buildlog">
+<div class="buildlog">
 [% logtext -%]
-</pre>
+</div>
 
 [% END %]
diff --git a/src/HydraFrontend/root/hydra.css b/src/HydraFrontend/root/static/css/hydra.css
similarity index 98%
rename from src/HydraFrontend/root/hydra.css
rename to src/HydraFrontend/root/static/css/hydra.css
index 6ba2976f..ce7925c2 100644
--- a/src/HydraFrontend/root/hydra.css
+++ b/src/HydraFrontend/root/static/css/hydra.css
@@ -159,6 +159,11 @@ pre.buildlog {
     white-space: pre-wrap;
 }
 
+div.buildlog {
+    border: 1px solid black;
+    padding: 0.3em;
+}
+
 ul.productList {
     list-style: none;
     padding-left: 1em;
diff --git a/src/HydraFrontend/root/static/css/logfile.css b/src/HydraFrontend/root/static/css/logfile.css
new file mode 100644
index 00000000..b4709048
--- /dev/null
+++ b/src/HydraFrontend/root/static/css/logfile.css
@@ -0,0 +1,84 @@
+ul.nesting, ul.toplevel {
+    padding: 0;
+    margin: 0;
+}
+
+ul.toplevel {
+    list-style-type: none;
+}
+
+.line, .head {
+    padding-top: 0em;
+}
+
+ul.nesting li.line, ul.nesting li.lastline {
+    position: relative;
+    list-style-type: none;
+}
+
+ul.nesting li.line {
+    padding-left: 2.0em;
+}
+
+ul.nesting li.lastline {
+    padding-left: 2.1em; // 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: 1.5em;
+    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/HydraFrontend/root/static/js/treebits.js b/src/HydraFrontend/root/static/js/treebits.js
new file mode 100644
index 00000000..3011b391
--- /dev/null
+++ b/src/HydraFrontend/root/static/js/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 = '';
+    }
+}
diff --git a/src/HydraFrontend/xsl/log2html.xsl b/src/HydraFrontend/xsl/log2html.xsl
new file mode 100644
index 00000000..78532882
--- /dev/null
+++ b/src/HydraFrontend/xsl/log2html.xsl
@@ -0,0 +1,74 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+  <xsl:output method='html' encoding="UTF-8"
+              doctype-public="-//W3C//DTD HTML 4.01//EN"
+              doctype-system="http://www.w3.org/TR/html4/strict.dtd" />
+
+  <xsl:template match="logfile">
+    <ul class='toplevel'>
+      <xsl:for-each select='line|nest'>
+        <li>
+          <xsl:apply-templates select='.'/>
+        </li>
+      </xsl:for-each>
+    </ul>
+  </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'/>
+
+    <!-- Be careful to only generate <ul>s if there are <li>s, otherwise it’s malformed. -->
+    <xsl:if test="line|nest">
+      
+      <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:if>
+    
+  </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/HydraFrontend/xsl/mark-errors.xsl b/src/HydraFrontend/xsl/mark-errors.xsl
new file mode 100644
index 00000000..4e91913e
--- /dev/null
+++ b/src/HydraFrontend/xsl/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