Package: src/packages/flx_cache.fdoc
Felix Cache¶
key | file |
---|---|
flx_cache.flx | share/lib/std/felix/flx_cache.flx |
The flx cache manager.¶
Check if the flx cache is stale and deletes it if it is.
//[flx_cache.flx]
class FlxCache
{
fun gramtime(debugln: string -> 0) (path:string, s:string) : double = {
//println$ "Path=" + path + " file = " + s;
fun maxtime (x:double) (s:string) => max (x, gramtime debugln (path, s));
if s.[0]=="@".char do
var file =
let f = s.[1 to].strip in
if Filename::is_absolute_filename f then f
else Directory::mk_absolute_filename (Filename::join$ path, f)
;
var filetime = FileStat::dfiletime(file,0.0);
if filetime == 0.0 do
println$ "Grammar include file '" + file "' doesn't exist, exiting";
// this one is pretty fatal :-)
System::exit 1;
done
debugln$ "Grammar include file '" + file + "' time=" + FileStat::strfiletime(filetime);
var filetext = load file;
var files = split (filetext, "\n");
files = map strip of (string) files;
files = filter (fun (s:string) => s != "") files;
files = map (fun (s:string) => Filename::join (split(s,"/"))) files;
//println$ "Files=" + files;
return fold_left maxtime filetime files;
else
file = Filename::join$ path, s;
filetime = FileStat::dfiletime(file,0.0);
if filetime == 0.0 do
println$ "Grammar file " + file " doesn't exist, exiting";
// this one is pretty fatal :-)
System::exit 1;
done
debugln$ "Grammar file " + file + " time=" + FileStat::strfiletime(filetime);
return filetime;
done
}
// FLX_INSTALL_DIR: root for finding standard grammar
// STDGRAMMAR: root standard grammar key, within FLX_INSTALL_DIR
// usually "grammar/grammar.files"
// FLXG: absolute filename of felix compiler executable
// CACHE_DIR: absolute filename of binary cache
// OUTPUT_DIR: absolute filename of text cache
// DEFAULT_CACHE_DIR: default location of CACHE_DIR
// DEFAULT_OUTPUT_DIR: default location of OUTPUT_DIR
// These defaults are used to determine if the
// the cache should be deleted automatically
// or a an interactive query used to verify.
// Automatic deletion requies the caches to be the default.
// CLEAR_CACHE: switch to force clearing the cache
typedef cache_validation_spec_t =
(
FLX_SHARE_DIR:string,
GRAMMAR_DIR:string,
STDGRAMMAR:string,
FLXG:string,
CACHE_DIR:string,
OUTPUT_DIR:string,
CLEAR_CACHE: int,
AUTOMATON: string,
debugln : string -> 0,
xqt: string -> string,
quote: string -> string
);
// CACHE VALIDATION
//
// This function validates the current cache, and if it is considered
// stale may flush it. If the cache is the default one in the users
// home directory the flush is done noisily but unconditionally.
// Otherwise the user is prompted for permission.
// The special cache locations / and . or "" are never deleted
// in case it wipes out parts of the root, home, or current directory.
// The validation checks the time of the flxg compiler used to build
// it against the current flxg compiler, these must be exactly equal.
//
// It also checks that all the files defining the grammar are older
// than the generated automaton.
//
// It does NOT check any RTL C++ libraries are up to date.
// It does NOT check any Felix program files are up to date.
// Therefore it does NOT guarrantee the contents of the cache are valid.
// Rather it ensures only that the compiler and cached automaton are not stale.
// However if they are stale the whole cache is invalidated.
//
// In effect this means this function ensures the parser is ready and valid
// or non-existant. The compiler and automaton are locked together. If the compiler
// changes the automaton must be rebuilt.
// returns cache time
gen validate_cache (var spec: cache_validation_spec_t) : int * double =
{
// ensure the cache directory exists
Directory::mkdirs(spec.CACHE_DIR);
// get the OS timestamp of the flxg compiler, +inf if not found
var flxg_time = FileStat::dfiletime(spec.FLXG, #FileStat::future_time);
spec.debugln$ "Flxg=" + spec.FLXG;
spec.debugln$ "Flxg_time=" + FileStat::strfiletime(flxg_time);
// get the OS timestamp of the file flxg_time.stamp
// this file is created with the cache
var flxg_stamp = Filename::join spec.CACHE_DIR "flxg_time.stamp";
var cache_time = FileStat::dfiletime(flxg_stamp,#FileStat::future_time);
spec.debugln$ "cache_time=" + FileStat::strfiletime(cache_time);
// get the timestamp string recorded in flxg_time.stamp
var flxg_stamp_data = load flxg_stamp;
//println$ "Flxg_stamp_data=" + flxg_stamp_data;
// convert the timestamp string to a double, if there is junk
// there or the string is empty, 0.0 is returned by atof,
// adjust that to -inf
var flxg_stamp_time = match flxg_stamp_data.atof with | 0.0 => #FileStat::past_time | x => x;
spec.debugln$ "Flxg_stamp_data : " + FileStat::strfiletime(flxg_stamp_time);
// Calculate the time of the newest text file defining the grammar
// these are files in directory share/lib/grammar.
var grammar_time = gramtime spec.debugln (spec.GRAMMAR_DIR, "@"+spec.STDGRAMMAR);
spec.debugln$ "Grammar text time=" + FileStat::strfiletime (grammar_time);
// calculate the name of the compiled grammar automaton in the cache
var automaton_name = spec.AUTOMATON;
// Get the timestamp of the grammar automaton or -inf if it doesn't exist.
var automaton_time = FileStat::dfiletime(automaton_name,#FileStat::past_time);
spec.debugln$ "Automaton " + automaton_name + " time=" + FileStat::strfiletime(automaton_time);
// If the cache exists and the recorded compiler time stamp is not equal
// to the current compiler time stamp, then the cache is stale
// and should be deleted.
if cache_time != #FileStat::future_time and flxg_stamp_time != flxg_time do
println$ "Cache may be out of date due to compiler change!";
println$ "Flxg compiler time stamp=" + FileStat::strfiletime(flxg_time);
println$ "Cache time stamp =" + FileStat::strfiletime(cache_time);
// special safety check if the output dirs are root or current directory
if not (
(spec.OUTPUT_DIR == "/" or spec.OUTPUT_DIR == "" or spec.OUTPUT_DIR == ".") or
(spec.CACHE_DIR == "/" or spec.CACHE_DIR == "" or spec.CACHE_DIR == ".")
)
do
spec&.CLEAR_CACHE <- 1;
done
// If the automaton exists and the grammar is newer than the automaton
// then the cache is stale and should be deleted.
elif grammar_time > automaton_time do
println$ "Cache may be out of date due to grammar upgrade!";
println$ "Grammar time stamp =" + FileStat::strfiletime(grammar_time);
println$ "Automaton.syntax time stamp =" + FileStat::strfiletime(automaton_time);
spec&.CLEAR_CACHE <- 1;
done
// FFF BE CAREFUL! The value "/" for these caches is perfectly good
if spec.CLEAR_CACHE != 0 do
// refuse to delete "" or "/" or ".", basic safety check
if
(spec.OUTPUT_DIR == "/" or spec.OUTPUT_DIR == "" or spec.OUTPUT_DIR == ".") or
(spec.CACHE_DIR == "/" or spec.CACHE_DIR == "" or spec.CACHE_DIR == ".")
do
println "WILL NOT DELETE CACHES";
println$ "output cache " + spec.OUTPUT_DIR;
println$ "binary cache " + spec.CACHE_DIR;
// INTENTIONAL EXIT
System::exit(1);
done
println$ "Delete cache " + spec.OUTPUT_DIR;
if PLAT_WIN32 do
C_hack::ignore$ spec.xqt("mkdir "+spec.quote(spec.OUTPUT_DIR+"\\rubbish") +"& rmdir /Q /S " + spec.quote(spec.OUTPUT_DIR));
else
C_hack::ignore$ spec.xqt("rm -rf " + spec.quote(spec.OUTPUT_DIR));
done
println$ "Delete cache " + spec.CACHE_DIR;
if PLAT_WIN32 do
C_hack::ignore$ spec.xqt("mkdir "+spec.quote(spec.CACHE_DIR+"\\rubbish")+"& rd /Q /S " + spec.quote(spec.CACHE_DIR));
else
C_hack::ignore$ spec.xqt("rm -rf " + spec.quote(spec.CACHE_DIR));
done
// Make a new cache.
Directory::mkdirs(spec.CACHE_DIR);
// make the stamp file with the time of the current compiler.
var f = fopen_output flxg_stamp;
write(f, fmt(flxg_time, fixed (0,3)));
f.fclose;
done
return spec.CLEAR_CACHE, cache_time;
}
fun cache_join (c:string, var f:string) =
{
//debugln$ "[cache_join] " + c + " with " + f;
if PLAT_WIN32 do
if f.[1 to 3] == ":\\" do f = f.[0 to 1]+f.[2 to];
elif f.[1] == char ":" do f = f.[0 to 1]+"\\"+f.[2 to];
done
if f.[0] == char "\\" do f = f.[1 to]; done
else
if f.[0] == char "/" do f = f.[1 to]; done
done
var k = Filename::join(c,f);
//debugln$ "[cache_join] result = " + k;
return k;
}
}