diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 205b9848d71..476a23dfcac 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,6 @@ +#### 5.0.0-alpha018 - 24.09.2017 +* BUGFIX: Cache loaded assemblies and redirect later calls. + #### 5.0.0-alpha017 - 23.09.2017 * BUGFIX: try to fallback to load framework assemblies from the default AssemblyLoadContext. diff --git a/build.fsx b/build.fsx index a5ec0f86acc..97137df365b 100644 --- a/build.fsx +++ b/build.fsx @@ -883,6 +883,15 @@ Target.Create "ReleaseDocs" (fun _ -> open Fake.Api Target.Create "FastRelease" (fun _ -> + + Git.Staging.StageAll "" + Git.Commit.Commit "" (sprintf "Bump version to %s" release.NugetVersion) + let branch = Git.Information.getBranchName "" + Git.Branches.pushBranch "" "origin" branch + + Git.Branches.tag "" release.NugetVersion + Git.Branches.pushTag "" "origin" release.NugetVersion + let token = match Environment.environVarOrDefault "github_token" "" with | s when not (System.String.IsNullOrWhiteSpace s) -> s @@ -900,14 +909,6 @@ Target.Create "FastRelease" (fun _ -> draftWithFiles |> GitHub.releaseDraft |> Async.RunSynchronously - - Git.Staging.StageAll "" - Git.Commit.Commit "" (sprintf "Bump version to %s" release.NugetVersion) - let branch = Git.Information.getBranchName "" - Git.Branches.pushBranch "" "origin" branch - - Git.Branches.tag "" release.NugetVersion - Git.Branches.pushTag "" "origin" release.NugetVersion ) open System diff --git a/src/app/Fake.Core.Targets/Target.fs b/src/app/Fake.Core.Targets/Target.fs index f6ddebe9f4c..dede1a8130c 100644 --- a/src/app/Fake.Core.Targets/Target.fs +++ b/src/app/Fake.Core.Targets/Target.fs @@ -219,7 +219,7 @@ module Target = /// Represents build errors type BuildError = { Target : string - Message : string } + Error : exn } //let mutable private errors = [] let private errorsVar = "Fake.Core.Targets.errors" @@ -240,7 +240,7 @@ module Target = //| BuildException(msg, errs) -> // let errMsgs = errs |> List.map(fun e -> { Target = targetName; Message = e }) // { Target = targetName; Message = msg } :: (errMsgs @ errors) - | _ -> { Target = targetName; Message = exn.ToString() } :: GetErrors()) + | _ -> { Target = targetName; Error = exn } :: GetErrors()) let error e = match e with //| BuildException(msg, errs) -> msg + (if PrintStackTraceOnError then Environment.NewLine + e.StackTrace.ToString() else "") @@ -291,7 +291,7 @@ module Target = |> Seq.map (fun kv -> kv.Key) |> Seq.iter (fun name -> try - let watch = new System.Diagnostics.Stopwatch() + let watch = System.Diagnostics.Stopwatch() watch.Start() Trace.tracefn "Starting BuildFailureTarget: %s" name let target = Get name @@ -370,7 +370,7 @@ module Target = let internal WriteErrors () = Trace.traceLine() GetErrors() - |> Seq.mapi(fun i e -> sprintf "%3d) %s" (i + 1) e.Message) + |> Seq.mapi(fun i e -> sprintf "%3d) %s" (i + 1) e.Error.Message) |> Seq.iter Trace.traceError /// Writes a build time report. @@ -541,8 +541,13 @@ module Target = match GetErrors() with | [] -> () - | errors -> failwithf "A target failed: %A" errors - + | errors -> + let targets = errors |> Seq.map (fun e -> e.Target) |> Seq.distinct + let targetStr = String.Join(", ", targets) + AggregateException( + sprintf "Targets '%s' failed." targetStr, + errors |> Seq.map (fun e -> e.Error)) + |> raise /// Registers a BuildFailureTarget (not activated). let BuildFailureTarget name body = Create name body diff --git a/src/app/Fake.Runtime/CoreCache.fs b/src/app/Fake.Runtime/CoreCache.fs index beccae74976..1d5698cea04 100644 --- a/src/app/Fake.Runtime/CoreCache.fs +++ b/src/app/Fake.Runtime/CoreCache.fs @@ -199,6 +199,7 @@ let loadAssembly (loadContext:AssemblyLoadContext) printDetails (assemInfo:Assem if printDetails then tracefn "Unable to find assembly %A. (Error: %O)" assemInfo ex None + let findAndLoadInRuntimeDeps (loadContext:AssemblyLoadContext) (name:AssemblyName) printDetails (runtimeDependencies:AssemblyInfo list) = let strName = name.FullName if printDetails then tracefn "Trying to resolve: %s" strName @@ -219,33 +220,43 @@ let findAndLoadInRuntimeDeps (loadContext:AssemblyLoadContext) (name:AssemblyNam | Some a -> a.FullName = strName, (Some (None, a)) | None -> - match runtimeDependencies |> List.tryFind (fun r -> r.FullName = strName) with - | Some a -> - true, loadAssembly loadContext printDetails a - | _ -> - let token = name.GetPublicKeyToken() - match runtimeDependencies - |> Seq.map (fun r -> AssemblyName(r.FullName), r) - |> Seq.tryFind (fun (n, _) -> - n.Name = name.Name && - (isNull token || // When null accept what we have. - n.GetPublicKeyToken() = token)) with - | Some (otherName, info) -> - // Then the version matches and the public token is null we still accept this as perfect match - (isNull token && otherName.Version = name.Version), loadAssembly loadContext printDetails info - | _ -> #if NETSTANDARD1_6 - // One last option is to try and load from the default app-context... - try let assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(name) + // Check if we can resolve to a framework assembly. + let result = + try let assembly = AssemblyLoadContext.Default.LoadFromAssemblyName(name) + let isFramework = + assembly.GetCustomAttributes() + |> Seq.exists (fun m -> m.Key = ".NETFrameworkAssembly") + if not isFramework then + None + else let location = try Some assembly.Location with e -> if printDetails then tracefn "Could not get Location from '%s': %O" strName e None - true, Some (location, assembly) - with e -> - if printDetails then tracefn "Could not find assembly in the default load-context: %s" strName + Some (location, assembly) + with e -> None + match result with + | Some r -> true, Some r + | None -> #endif + if printDetails then tracefn "Could not find assembly in the default load-context: %s" strName + match runtimeDependencies |> List.tryFind (fun r -> r.FullName = strName) with + | Some a -> + true, loadAssembly loadContext printDetails a + | _ -> + let token = name.GetPublicKeyToken() + match runtimeDependencies + |> Seq.map (fun r -> AssemblyName(r.FullName), r) + |> Seq.tryFind (fun (n, _) -> + n.Name = name.Name && + (isNull token || // When null accept what we have. + n.GetPublicKeyToken() = token)) with + | Some (otherName, info) -> + // Then the version matches and the public token is null we still accept this as perfect match + (isNull token && otherName.Version = name.Version), loadAssembly loadContext printDetails info + | _ -> false, None match result with | Some (location, a) -> @@ -260,22 +271,29 @@ let findAndLoadInRuntimeDeps (loadContext:AssemblyLoadContext) (name:AssemblyNam if printDetails then tracefn "Could not resolve: %s" strName null +let findAndLoadInRuntimeDepsCached = + let assemblyCache = System.Collections.Concurrent.ConcurrentDictionary<_,Assembly>() + fun (loadContext:AssemblyLoadContext) (name:AssemblyName) printDetails (runtimeDependencies:AssemblyInfo list) -> + let mutable wasCalled = false + let result = assemblyCache.GetOrAdd(name.Name, (fun _ -> + wasCalled <- true + findAndLoadInRuntimeDeps loadContext name printDetails runtimeDependencies)) + if not wasCalled then + let loadedName = result.GetName() + let isPerfectMatch = loadedName.Name = name.Name && loadedName.Version = name.Version + if not isPerfectMatch then + traceFAKE "Redirect assembly from '%A' to previous loaded assembly '%A'" name loadedName + else + if printDetails then tracefn "Redirect assembly load to previously loaded assembly: %A" loadedName + result + #if NETSTANDARD1_6 // See https://github.com/dotnet/coreclr/issues/6411 type FakeLoadContext (printDetails:bool, dependencies:AssemblyInfo list) = inherit AssemblyLoadContext() - //let basePath = System.AppContext.BaseDirectory - //let references = - // System.IO.Directory.GetFiles(basePath, "*.dll") - // |> Seq.filter (fun r -> not (System.IO.Path.GetFileName(r).ToLowerInvariant().StartsWith("api-ms"))) - // |> Seq.choose (fun r -> - // try Some (AssemblyInfo.ofLocation r) - // with e -> None) - // |> Seq.toList - //let allReferences = references @ dependencies let allReferences = dependencies override x.Load(assem:AssemblyName) = - findAndLoadInRuntimeDeps x assem printDetails allReferences + findAndLoadInRuntimeDepsCached x assem printDetails allReferences #endif let fakeDirectoryName = ".fake"