1quietdec := true;
2app load ["Mosml", "Process", "Path", "FileSys", "Timer", "Real", "Int",
3          "Bool", "OS", "CommandLine"] ;
4open Mosml;
5
6fun die s = (TextIO.output(TextIO.stdErr, s ^ "\n");
7             OS.Process.exit OS.Process.failure)
8
9(* Thanks to Ken Friis Larsen for this very cute trick *)
10val version_string =
11    List.nth([], 1) handle Option => "2.01" | Subscript => "2.10";
12
13val _ = if version_string = "2.01" then let
14            val _ = print "\n\nUsing Basis 2002 update for Moscow ML 2.01\n"
15            val _ = app load ["CharVector", "Math", "ListPair"]
16            infix ++ val op++ = OS.Path.concat
17          in
18            use ("tools" ++ "Holmake" ++ "basis2002.sml")
19          end
20        else ();
21
22structure FileSys = OS.FileSys
23structure Process = OS.Process
24structure Path = OS.Path
25
26(* utility functions *)
27fun readdir s = let
28  val ds = FileSys.openDir s
29  fun recurse acc =
30      case FileSys.readDir ds of
31        NONE => acc
32      | SOME s => recurse (s::acc)
33in
34  recurse [] before FileSys.closeDir ds
35end;
36
37fun mem x [] = false
38  | mem x (h::t) = x = h orelse mem x t
39
40fun frontlast [] = raise Fail "frontlast: failure"
41  | frontlast [h] = ([], h)
42  | frontlast (h::t) = let val (f,l) = frontlast t in (h::f, l) end;
43
44(* returns a function of type unit -> int, which returns time elapsed in
45   seconds since the call to start_timer() *)
46fun start_timer() = let
47  val timer = Timer.startRealTimer()
48in
49  fn () => let
50       val time_now = Timer.checkRealTimer timer
51     in
52       Real.floor (Time.toReal time_now)
53     end handle Time.Time => 0
54end
55
56(* busy loop sleeping *)
57fun delay limit action = let
58  val timer = start_timer()
59  fun loop last = let
60    val elapsed = timer()
61  in
62    if elapsed = last then loop last
63    else (action elapsed; if elapsed >= limit then () else loop elapsed)
64  end
65in
66  action 0; loop 0
67end;
68
69fun determining s =
70    (print (s^" "); delay 1 (fn _ => ()));
71
72(* action starts here *)
73print "\nHOL smart configuration.\n\n";
74
75print "Determining configuration parameters: ";
76determining "OS";
77
78val currentdir = FileSys.getDir()
79
80val OS = let
81  val {vol,...} = Path.fromString currentdir
82in
83  if vol = "" then (* i.e. Unix *)
84    case Mosml.run "uname" ["-a"] "" of
85      Success s => if String.isPrefix "Linux" s then
86                     "linux"
87                   else if String.isPrefix "SunOS" s then
88                     "solaris"
89                   else if String.isPrefix "Darwin" s then
90                     "macosx"
91                   else
92                     "unix"
93    | Failure s => (print "\nRunning uname failed with message: ";
94                    print s;
95                    Process.exit Process.failure)
96  else "winNT"
97end;
98
99fun findpartial f [] = NONE
100  | findpartial f (h::t) =
101    case f h of NONE => findpartial f t | x => x
102
103fun which arg =
104  let
105    open OS.FileSys
106    val sepc = if OS = "winNT" then #";" else #":"
107    fun check p =
108      let
109        val fname = OS.Path.concat(p, arg)
110      in
111        if access (fname, [A_READ, A_EXEC]) then
112          SOME
113            (OS.Path.mkAbsolute{path = fname, relativeTo = OS.FileSys.getDir()})
114        else NONE
115      end
116  in
117    case OS.Process.getEnv "PATH" of
118        SOME path =>
119        let
120          val paths = (if OS = "winNT" then ["."] else []) @
121                      String.fields (fn c => c = sepc) path
122        in
123          findpartial check paths
124        end
125      | NONE => if OS = "winNT" then check "." else NONE
126  end
127
128val exe_ext = if OS = "winNT" then ".exe" else "";
129determining "mosmldir";
130
131fun check_mosml candidate = let
132  open FileSys
133in
134  access(Path.concat(candidate,"mosml"^exe_ext), [A_EXEC])
135end
136
137fun mosml_from_loadpath () = let
138  val libdir = hd (!Meta.loadPath)
139  val () = print ("\nMosml library directory (from loadPath) is "^libdir)
140  val {arcs, isAbs, vol} = Path.fromString libdir
141  val _ = isAbs orelse
142          (print "\n\n*** ML library directory not specified with absolute";
143           print "filename --- aborting\n";
144           Process.exit Process.failure)
145  val arcs = case frontlast arcs of
146                 (arcs,  "lib") => arcs
147               | ([],  ult)     => [ult]
148               | (arcs,"mosml") => (* default since 2.10 *)
149                 let val (arcs', pen) = frontlast arcs in
150                     (if pen = "lib" then arcs' else arcs)
151                 end
152               | (arcs, _)      =>
153                 (print "\nMosml library directory (from loadPath) not .../lib -- weird!\n";
154                  arcs)
155  val candidate =
156      Path.toString {arcs = arcs @ ["bin"], isAbs = true, vol = vol}
157  val _ = print ("\nUsing "^candidate^" for mosml directory (from loadPath)\n")
158in
159  if check_mosml candidate then candidate
160  else (print "\nCan't find mosml -- hope you have it in a \
161              \config-override file\n";
162        "")
163end;
164
165fun dirify {arcs,isAbs,vol} =
166    OS.Path.toString {arcs = #1 (frontlast arcs), isAbs = isAbs, vol = vol}
167
168
169val mosmldir = let
170  val nm = CommandLine.name()
171  val p as {arcs, isAbs, vol} = OS.Path.fromString nm
172  val cand =
173      if isAbs then SOME (dirify p)
174      else if length arcs > 1 then
175        SOME (Path.mkAbsolute {path = dirify p,
176                               relativeTo = OS.FileSys.getDir()})
177      else (* examine PATH variable *)
178        case OS.Process.getEnv "PATH" of
179          NONE => NONE
180        | SOME elist => let
181            val sep = case OS of "winNT" => #";" | _ => #":"
182            val search_these = String.fields (fn c => c = sep) elist
183          in
184            List.find check_mosml search_these
185          end
186in
187  case cand of
188    NONE => mosml_from_loadpath ()
189  | SOME c => if check_mosml c then c else mosml_from_loadpath ()
190end;
191
192determining "holdir";
193
194val holdir = let
195  val cdir_files = readdir currentdir
196in
197  if mem "sigobj" cdir_files andalso mem "std.prelude" cdir_files then
198    currentdir
199  else if mem "smart-configure.sml" cdir_files andalso
200          mem "configure.sml" cdir_files
201  then let
202      val {arcs, isAbs, vol} = Path.fromString currentdir
203      val (arcs', _) = frontlast arcs
204    in
205      Path.toString {arcs = arcs', isAbs = isAbs, vol = vol}
206    end
207  else (print "\n\n*** Couldn't determine holdir; ";
208        print "please run me from the root HOL directory\n";
209        Process.exit Process.failure)
210end;
211
212determining "dynlib_available";
213
214val dynlib_available = (load "Dynlib"; true) handle _ => false;
215
216print "\n";
217
218val DOT_PATH = SOME "";
219
220val _ = let
221  val override = Path.concat(holdir, "config-override")
222in
223  if FileSys.access (override, [FileSys.A_READ]) then
224    (print "\n[Using override file!]\n\n";
225     use override)
226  else ()
227end;
228
229val DOT_PATH = if DOT_PATH = SOME "" then which "dot" else DOT_PATH;
230
231fun verdict (prompt, value) =
232    (print (StringCvt.padRight #" " 20 (prompt^":"));
233     print value;
234     print "\n");
235
236fun optverdict (prompt, optvalue) =
237  (print (StringCvt.padRight #" " 20 (prompt ^ ":"));
238   print (case optvalue of NONE => "NONE" | SOME p => "SOME "^p);
239   print "\n");
240
241verdict ("OS", OS);
242verdict ("mosmldir", mosmldir);
243verdict ("holdir", holdir);
244verdict ("dynlib_available", Bool.toString dynlib_available);
245optverdict ("DOT_PATH", DOT_PATH);
246
247val _ = let
248  val mosml' = if OS = "winNT" then "mosmlc.exe" else "mosmlc"
249in
250  if FileSys.access (Path.concat(mosmldir, mosml'), [FileSys.A_EXEC]) then
251    ()
252  else (print ("\nCouldn't find executable mosmlc in "^mosmldir^"\n");
253        print ("Giving up - please use config-override file to fix\n");
254        Process.exit Process.failure)
255end;
256
257print "\nConfiguration will begin with above values.  If they are wrong\n";
258print "press Control-C.\n\n";
259
260delay 3
261      (fn n => print ("\rWill continue in "^Int.toString (3 - n)^" seconds."))
262      handle Interrupt => (print "\n"; Process.exit Process.failure);
263
264print "\n";
265
266val configfile = Path.concat (Path.concat (holdir, "tools"), "configure.sml");
267
268
269use configfile;
270