# HG changeset patch # User Adam Chlipala # Date 1335730983 14400 # Node ID ab24a7cb2a640177cf3cc0c198b56dd4e4a6b840 # Parent 4a03aa3251cbfa691787c16edf84ac884fb0804d 'urweb daemon start' and 'urweb daemon stop' diff -r 4a03aa3251cb -r ab24a7cb2a64 Makefile.am --- a/Makefile.am Sun Apr 29 13:17:31 2012 -0400 +++ b/Makefile.am Sun Apr 29 16:23:03 2012 -0400 @@ -75,7 +75,7 @@ install-exec-local-main: mkdir -p $(DESTDIR)$(BIN) - cp bin/urweb $(DESTDIR)$(BIN)/ + install bin/urweb $(DESTDIR)$(BIN)/ mkdir -p $(DESTDIR)$(LIB_UR) cp lib/ur/*.urs $(DESTDIR)$(LIB_UR)/ cp lib/ur/*.ur $(DESTDIR)$(LIB_UR)/ diff -r 4a03aa3251cb -r ab24a7cb2a64 Makefile.in --- a/Makefile.in Sun Apr 29 13:17:31 2012 -0400 +++ b/Makefile.in Sun Apr 29 16:23:03 2012 -0400 @@ -822,7 +822,7 @@ install-exec-local-main: mkdir -p $(DESTDIR)$(BIN) - cp bin/urweb $(DESTDIR)$(BIN)/ + install bin/urweb $(DESTDIR)$(BIN)/ mkdir -p $(DESTDIR)$(LIB_UR) cp lib/ur/*.urs $(DESTDIR)$(LIB_UR)/ cp lib/ur/*.ur $(DESTDIR)$(LIB_UR)/ diff -r 4a03aa3251cb -r ab24a7cb2a64 doc/manual.tex --- a/doc/manual.tex Sun Apr 29 13:17:31 2012 -0400 +++ b/doc/manual.tex Sun Apr 29 16:23:03 2012 -0400 @@ -211,6 +211,18 @@ \end{verbatim} The first output line is a list of categories of CSS properties that would be worth setting on the document body. The remaining lines are space-separated pairs of CSS class names and categories of properties that would be worth setting for that class. The category codes are divided into two varieties. Codes that reveal properties of a tag or its (recursive) children are \cd{B} for block-level elements, \cd{C} for table captions, \cd{D} for table cells, \cd{L} for lists, and \cd{T} for tables. Codes that reveal properties of the precise tag that uses a class are \cd{b} for block-level elements, \cd{t} for tables, \cd{d} for table cells, \cd{-} for table rows, \cd{H} for the possibility to set a height, \cd{N} for non-replaced inline-level elements, \cd{R} for replaced inline elements, and \cd{W} for the possibility to set a width. +Ur/Web type inference can take a significant amount of time, so it can be helpful to cache type-inferred versions of source files. This mode can be activated by running +\begin{verbatim} +urweb daemon start +\end{verbatim} +Further \cd{urweb} invocations in the same working directory will send requests to a background daemon process that reuses type inference results whenever possible, tracking source file dependencies and modification times. To stop the background daemon, run +\begin{verbatim} +urweb daemon stop +\end{verbatim} +Communication happens via a UNIX domain socket in file \cd{.urweb\_daemon} in the working directory. + +\medskip + Some other command-line parameters are accepted: \begin{itemize} \item \texttt{-db }: Set database connection information, using the format expected by Postgres's \texttt{PQconnectdb()}, which is \texttt{name1=value1 ... nameN=valueN}. The same format is also parsed and used to discover connection parameters for MySQL and SQLite. The only significant settings for MySQL are \texttt{host}, \texttt{hostaddr}, \texttt{port}, \texttt{dbname}, \texttt{user}, and \texttt{password}. The only significant setting for SQLite is \texttt{dbname}, which is interpreted as the filesystem path to the database. Additionally, when using SQLite, a database string may be just a file path. diff -r 4a03aa3251cb -r ab24a7cb2a64 src/compiler.sml --- a/src/compiler.sml Sun Apr 29 13:17:31 2012 -0400 +++ b/src/compiler.sml Sun Apr 29 16:23:03 2012 -0400 @@ -917,7 +917,7 @@ val sgn = (Source.SgnConst (#func parseUrs urs), loc) in checkErrors (); - (Source.DFfiStr (mname, sgn, OS.FileSys.modTime urs), loc) + (Source.DFfiStr (mname, sgn, if !Elaborate.incremental then SOME (OS.FileSys.modTime urs) else NONE), loc) end val defed = ref SS.empty @@ -944,7 +944,8 @@ last = ErrorMsg.dummyPos} val ds = #func parseUr ur - val d = (Source.DStr (mname, sgnO, SOME (OS.FileSys.modTime ur), (Source.StrConst ds, loc)), loc) + val d = (Source.DStr (mname, sgnO, if !Elaborate.incremental then SOME (OS.FileSys.modTime ur) else NONE, + (Source.StrConst ds, loc)), loc) val fname = OS.Path.mkCanonical fname val d = case List.find (fn (root, name) => diff -r 4a03aa3251cb -r ab24a7cb2a64 src/elaborate.sig --- a/src/elaborate.sig Sun Apr 29 13:17:31 2012 -0400 +++ b/src/elaborate.sig Sun Apr 29 16:23:03 2012 -0400 @@ -41,4 +41,6 @@ (* Run all phases of type inference, even if an error is detected by an * early phase. *) + val incremental : bool ref + end diff -r 4a03aa3251cb -r ab24a7cb2a64 src/elaborate.sml --- a/src/elaborate.sml Sun Apr 29 13:17:31 2012 -0400 +++ b/src/elaborate.sml Sun Apr 29 16:23:03 2012 -0400 @@ -40,6 +40,7 @@ val dumpTypes = ref false val unifyMore = ref false + val incremental = ref false structure IS = IntBinarySet structure IM = IntBinaryMap @@ -3977,7 +3978,7 @@ ([dNew], (env', denv', gs' @ gs)) end) - | L.DFfiStr (x, sgn, tm) => + | L.DFfiStr (x, sgn, tmo) => (case ModDb.lookup dAll of SOME d => let @@ -3994,7 +3995,7 @@ val dNew = (L'.DFfiStr (x, n, sgn'), loc) in - ModDb.insert (dNew, tm); + Option.map (fn tm => ModDb.insert (dNew, tm)) tmo; ([dNew], (env', denv, enD gs' @ gs)) end) @@ -4461,9 +4462,9 @@ val () = delayedUnifs := [] val () = delayedExhaustives := [] - val d = (L.DFfiStr ("Basis", (L.SgnConst basis, ErrorMsg.dummySpan), basis_tm), ErrorMsg.dummySpan) + val d = (L.DFfiStr ("Basis", (L.SgnConst basis, ErrorMsg.dummySpan), SOME basis_tm), ErrorMsg.dummySpan) val (basis_n, env', sgn) = - case ModDb.lookup d of + case (if !incremental then ModDb.lookup d else NONE) of NONE => let val (sgn, gs) = elabSgn (env, D.empty) (L.SgnConst basis, ErrorMsg.dummySpan) @@ -4503,7 +4504,7 @@ SOME (if Time.< (top_tm, basis_tm) then basis_tm else top_tm), (L.StrConst topStr, ErrorMsg.dummySpan)), ErrorMsg.dummySpan) val (top_n, env', topSgn, topStr) = - case ModDb.lookup d of + case (if !incremental then ModDb.lookup d else NONE) of NONE => let val (topSgn, gs) = elabSgn (env', D.empty) (L.SgnConst topSgn, ErrorMsg.dummySpan) diff -r 4a03aa3251cb -r ab24a7cb2a64 src/main.mlton.sml --- a/src/main.mlton.sml Sun Apr 29 13:17:31 2012 -0400 +++ b/src/main.mlton.sml Sun Apr 29 16:23:03 2012 -0400 @@ -25,147 +25,271 @@ * POSSIBILITY OF SUCH DAMAGE. *) -val timing = ref false -val tc = ref false -val sources = ref ([] : string list) -val demo = ref (NONE : (string * bool) option) -val tutorial = ref false -val css = ref false +val socket = ".urweb_daemon" -val () = Compiler.beforeC := MLton.GC.pack +(* Encapsulate main invocation handler in a function, possibly to be called multiple times within a daemon. *) -fun printVersion () = (print (Config.versionString ^ "\n"); - OS.Process.exit OS.Process.success) -fun printNumericVersion () = (print (Config.versionNumber ^ "\n"); - OS.Process.exit OS.Process.success) +exception Code of OS.Process.status -fun doArgs args = - case args of - [] => () - | "-version" :: rest => - printVersion () - | "-numeric-version" :: rest => - printNumericVersion () - | "-css" :: rest => - (css := true; - doArgs rest) - | "-demo" :: prefix :: rest => - (demo := SOME (prefix, false); - doArgs rest) - | "-guided-demo" :: prefix :: rest => - (demo := SOME (prefix, true); - doArgs rest) - | "-tutorial" :: rest => - (tutorial := true; - doArgs rest) - | "-protocol" :: name :: rest => - (Settings.setProtocol name; - doArgs rest) - | "-prefix" :: prefix :: rest => - (Settings.setUrlPrefix prefix; - doArgs rest) - | "-db" :: s :: rest => - (Settings.setDbstring (SOME s); - doArgs rest) - | "-dbms" :: name :: rest => - (Settings.setDbms name; - doArgs rest) - | "-debug" :: rest => - (Settings.setDebug true; - doArgs rest) - | "-verbose" :: rest => - (Compiler.debug := true; - doArgs rest) - | "-timing" :: rest => - (timing := true; - doArgs rest) - | "-tc" :: rest => - (tc := true; - doArgs rest) - | "-dumpTypes" :: rest => - (Elaborate.dumpTypes := true; - doArgs rest) - | "-unifyMore" :: rest => - (Elaborate.unifyMore := true; - doArgs rest) - | "-dumpSource" :: rest => - (Compiler.dumpSource := true; - doArgs rest) - | "-output" :: s :: rest => - (Settings.setExe (SOME s); - doArgs rest) - | "-sql" :: s :: rest => - (Settings.setSql (SOME s); - doArgs rest) - | "-static" :: rest => - (Settings.setStaticLinking true; - doArgs rest) - | "-path" :: name :: path :: rest => - (Compiler.addPath (name, path); - doArgs rest) - | "-root" :: name :: root :: rest => - (Compiler.addModuleRoot (root, name); - doArgs rest) - | "-sigfile" :: name :: rest => - (Settings.setSigFile (SOME name); - doArgs rest) - | "-iflow" :: rest => - (Compiler.doIflow := true; - doArgs rest) - | "-moduleOf" :: fname :: _ => - (print (Compiler.moduleOf fname ^ "\n"); - OS.Process.exit OS.Process.success) - | "-noEmacs" :: rest => - (Demo.noEmacs := true; - doArgs rest) - | "-limit" :: class :: num :: rest => - (case Int.fromString num of - NONE => raise Fail ("Invalid limit number '" ^ num ^ "'") - | SOME n => - if n < 0 then - raise Fail ("Invalid limit number '" ^ num ^ "'") - else - Settings.addLimit (class, n); - doArgs rest) - | arg :: rest => - (if size arg > 0 andalso String.sub (arg, 0) = #"-" then - raise Fail ("Unknown flag " ^ arg) - else - sources := arg :: !sources; - doArgs rest) +fun oneRun args = + let + val timing = ref false + val tc = ref false + val sources = ref ([] : string list) + val demo = ref (NONE : (string * bool) option) + val tutorial = ref false + val css = ref false -val () = doArgs (CommandLine.arguments ()) + val () = (Compiler.debug := false; + Elaborate.dumpTypes := false; + Elaborate.unifyMore := false; + Compiler.dumpSource := false; + Compiler.doIflow := false; + Demo.noEmacs := false; + Settings.setDebug false) -val job = - case !sources of - [file] => file - | _ => printVersion () + val () = Compiler.beforeC := MLton.GC.pack -val () = - case (!css, !demo, !tutorial) of - (true, _, _) => - (case Compiler.run Compiler.toCss job of - NONE => OS.Process.exit OS.Process.failure - | SOME {Overall = ov, Classes = cl} => - (app (print o Css.inheritableToString) ov; - print "\n"; - app (fn (x, (ins, ots)) => - (print x; - print " "; - app (print o Css.inheritableToString) ins; - app (print o Css.othersToString) ots; - print "\n")) cl)) - | (_, SOME (prefix, guided), _) => - Demo.make {prefix = prefix, dirname = job, guided = guided} - | (_, _, true) => Tutorial.make job - | _ => - if !tc then - (Compiler.check Compiler.toElaborate job; - if ErrorMsg.anyErrors () then - OS.Process.exit OS.Process.failure - else - ()) - else if !timing then - Compiler.time Compiler.toCjrize job + fun printVersion () = (print (Config.versionString ^ "\n"); + raise Code OS.Process.success) + fun printNumericVersion () = (print (Config.versionNumber ^ "\n"); + raise Code OS.Process.success) + + fun doArgs args = + case args of + [] => () + | "-version" :: rest => + printVersion () + | "-numeric-version" :: rest => + printNumericVersion () + | "-css" :: rest => + (css := true; + doArgs rest) + | "-demo" :: prefix :: rest => + (demo := SOME (prefix, false); + doArgs rest) + | "-guided-demo" :: prefix :: rest => + (demo := SOME (prefix, true); + doArgs rest) + | "-tutorial" :: rest => + (tutorial := true; + doArgs rest) + | "-protocol" :: name :: rest => + (Settings.setProtocol name; + doArgs rest) + | "-prefix" :: prefix :: rest => + (Settings.setUrlPrefix prefix; + doArgs rest) + | "-db" :: s :: rest => + (Settings.setDbstring (SOME s); + doArgs rest) + | "-dbms" :: name :: rest => + (Settings.setDbms name; + doArgs rest) + | "-debug" :: rest => + (Settings.setDebug true; + doArgs rest) + | "-verbose" :: rest => + (Compiler.debug := true; + doArgs rest) + | "-timing" :: rest => + (timing := true; + doArgs rest) + | "-tc" :: rest => + (tc := true; + doArgs rest) + | "-dumpTypes" :: rest => + (Elaborate.dumpTypes := true; + doArgs rest) + | "-unifyMore" :: rest => + (Elaborate.unifyMore := true; + doArgs rest) + | "-dumpSource" :: rest => + (Compiler.dumpSource := true; + doArgs rest) + | "-output" :: s :: rest => + (Settings.setExe (SOME s); + doArgs rest) + | "-sql" :: s :: rest => + (Settings.setSql (SOME s); + doArgs rest) + | "-static" :: rest => + (Settings.setStaticLinking true; + doArgs rest) + | "-path" :: name :: path :: rest => + (Compiler.addPath (name, path); + doArgs rest) + | "-root" :: name :: root :: rest => + (Compiler.addModuleRoot (root, name); + doArgs rest) + | "-sigfile" :: name :: rest => + (Settings.setSigFile (SOME name); + doArgs rest) + | "-iflow" :: rest => + (Compiler.doIflow := true; + doArgs rest) + | "-moduleOf" :: fname :: _ => + (print (Compiler.moduleOf fname ^ "\n"); + raise Code OS.Process.success) + | "-noEmacs" :: rest => + (Demo.noEmacs := true; + doArgs rest) + | "-limit" :: class :: num :: rest => + (case Int.fromString num of + NONE => raise Fail ("Invalid limit number '" ^ num ^ "'") + | SOME n => + if n < 0 then + raise Fail ("Invalid limit number '" ^ num ^ "'") + else + Settings.addLimit (class, n); + doArgs rest) + | arg :: rest => + (if size arg > 0 andalso String.sub (arg, 0) = #"-" then + raise Fail ("Unknown flag " ^ arg) + else + sources := arg :: !sources; + doArgs rest) + + val () = case args of + ["daemon", "stop"] => OS.Process.exit OS.Process.success + | _ => () + + val () = doArgs args + + val job = + case !sources of + [file] => file + | _ => printVersion () + in + case (!css, !demo, !tutorial) of + (true, _, _) => + (case Compiler.run Compiler.toCss job of + NONE => OS.Process.failure + | SOME {Overall = ov, Classes = cl} => + (app (print o Css.inheritableToString) ov; + print "\n"; + app (fn (x, (ins, ots)) => + (print x; + print " "; + app (print o Css.inheritableToString) ins; + app (print o Css.othersToString) ots; + print "\n")) cl; + OS.Process.success)) + | (_, SOME (prefix, guided), _) => + if Demo.make' {prefix = prefix, dirname = job, guided = guided} then + OS.Process.success + else + OS.Process.failure + | (_, _, true) => (Tutorial.make job; + OS.Process.success) + | _ => + if !tc then + (Compiler.check Compiler.toElaborate job; + if ErrorMsg.anyErrors () then + OS.Process.failure + else + OS.Process.success) + else if !timing then + (Compiler.time Compiler.toCjrize job; + OS.Process.success) + else + (if Compiler.compile job then + OS.Process.success + else + OS.Process.failure) + end handle Code n => n + +fun send (sock, s) = + let + val n = Socket.sendVec (sock, Word8VectorSlice.full (Vector.map (Word8.fromInt o ord) s)) + in + if n >= size s then + () else - Compiler.compiler job + send (sock, String.extract (s, n, NONE)) + end + +val () = case CommandLine.arguments () of + ["daemon", "start"] => + (case Posix.Process.fork () of + SOME _ => () + | NONE => + let + val () = Elaborate.incremental := true + val listen = UnixSock.Strm.socket () + + fun loop () = + let + val (sock, _) = Socket.accept listen + + fun loop' (buf, args) = + let + val s = if CharVector.exists (fn ch => ch = #"\n") buf then + "" + else + Vector.map (chr o Word8.toInt) (Socket.recvVec (sock, 1024)) + val s = buf ^ s + val (befor, after) = Substring.splitl (fn ch => ch <> #"\n") (Substring.full s) + in + if Substring.isEmpty after then + loop' (s, args) + else + let + val cmd = Substring.string befor + val rest = Substring.string (Substring.slice (after, 1, NONE)) + in + case cmd of + "" => send (sock, if OS.Process.isSuccess ((oneRun (rev args)) + handle ex => (print "unhandled exception:\n"; + print (General.exnMessage ex ^ "\n"); + OS.Process.failure)) then + "0" + else + "1") + | _ => loop' (rest, cmd :: args) + end + end handle OS.SysErr _ => () + in + loop' ("", []); + Socket.close sock; + MLton.GC.pack (); + loop () + end + in + OS.Process.atExit (fn () => OS.FileSys.remove socket); + Socket.bind (listen, UnixSock.toAddr socket); + Socket.listen (listen, 1); + loop () + end) + + | args => + let + val sock = UnixSock.Strm.socket () + + fun wait () = + let + val v = Socket.recvVec (sock, 1) + in + if Vector.length v = 0 then + OS.Process.failure + else + case chr (Word8.toInt (Vector.sub (v, 0))) of + #"0" => OS.Process.success + | #"1" => OS.Process.failure + | _ => raise Fail "Weird return code from daemon" + end handle OS.SysErr _ => OS.Process.failure + in + if Socket.connectNB (sock, UnixSock.toAddr socket) + orelse not (List.null (#wrs (Socket.select {rds = [], + wrs = [Socket.sockDesc sock], + exs = [], + timeout = SOME (Time.fromSeconds 1)}))) then + (app (fn arg => send (sock, arg ^ "\n")) args; + send (sock, "\n"); + OS.Process.exit (wait ())) + else + (OS.FileSys.remove socket; + raise OS.SysErr ("", NONE)) + end handle OS.SysErr _ => case args of + ["daemon", "stop"] => (OS.FileSys.remove socket handle OS.SysErr _ => ()) + | _ => OS.Process.exit (oneRun args) diff -r 4a03aa3251cb -r ab24a7cb2a64 src/mod_db.sml --- a/src/mod_db.sml Sun Apr 29 13:17:31 2012 -0400 +++ b/src/mod_db.sml Sun Apr 29 16:23:03 2012 -0400 @@ -131,7 +131,7 @@ SOME (#Decl r) else NONE) - | Source.DFfiStr (x, _, tm) => + | Source.DFfiStr (x, _, SOME tm) => (case SM.find (!byName, x) of NONE => NONE | SOME r => diff -r 4a03aa3251cb -r ab24a7cb2a64 src/source.sml --- a/src/source.sml Sun Apr 29 13:17:31 2012 -0400 +++ b/src/source.sml Sun Apr 29 16:23:03 2012 -0400 @@ -155,7 +155,7 @@ | DValRec of (string * con option * exp) list | DSgn of string * sgn | DStr of string * sgn option * Time.time option * str - | DFfiStr of string * sgn * Time.time + | DFfiStr of string * sgn * Time.time option | DOpen of string * string list | DConstraint of con * con | DOpenConstraints of string * string list diff -r 4a03aa3251cb -r ab24a7cb2a64 tests/dep.urp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/dep.urp Sun Apr 29 16:23:03 2012 -0400 @@ -0,0 +1,4 @@ +dep1 +dep2 +dep3 +dep4 diff -r 4a03aa3251cb -r ab24a7cb2a64 tests/dep1.ur --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/dep1.ur Sun Apr 29 16:23:03 2012 -0400 @@ -0,0 +1,1 @@ +val x = "Hello world" diff -r 4a03aa3251cb -r ab24a7cb2a64 tests/dep2.ur --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/dep2.ur Sun Apr 29 16:23:03 2012 -0400 @@ -0,0 +1,1 @@ +val y = Dep1.x diff -r 4a03aa3251cb -r ab24a7cb2a64 tests/dep3.ur --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/dep3.ur Sun Apr 29 16:23:03 2012 -0400 @@ -0,0 +1,1 @@ +val y = Dep1.x ^ "?!" diff -r 4a03aa3251cb -r ab24a7cb2a64 tests/dep4.ur --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/dep4.ur Sun Apr 29 16:23:03 2012 -0400 @@ -0,0 +1,3 @@ +fun main () : transaction page = return + {[Dep2.y]}! +