1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
|
(*
Title: Modified version of the "use" function which saves state
Author: David Matthews
Copyright David Matthews 2009
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*)
(*
This is an example version of the "use" function that the IDE may call
in the prelude before a build. It takes a directory name and returns a "use"
function that saves the state and dependencies in ".save" and ".deps" files
within that directory. It should be called as e.g.
val use = ideUse ".polysave"
to define a version of "use" for the rest of the compilation.
*)
fun ideUse saveDirName =
let
(* If we are building under the IDE we need to record the dependencies
and also save the state before each file we "use". *)
val saveDirectory: string option ref = ref NONE
val dependencies: string list ref = ref []
open OS.Path
(* Get the root directory and save directory (typically .polysave). Do this once
when this function is called and convert them to absolute paths using the
current directory when this is called.
Assume that the root directory is the parent of the save directory.
N.B. Because the directory may not yet exist we can't use OS.FileSys.fullPath. *)
val saveDirPath = mkAbsolute { path = saveDirName, relativeTo = OS.FileSys.getDir() }
(* The root directory is the directory that is assumed to be the root of the project.
For each source file within this directory with path a/b/c.ML there will be a
corresponding saved state file .polysave/a/b/c.ML .
If "use" is called on a file that is not within the root directory no information
will be saved for that file. *)
val { dir = rootPath, ...} = splitDirFile saveDirPath
fun preUse fileName =
let
open OS.Path
(* Create a directory hierarchy. *)
fun createDirs path =
if path = "" orelse (OS.FileSys.isDir path handle OS.SysErr _ => false)
then ()
else
(
createDirs (OS.Path.dir path);
OS.FileSys.mkDir path
);
(* Compute the full path to the actual file taking account of any
change of directory then make it relative to the root. *)
val filePathRelativeToRoot =
let
val fullFileName = OS.FileSys.fullPath fileName
val pathFromRoot = mkRelative { path = fullFileName, relativeTo = rootPath }
(* Is the file in the root directory or a sub-directory or is it in
some other directory? *)
val { arcs, ...} = fromString pathFromRoot
in
case arcs of
topArc :: _ =>
(* If the first part of the path is ".." then it's in some other directory. *)
if topArc = parentArc then NONE else SOME pathFromRoot
| _ => NONE (* No path at all? *)
end handle Path => NONE (* Different volumes: can't make relative path. *)
| OS.SysErr _ => NONE (* If fileName doesn't actually exist. *)
in
case filePathRelativeToRoot of
NONE => () (* Do nothing: we can't save it. *)
| SOME fileName =>
let
local
val baseName = concat(saveDirPath, fileName)
in
val saveFile =
mkCanonical (joinBaseExt{ base = baseName, ext = SOME "save"})
val depFile =
mkCanonical (joinBaseExt{ base = baseName, ext = SOME "deps"})
end
(* Reset the save directory before we save so that it isn't set in the saved
state. That means that "use" won't save the state unless it's explicitly
asked to. *)
val saveSave = ! saveDirectory
in
(* Create any containing directories. *)
createDirs(OS.Path.dir saveFile);
saveDirectory := NONE;
(* Save the state. *)
PolyML.SaveState.saveChild (saveFile, List.length(PolyML.SaveState.showHierarchy()));
(* Restore the ref. *)
saveDirectory := saveSave;
(* Write out the dependencies. *)
let
open TextIO
val f = openOut depFile
in
List.app(fn s => output(f, s ^ "\n")) (!dependencies);
closeOut f
end;
(* Add this file to the dependency list. *)
dependencies := ! dependencies @ [fileName]
end handle (ex as OS.SysErr args) =>
(print (String.concat["Exception SysErr(", PolyML.makestring args, ") raised for ", fileName, "\n"]); raise ex)
end
in
fn originalName =>
let
(* Find the actual file name by following the suffixes. This mirrors what "use" will do. *)
(* use "f" first tries to open "f" but if that fails it tries "f.ML", "f.sml" etc. *)
fun trySuffixes [] =
(* Not found - attempt to open the original and pass back the
exception. *)
(TextIO.openIn originalName, originalName)
| trySuffixes (s::l) =
(TextIO.openIn (originalName ^ s), originalName ^ s)
handle IO.Io _ => trySuffixes l
(* First in list is the name with no suffix. *)
val (inStream, fileName) = trySuffixes("" :: ! PolyML.suffixes)
val () = preUse fileName
in
PolyML.use fileName (* Now call the underlying use to do the work. *)
end
end;
|