about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEelco Dolstra <e.dolstra@tudelft.nl>2004-03-15T21·51+0000
committerEelco Dolstra <e.dolstra@tudelft.nl>2004-03-15T21·51+0000
commit9d2669d218d03d64c69a702a96fc87ee1fd3a9d0 (patch)
tree3543711b7082d29a68922c0e3ea71c8b14507f5d
parentbeda10f5a2a69ac32ad91c8a80477fde19be6a83 (diff)
* 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).
  

-rw-r--r--configure.ac1
-rw-r--r--src/Makefile.am2
-rw-r--r--src/log2xml/Makefile.am9
-rw-r--r--src/log2xml/log2html.xsl61
-rw-r--r--src/log2xml/log2xml.cc102
-rw-r--r--src/log2xml/logfile.css66
6 files changed, 240 insertions, 1 deletions
diff --git a/configure.ac b/configure.ac
index 093812e7acc0..f07dd6dd7b82 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/src/Makefile.am b/src/Makefile.am
index 0f1273426e32..29bd535f6ee0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -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
diff --git a/src/log2xml/Makefile.am b/src/log2xml/Makefile.am
new file mode 100644
index 000000000000..b439f5e10d77
--- /dev/null
+++ b/src/log2xml/Makefile.am
@@ -0,0 +1,9 @@
+bin_PROGRAMS = log2xml
+
+log2xml_SOURCES = log2xml.cc
+
+%.xml: %.log log2xml
+	./log2xml < $< > $@
+
+%.html: %.xml log2html.xsl
+	xsltproc log2html.xsl $< > $@
\ No newline at end of file
diff --git a/src/log2xml/log2html.xsl b/src/log2xml/log2html.xsl
new file mode 100644
index 000000000000..96691a6aefd7
--- /dev/null
+++ b/src/log2xml/log2html.xsl
@@ -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>
\ No newline at end of file
diff --git a/src/log2xml/log2xml.cc b/src/log2xml/log2xml.cc
new file mode 100644
index 000000000000..711fc82b89cf
--- /dev/null
+++ b/src/log2xml/log2xml.cc
@@ -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 += "&lt;";
+            else if (c == '&')
+                line += "&amp;";
+            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;
+}
diff --git a/src/log2xml/logfile.css b/src/log2xml/logfile.css
new file mode 100644
index 000000000000..e240eb381520
--- /dev/null
+++ b/src/log2xml/logfile.css
@@ -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;
+}