diff --git a/paket.dependencies b/paket.dependencies index e4a5b32efc8..a87e0f3d6e6 100644 --- a/paket.dependencies +++ b/paket.dependencies @@ -36,3 +36,4 @@ nuget xunit.extensions nuget Newtonsoft.Json nuget Microsoft.AspNet.Razor 2.0.30506 nuget Microsoft.AspNet.WebPages 2.0.30506 +nuget HashLib diff --git a/paket.lock b/paket.lock index 43770672c2b..bedc87c45d9 100644 --- a/paket.lock +++ b/paket.lock @@ -12,7 +12,7 @@ NUGET FsCheck.Xunit (1.0.4) FsCheck (>= 1.0.4) xunit (>= 1.9.2) - FSharp.Compiler.Service (0.0.89) + FSharp.Compiler.Service (1.3.1.0) FSharp.Core (3.1.2.1) FSharp.Formatting (2.9.3) FSharp.Compiler.Service (>= 0.0.87) @@ -20,6 +20,7 @@ NUGET FSharp.Formatting.CommandTool (2.9.3) FSharpVSPowerTools.Core (1.8.0) FSharp.Compiler.Service (>= 0.0.87) + HashLib (2.0.1) jQuery (2.1.3) Knockout (0.0.1) AspNetMvc (>= 4.0.0.0) @@ -79,4 +80,4 @@ NUGET xunit (1.9.2) xunit.extensions (1.9.2) xunit (1.9.2) - xunit.runners (1.9.2) \ No newline at end of file + xunit.runners (1.9.2) diff --git a/src/app/FAKE/Cli.fs b/src/app/FAKE/Cli.fs index ded7f41a59f..80db603840e 100644 --- a/src/app/FAKE/Cli.fs +++ b/src/app/FAKE/Cli.fs @@ -15,6 +15,7 @@ type FakeArg = | [] [] Boot of string | [] Break | [] Single_Target + | [] NoCache interface IArgParserTemplate with member x.Usage = match x with @@ -27,6 +28,7 @@ type FakeArg = | Boot _ -> "Boostrapp your FAKE script." | Break -> "Pauses FAKE with a Debugger.Break() near the start" | Single_Target -> "Runs only the specified target and not the dependencies." + | NoCache -> "Disables caching of compiled script" /// Return the parsed FAKE args or the parse exception. let parsedArgsOrEx args = diff --git a/src/app/FAKE/Program.fs b/src/app/FAKE/Program.fs index 23df49f8e43..955e01b841e 100644 --- a/src/app/FAKE/Program.fs +++ b/src/app/FAKE/Program.fs @@ -116,7 +116,8 @@ try //TODO if printDetails then printEnvironment cmdArgs args - if not (runBuildScriptWithFsiArgsAt printDetails fsiArgs envVars) then Environment.ExitCode <- 1 + let useCache = not (fakeArgs.Contains <@ Cli.NoCache @>) + if not (runBuildScriptWithFsiArgsAt printDetails fsiArgs envVars useCache true) then Environment.ExitCode <- 1 else if printDetails then log "Ready." () @@ -135,7 +136,7 @@ try let printDetails = containsParam "details" cmdArgs if printDetails then printEnvironment cmdArgs args - if not (runBuildScript printDetails buildScriptArg fsiArgs args) then Environment.ExitCode <- 1 + if not (runBuildScript printDetails buildScriptArg fsiArgs args true true) then Environment.ExitCode <- 1 else if printDetails then log "Ready." | Some handler -> handler.Interact() diff --git a/src/app/FakeLib/FSIHelper.fs b/src/app/FakeLib/FSIHelper.fs index ef141a08190..38fb66469d7 100644 --- a/src/app/FakeLib/FSIHelper.fs +++ b/src/app/FakeLib/FSIHelper.fs @@ -10,6 +10,49 @@ open System.Threading 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\" +let createDirectiveRegex id = + Text.RegularExpressions.Regex( + "^\s*#" + id + "\s*(@\"|\"\"\"|\")(?.+?)(\"\"\"|\")", + System.Text.RegularExpressions.RegexOptions.Compiled ||| + System.Text.RegularExpressions.RegexOptions.Multiline) +let loadRegex = createDirectiveRegex "load" +let rAssemblyRegex = createDirectiveRegex "r" +let searchPathRegex = createDirectiveRegex "I" + +let private extractDirectives (regex : System.Text.RegularExpressions.Regex) scriptContents = + regex.Matches(scriptContents) + |> Seq.cast + |> Seq.map(fun m -> + (m.Groups.Item("path").Value) + ) +let rec getAllScripts scriptPath : seq = + let scriptPath = + if Path.IsPathRooted scriptPath then + scriptPath + else + Path.Combine(Directory.GetCurrentDirectory(), scriptPath) + let scriptContents = File.ReadAllText(scriptPath) + let loadedContents = + extractDirectives loadRegex scriptContents + |> Seq.collect(fun path -> + let path = + if Path.IsPathRooted path then + path + else + Path.Combine(Path.GetDirectoryName(scriptPath), path) + getAllScripts path + ) + Seq.concat [List.toSeq [scriptPath, scriptContents]; loadedContents] + +let getAllScriptContents (pathsAndContents : seq) = + pathsAndContents |> Seq.map(snd) +let getIncludedAssembly scriptContents = extractDirectives rAssemblyRegex scriptContents +let getSearchPaths scriptContents = extractDirectives searchPathRegex scriptContents + +let getScriptHash pathsAndContents = + let fullContents = getAllScriptContents pathsAndContents |> String.concat("\n") + let hasher = HashLib.HashFactory.Checksum.CreateCRC32a() + hasher.ComputeString(fullContents).ToString() /// The path to the F# Interactive tool. let fsiPath = let ev = environVar "FSI" @@ -87,9 +130,14 @@ let executeFSIWithScriptArgsAndReturnMessages script (scriptArgs: string[]) = open Microsoft.FSharp.Compiler.Interactive.Shell +type private AssemblySource = +| GAC +| Disk + +let hashRegex = Text.RegularExpressions.Regex("(?