Bootstrap Blues

Bootstrapping a compiler can be a finicky process, because many compilers are written in the language that they compile. It's easy to paint yourself into a corner if you're not extremely vigilant about binaries and configuration management.

I wanted to try SMLserver, a system for writing database-backed web services using Standard ML. It is tightly integrated with the MLkit compiler.

Unfortunately, the distributed binaries would not run on my installation of Debian stable (codename ‘etch’) because they expect a newer version of ‘libc’, the main system library. Upgrading that library could be pretty disruptive, which defeats the purpose of running a ‘stable’ distribution.

Unlike many compiler code bases, MLkit can be built by compilers other than itself, namely SML/NJ and MLton. Ordinarily, this would simplify the process, except that the SML/NJ version it requires is ancient (and doesn't itself compile out of the box anymore on this system) and MLton has extreme memory demands.

Fortunately, an older binary of MLkit (4.3.0) runs on this system, but at best that's a starting point. Once a particular revision can bootstrap itself, it's natural for the source language to evolve beyond what the previous revision could handle. But in this case, the changes were small. Version 4.3.0 lacks some pieces of the ‘Posix.FileSys’ module that 4.3.2 needs, but they were small enough to rewrite:

Index: src/Tools/MlbMake/MlbFileSys.sml
--- src/Tools/MlbMake/MlbFileSys.sml	(revision 2311)
+++ src/Tools/MlbMake/MlbFileSys.sml	(working copy)
@@ -49,9 +49,9 @@
 	  | EQUAL => (b,d)
     fun unique link f =
-	let val s = if link then Posix.FileSys.lstat f else Posix.FileSys.stat f
-	in (Posix.FileSys.inoToWord(Posix.FileSys.ST.ino s),
-	    Posix.FileSys.devToWord( s))
-	end
+        let val {dev,ino} = OS.FileSys.fileId f
+         in (Word.fromInt ino,
+             Word.fromInt dev)
+        end
Index: src/Manager/Manager.sml
--- src/Manager/Manager.sml	(revision 2311)
+++ src/Manager/Manager.sml	(working copy)
@@ -807,7 +807,6 @@
            val fu = (Posix.IO.close (Posix.FileSys.creat (lockfile ^ unique,Posix.FileSys.S.iwusr)) ; true) handle OS.SysErr _ => false
            val f = if fu
                    then ({old=lockfile ^ unique, new=lockfile}; true)
-                        handle OS.SysErr _ => Posix.FileSys.ST.nlink (Posix.FileSys.stat (lockfile ^ unique)) = 2
                    else false
          in if fu then (Posix.FileSys.unlink (lockfile ^ unique); f) handle _ => f
             else false
Version 4.3.0 can build 4.3.2 patched thusly, which can then bootstrap its unmodified self. Really, I lucked out here. Imagine having to do this across a major compiler release cycle!

©20022015 Christopher League