Skip to content

Commit

Permalink
Loaded dlls versions are used to invalidate cache.
Browse files Browse the repository at this point in the history
  • Loading branch information
xavierzwirtz committed Jul 23, 2015
1 parent 762af27 commit e69f2ee
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 18 deletions.
104 changes: 90 additions & 14 deletions src/app/FakeLib/FSIHelper.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ open System
open System.IO
open System.Diagnostics
open System.Threading
open System.Xml.Linq

let private FSIPath = @".\tools\FSharp\;.\lib\FSharp\;[ProgramFilesX86]\Microsoft SDKs\F#\4.0\Framework\v4.0;[ProgramFilesX86]\Microsoft SDKs\F#\3.1\Framework\v4.0;[ProgramFilesX86]\Microsoft SDKs\F#\3.0\Framework\v4.0;[ProgramFiles]\Microsoft F#\v4.0\;[ProgramFilesX86]\Microsoft F#\v4.0\;[ProgramFiles]\FSharp-2.0.0.0\bin\;[ProgramFilesX86]\FSharp-2.0.0.0\bin\;[ProgramFiles]\FSharp-1.9.9.9\bin\;[ProgramFilesX86]\FSharp-1.9.9.9\bin\"

Expand All @@ -25,14 +26,20 @@ let private extractDirectives (regex : System.Text.RegularExpressions.Regex) scr
|> Seq.map(fun m ->
(m.Groups.Item("path").Value)
)

let getAllScriptContents (pathsAndContents : seq<string * string>) =
pathsAndContents |> Seq.map(snd)

type Script = {
Content : string
Location : string
SearchPaths : string seq
IncludedAssemblies : Lazy<string seq>
}

let getAllScriptContents (pathsAndContents : seq<Script>) =
pathsAndContents |> Seq.map(fun s -> s.Content)
let getIncludedAssembly scriptContents = extractDirectives rAssemblyRegex scriptContents
let getSearchPaths scriptContents = extractDirectives searchPathRegex scriptContents

let rec getAllScripts scriptPath : seq<string * string> =

let rec getAllScripts scriptPath : seq<Script> =
let scriptContents = File.ReadAllText(scriptPath)
let searchPaths = (getSearchPaths scriptContents |> Seq.toList)

Expand All @@ -57,13 +64,62 @@ let rec getAllScripts scriptPath : seq<string * string> =
| Some x -> x
getAllScripts path
)
Seq.concat [List.toSeq [scriptPath, scriptContents]; loadedContents]
let s =
{ Location = scriptPath
Content = scriptContents
SearchPaths = searchPaths
IncludedAssemblies = lazy(getIncludedAssembly scriptContents) }
Seq.concat [List.toSeq [s]; loadedContents]

let getScriptHash pathsAndContents =
let fullContents = getAllScriptContents pathsAndContents |> String.concat "\n"
let hasher = HashLib.HashFactory.Checksum.CreateCRC32a()
hasher.ComputeString(fullContents).ToString()

module private Cache =
let xname name = XName.Get(name)
let create (loadedAssemblies : Reflection.Assembly seq) =
let xelement name = XElement(xname name)
let xattribute name value = XAttribute(xname name, value)

let doc = XDocument()
let root = xelement "FAKECache"
doc.Add(root)
let assemblies = xelement "Assemblies"
root.Add(assemblies)

let assemNodes =
loadedAssemblies
|> Seq.map(fun assem ->
let ele = xelement "Assembly"
ele.Add(xattribute "Location" assem.Location)
ele.Add(xattribute "FullName" assem.FullName)
ele.Add(xattribute "Version" (assem.GetName().Version.ToString()))
ele)
|> Seq.iter(assemblies.Add)
doc

type AssemblyInfo = {
Location : string
FullName : string
Version : string
}

type CacheConfig = {
Assemblies : AssemblyInfo seq
}
let read (path : string) : CacheConfig =
let doc = XDocument.Load(path)
//let root = doc.Descendants() |> Seq.exactlyOne
let assembliesEle = doc.Descendants(xname "Assemblies") |> Seq.exactlyOne
let assemblies =
assembliesEle.Descendants()
|> Seq.map(fun assemblyEle ->
let get name = assemblyEle.Attribute(xname name).Value
{ Location = get "Location"
FullName = get "FullName"
Version = get "Version" })
{ Assemblies = assemblies }
/// The path to the F# Interactive tool.
let fsiPath =
let ev = environVar "FSI"
Expand Down Expand Up @@ -194,8 +250,29 @@ let internal runFAKEScriptWithFsiArgsAndRedirectMessages printDetails (FsiArgs(f
let scriptFileName = lazy(Path.GetFileName(scriptPath))
let hashPath = lazy("./.fake/" + scriptFileName.Value + "_" + scriptHash.Value)
let assemblyPath = lazy(hashPath.Value + ".dll")
let assemblyRefPath = lazy(hashPath.Value + "_references.txt")
let cacheValid = lazy (File.Exists(assemblyPath.Value) && File.Exists(assemblyRefPath.Value))
let cacheConfigPath = lazy(hashPath.Value + "_config.xml")
let cacheConfig = lazy(Cache.read cacheConfigPath.Value)
let cacheValid = lazy (
let cacheFilesExist =
System.IO.File.Exists(assemblyPath.Value) &&
System.IO.File.Exists(cacheConfigPath.Value)
if cacheFilesExist then
let assemVersionValidCount =
cacheConfig.Value.Assemblies
|> Seq.map(fun assemInfo ->
let assem =
if assemInfo.Location <> "" then
Reflection.Assembly.LoadFrom(assemInfo.Location)
else
Reflection.Assembly.Load(assemInfo.FullName)
assem.GetName().Version.ToString() = assemInfo.Version)
|> Seq.filter(fun x -> x = true)
|> Seq.length

assemVersionValidCount = Seq.length cacheConfig.Value.Assemblies
else
false
)

let getScriptAndHash fileName =
let matched = hashRegex.Match(fileName)
Expand All @@ -211,9 +288,6 @@ let internal runFAKEScriptWithFsiArgsAndRedirectMessages printDetails (FsiArgs(f
(noExtension.Substring(1))
(Path.GetExtension(scriptFileName.Value).Substring(1))

for loc in File.ReadAllLines(assemblyRefPath.Value) do
Reflection.Assembly.LoadFrom(loc) |> ignore

let assembly = Reflection.Assembly.LoadFrom(assemblyPath.Value)

let mainModule = assembly.GetType(fullName)
Expand Down Expand Up @@ -269,9 +343,11 @@ let internal runFAKEScriptWithFsiArgsAndRedirectMessages printDetails (FsiArgs(f
let refedAssemblies =
System.AppDomain.CurrentDomain.GetAssemblies()
|> Seq.filter(fun assem -> not assem.IsDynamic)
|> Seq.map(fun assem -> assem.Location)

File.WriteAllLines(assemblyRefPath.Value, refedAssemblies) |> ignore
//|> Seq.map(fun assem -> assem.Location)

let cacheConfig : XDocument = Cache.create refedAssemblies
cacheConfig.Save(cacheConfigPath.Value)
//File.WriteAllLines(assemblyRefPath.Value, refedAssemblies) |> ignore
if printDetails then trace (System.Environment.NewLine + "Saved cache")
with
| ex ->
Expand Down
8 changes: 4 additions & 4 deletions src/test/Test.FAKECore/FSIHelperSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ static string Run(string script, string arguments, bool useCache)

static string nl = System.Environment.NewLine;

static Tuple<string, string> sc(string path, string contents)
static FSIHelper.Script script(string path, string contents)
{
return new Tuple<string, string>(path.Replace("\\", "/"), contents);
return new FSIHelper.Script(contents, path.Replace("\\", "/"), null, null);
}

It should_use_then_invalidate_cache =
Expand All @@ -82,7 +82,7 @@ static Tuple<string, string> sc(string path, string contents)
{
File.WriteAllText(scriptFilePath, "printf \"foobar\"");
var scriptHash =
FSIHelper.getScriptHash(new Tuple<string, string>[] { sc(scriptFilePath, "printf \"foobar\"") });
FSIHelper.getScriptHash(new FSIHelper.Script[] { script(scriptFilePath, "printf \"foobar\"") });

var cacheFilePath = Path.Combine(".", ".fake", scriptFileName + "_" + scriptHash + ".dll");

Expand All @@ -105,7 +105,7 @@ static Tuple<string, string> sc(string path, string contents)

File.WriteAllText(scriptFilePath, "printf \"foobarbaz\"");

var changedScriptHash = FSIHelper.getScriptHash(new Tuple<string, string>[] { sc(scriptFilePath, "printf \"foobarbaz\"") });
var changedScriptHash = FSIHelper.getScriptHash(new FSIHelper.Script[] { script(scriptFilePath, "printf \"foobarbaz\"") });
RunExplicit(scriptFilePath, arguments, true)
.ShouldStartWith("Cache is invalid, recompiling");

Expand Down

0 comments on commit e69f2ee

Please sign in to comment.