From bbf87520db51644bd6d30a15eaf9076826f815d2 Mon Sep 17 00:00:00 2001 From: Maxime Mangel Date: Mon, 29 Jan 2024 11:21:26 +0100 Subject: [PATCH 01/16] Experiment with MSBuild DesignTime --- src/Fable.Cli/BuildalyzerCrackerResolver.fs | 305 +++++++++----------- src/Fable.Cli/Main.fs | 7 +- 2 files changed, 143 insertions(+), 169 deletions(-) diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/BuildalyzerCrackerResolver.fs index 10397d87a8..3fae1c4083 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/BuildalyzerCrackerResolver.fs @@ -1,4 +1,4 @@ -namespace Fable.Cli +module Fable.Cli.MsBuildCrackerResolver open System open System.Xml.Linq @@ -8,178 +8,147 @@ open Fable.AST open Fable.Compiler.Util open Fable.Compiler.ProjectCracker open Buildalyzer - -/// Use Buildalyzer to invoke MSBuild and get F# compiler args from an .fsproj file. -/// As we'll merge this later with other projects we'll only take the sources and -/// the references, checking if some .dlls correspond to Fable libraries -type BuildalyzerCrackerResolver() = - let mutable manager = None - - let tryGetResult - (isMain: bool) - (opts: CrackerOptions) - (manager: AnalyzerManager) - (maybeCsprojFile: string) - = - if isMain && not opts.NoRestore then - Process.runSync - (IO.Path.GetDirectoryName opts.ProjFile) - "dotnet" - [ - "restore" - IO.Path.GetFileName maybeCsprojFile - // $"-p:TargetFramework={opts.TargetFramework}" - for constant in opts.FableOptions.Define do - $"-p:{constant}=true" - ] - |> ignore - - let analyzer = manager.GetProject(maybeCsprojFile) - - let env = - analyzer.EnvironmentFactory.GetBuildEnvironment( - Environment.EnvironmentOptions( - DesignTime = true, - Restore = false - ) - ) - // If the project targets multiple frameworks, multiple results will be returned - // For now we just take the first one with non-empty command - let results = analyzer.Build(env) - results |> Seq.tryFind (fun r -> String.IsNullOrEmpty(r.Command) |> not) +open System.Reflection +open System.IO +open System.Diagnostics +open System.Text.Json + + +/// Add additional Fable arguments +let private mkOptions (compilerArgs: string array) : string array = + [| + yield! compilerArgs + yield "--define:FABLE_COMPILER" + yield "--define:FABLE_COMPILER_4" + yield "--define:FABLE_COMPILER_JAVASCRIPT" + |] + +type FullPath = string + +let private dotnet_msbuild (fsproj: FullPath) (args: string) : Async = + backgroundTask { + let psi = ProcessStartInfo "dotnet" + let pwd = Assembly.GetEntryAssembly().Location |> Path.GetDirectoryName + psi.WorkingDirectory <- "/Users/mmangel" + psi.Arguments <- $"msbuild \"%s{fsproj}\" %s{args}" + psi.RedirectStandardOutput <- true + psi.RedirectStandardError <- true + psi.UseShellExecute <- false + use ps = new Process() + ps.StartInfo <- psi + ps.Start() |> ignore + let output = ps.StandardOutput.ReadToEnd() + let error = ps.StandardError.ReadToEnd() + do! ps.WaitForExitAsync() + + if not (String.IsNullOrWhiteSpace error) then + failwithf $"In %s{pwd}:\ndotnet %s{args} failed with\n%s{error}" + + return output.Trim() + } + |> Async.AwaitTask + +let mkOptionsFromDesignTimeBuildAux + (fsproj: FileInfo) + (additionalArguments: string) + : Async + = + async { + let targets = + "Restore,ResolveAssemblyReferencesDesignTime,ResolveProjectReferencesDesignTime,ResolvePackageDependenciesDesignTime,FindReferenceAssembliesForReferences,_GenerateCompileDependencyCache,_ComputeNonExistentFileProperty,BeforeBuild,BeforeCompile,CoreCompile" + + let! targetFrameworkJson = + dotnet_msbuild + fsproj.FullName + "--getProperty:TargetFrameworks --getProperty:TargetFramework" + + let targetFramework = + let tf, tfs = + JsonDocument.Parse targetFrameworkJson + |> fun json -> json.RootElement.GetProperty "Properties" + |> fun properties -> + properties.GetProperty("TargetFramework").GetString(), + properties.GetProperty("TargetFrameworks").GetString() + + if not (String.IsNullOrWhiteSpace tf) then + tf + else + tfs.Split ';' |> Array.head + + let version = DateTime.UtcNow.Ticks % 3600L + + let properties = + [ + "/p:Telplin=True" + $"/p:TargetFramework=%s{targetFramework}" + "/p:DesignTimeBuild=True" + "/p:SkipCompilerExecution=True" + "/p:ProvideCommandLineArgs=True" + // See https://github.com/NuGet/Home/issues/13046 + "/p:RestoreUseStaticGraphEvaluation=False" + // Pass in a fake version to avoid skipping the CoreCompile target + $"/p:Version=%i{version}" + ] + |> List.filter (String.IsNullOrWhiteSpace >> not) + |> String.concat " " + + let arguments = + $"/t:%s{targets} %s{properties} --getItem:FscCommandLineArgs %s{additionalArguments} --getItem:ProjectReference --getProperty:OutputType" + + let! json = dotnet_msbuild fsproj.FullName arguments + let jsonDocument = JsonDocument.Parse json + let items = jsonDocument.RootElement.GetProperty "Items" + let properties = jsonDocument.RootElement.GetProperty "Properties" + + let options = + items.GetProperty("FscCommandLineArgs").EnumerateArray() + |> Seq.map (fun arg -> arg.GetProperty("Identity").GetString()) + |> Seq.toArray + + if Array.isEmpty options then + return + failwithf + $"Design time build for %s{fsproj.FullName} failed. CoreCompile was most likely skipped. `dotnet clean` might help here." + else + + let options = mkOptions options + + let projectReferences = + items.GetProperty("ProjectReference").EnumerateArray() + |> Seq.map (fun arg -> arg.GetProperty("FullPath").GetString()) + |> Seq.toArray + + let outputType = properties.GetProperty("OutputType").GetString() + + return + { + ProjectOptions = options + ProjectReferences = projectReferences + OutputType = Some outputType + TargetFramework = Some targetFramework + } + } + +type MsBuildCrackerResolver() = interface ProjectCrackerResolver with - member x.GetProjectOptionsFromProjectFile + member _.GetProjectOptionsFromProjectFile ( isMain, options: CrackerOptions, projectFile ) = - let manager = - match manager with - | Some m -> m - | None -> - let log = new System.IO.StringWriter() - let amo = AnalyzerManagerOptions(LogWriter = log) - let m = AnalyzerManager(amo) - m.SetGlobalProperty("Configuration", options.Configuration) - // m.SetGlobalProperty("TargetFramework", opts.TargetFramework) - for define in options.FableOptions.Define do - m.SetGlobalProperty(define, "true") - - manager <- Some m - m - - // Because BuildAnalyzer works better with .csproj, we first "dress up" the project as if it were a C# one - // and try to adapt the results. If it doesn't work, we try again to analyze the .fsproj directly - let csprojResult = - let csprojFile = - projectFile.Replace(".fsproj", ".fable-temp.csproj") - - if IO.File.Exists(csprojFile) then - None - else - try - IO.File.Copy(projectFile, csprojFile) - - let xmlDocument = XDocument.Load(csprojFile) - - let xmlComment = - XComment( - """This is a temporary file used by Fable to restore dependencies. - If you see this file in your project, you can delete it safely""" - ) - - // An fsproj/csproj should always have a root element - // so it should be safe to add the comment as first child - // of the root element - xmlDocument.Root.AddFirst(xmlComment) - xmlDocument.Save(csprojFile) - - tryGetResult isMain options manager csprojFile - |> Option.map (fun (r: IAnalyzerResult) -> - // Careful, options for .csproj start with / but so do root paths in unix - let reg = Regex(@"^\/[^\/]+?(:?:|$)") - - let comArgs = - r.CompilerArguments - |> Array.map (fun line -> - if reg.IsMatch(line) then - if - line.StartsWith( - "/reference", - StringComparison.Ordinal - ) - then - "-r" + line.Substring(10) - else - "--" + line.Substring(1) - else - line - ) - - let comArgs = - match - r.Properties.TryGetValue("OtherFlags") - with - | false, _ -> comArgs - | true, otherFlags -> - let otherFlags = - otherFlags.Split( - ' ', - StringSplitOptions.RemoveEmptyEntries - ) - - Array.append otherFlags comArgs - - comArgs, r - ) - finally - File.safeDelete csprojFile - - let compilerArgs, result = - csprojResult - |> Option.orElseWith (fun () -> - tryGetResult isMain options manager projectFile - |> Option.map (fun r -> - // result.CompilerArguments doesn't seem to work well in Linux - let comArgs = Regex.Split(r.Command, @"\r?\n") - comArgs, r - ) - ) - |> function - | Some result -> result - // TODO: Get Buildalyzer errors from the log - | None -> - $"Cannot parse {projectFile}" - |> Fable.FableError - |> raise - - let projDir = IO.Path.GetDirectoryName(projectFile) - - let projOpts = - compilerArgs - |> Array.skipWhile (fun line -> not (line.StartsWith('-'))) - |> Array.map (fun f -> - if - f.EndsWith(".fs", StringComparison.Ordinal) - || f.EndsWith(".fsi", StringComparison.Ordinal) - then - if Path.IsPathRooted f then - f - else - Path.Combine(projDir, f) - else - f - ) - - let outputType = - ReadOnlyDictionary.tryFind "OutputType" result.Properties - - { - ProjectOptions = projOpts - ProjectReferences = Seq.toArray result.ProjectReferences - OutputType = outputType - TargetFramework = Some result.TargetFramework - } + let fsproj = FileInfo projectFile + + if not fsproj.Exists then + invalidArg + (nameof fsproj) + $"\"%s{fsproj.FullName}\" does not exist." + + // Bad I know... + let result = + mkOptionsFromDesignTimeBuildAux fsproj "" + |> Async.RunSynchronously + + result diff --git a/src/Fable.Cli/Main.fs b/src/Fable.Cli/Main.fs index c6b1826267..8aa68fff6a 100644 --- a/src/Fable.Cli/Main.fs +++ b/src/Fable.Cli/Main.fs @@ -14,6 +14,7 @@ open Fable.Transforms open Fable.Transforms.State open Fable.Compiler.ProjectCracker open Fable.Compiler.Util +open Fable.Cli.MsBuildCrackerResolver module private Util = type PathResolver with @@ -465,7 +466,7 @@ type ProjectCracked Performance.measure <| fun () -> CrackerOptions(cliArgs) - |> getFullProjectOpts (BuildalyzerCrackerResolver()) + |> getFullProjectOpts (MsBuildCrackerResolver()) // We display "parsed" because "cracked" may not be understood by users Log.always @@ -1200,6 +1201,10 @@ let private checkRunProcess | _, exeFile -> exeFile, runProc.Args if Option.isSome state.Watcher then + printfn "cliArgs.RunProcessEnv: %A" cliArgs.RunProcessEnv + printfn "workingDir: %A" workingDir + printfn "exeFile: %A" exeFile + printfn "args: %A" args Process.startWithEnv cliArgs.RunProcessEnv workingDir exeFile args let runProc = From bac5066df444042122f55cbc76ddfb8206530184 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 29 Jan 2024 13:11:07 +0100 Subject: [PATCH 02/16] Ensure F# files have full paths --- src/Fable.Cli/BuildalyzerCrackerResolver.fs | 38 ++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/BuildalyzerCrackerResolver.fs index 3fae1c4083..baf07071c4 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/BuildalyzerCrackerResolver.fs @@ -14,10 +14,37 @@ open System.Diagnostics open System.Text.Json +let private fsharpFiles = + set + [| + ".fs" + ".fsi" + ".fsx" + |] + +let private isFSharpFile (file: string) = + Set.exists + (fun (ext: string) -> file.EndsWith(ext, StringComparison.Ordinal)) + fsharpFiles + + /// Add additional Fable arguments -let private mkOptions (compilerArgs: string array) : string array = +let private mkOptions + (projectFile: FileInfo) + (compilerArgs: string array) + : string array + = + let arguments = + compilerArgs + |> Array.map (fun (line: string) -> + if not (isFSharpFile line) then + line + else + Path.Combine(projectFile.DirectoryName, line) + ) + [| - yield! compilerArgs + yield! arguments yield "--define:FABLE_COMPILER" yield "--define:FABLE_COMPILER_4" yield "--define:FABLE_COMPILER_JAVASCRIPT" @@ -29,7 +56,10 @@ let private dotnet_msbuild (fsproj: FullPath) (args: string) : Async = backgroundTask { let psi = ProcessStartInfo "dotnet" let pwd = Assembly.GetEntryAssembly().Location |> Path.GetDirectoryName - psi.WorkingDirectory <- "/Users/mmangel" + + psi.WorkingDirectory <- + Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + psi.Arguments <- $"msbuild \"%s{fsproj}\" %s{args}" psi.RedirectStandardOutput <- true psi.RedirectStandardError <- true @@ -111,7 +141,7 @@ let mkOptionsFromDesignTimeBuildAux $"Design time build for %s{fsproj.FullName} failed. CoreCompile was most likely skipped. `dotnet clean` might help here." else - let options = mkOptions options + let options = mkOptions fsproj options let projectReferences = items.GetProperty("ProjectReference").EnumerateArray() From 37b93771b27a609df6cee1126410c6a8440df7a9 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 29 Jan 2024 13:28:20 +0100 Subject: [PATCH 03/16] Correct the passed down defines --- src/Fable.Cli/BuildalyzerCrackerResolver.fs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/BuildalyzerCrackerResolver.fs index baf07071c4..ade3594624 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/BuildalyzerCrackerResolver.fs @@ -31,6 +31,7 @@ let private isFSharpFile (file: string) = /// Add additional Fable arguments let private mkOptions (projectFile: FileInfo) + (additionalDefines: string list) (compilerArgs: string array) : string array = @@ -44,10 +45,8 @@ let private mkOptions ) [| + yield! List.map (sprintf "--define:%s") additionalDefines yield! arguments - yield "--define:FABLE_COMPILER" - yield "--define:FABLE_COMPILER_4" - yield "--define:FABLE_COMPILER_JAVASCRIPT" |] type FullPath = string @@ -80,6 +79,7 @@ let private dotnet_msbuild (fsproj: FullPath) (args: string) : Async = let mkOptionsFromDesignTimeBuildAux (fsproj: FileInfo) + (additionalDefines: string list) (additionalArguments: string) : Async = @@ -141,7 +141,7 @@ let mkOptionsFromDesignTimeBuildAux $"Design time build for %s{fsproj.FullName} failed. CoreCompile was most likely skipped. `dotnet clean` might help here." else - let options = mkOptions fsproj options + let options = mkOptions fsproj additionalDefines options let projectReferences = items.GetProperty("ProjectReference").EnumerateArray() @@ -178,7 +178,10 @@ type MsBuildCrackerResolver() = // Bad I know... let result = - mkOptionsFromDesignTimeBuildAux fsproj "" + mkOptionsFromDesignTimeBuildAux + fsproj + options.FableOptions.Define + "" |> Async.RunSynchronously result From f5e4a53ca3b793aa4749f10d517dab3a4e83ad77 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 29 Jan 2024 13:41:53 +0100 Subject: [PATCH 04/16] Don't warn on NU1608 --- src/Fable.Cli/BuildalyzerCrackerResolver.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/BuildalyzerCrackerResolver.fs index ade3594624..168bba8222 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/BuildalyzerCrackerResolver.fs @@ -123,7 +123,7 @@ let mkOptionsFromDesignTimeBuildAux |> String.concat " " let arguments = - $"/t:%s{targets} %s{properties} --getItem:FscCommandLineArgs %s{additionalArguments} --getItem:ProjectReference --getProperty:OutputType" + $"/t:%s{targets} %s{properties} --getItem:FscCommandLineArgs %s{additionalArguments} --getItem:ProjectReference --getProperty:OutputType -warnAsMessage:NU1608" let! json = dotnet_msbuild fsproj.FullName arguments let jsonDocument = JsonDocument.Parse json From 3b54c252b1b3134579c3a1136fccb4aa6514e282 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 29 Jan 2024 17:06:38 +0100 Subject: [PATCH 05/16] Use /restore instead of /t:Restore --- src/Fable.Cli/BuildalyzerCrackerResolver.fs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/BuildalyzerCrackerResolver.fs index 168bba8222..2f082a1942 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/BuildalyzerCrackerResolver.fs @@ -84,9 +84,6 @@ let mkOptionsFromDesignTimeBuildAux : Async = async { - let targets = - "Restore,ResolveAssemblyReferencesDesignTime,ResolveProjectReferencesDesignTime,ResolvePackageDependenciesDesignTime,FindReferenceAssembliesForReferences,_GenerateCompileDependencyCache,_ComputeNonExistentFileProperty,BeforeBuild,BeforeCompile,CoreCompile" - let! targetFrameworkJson = dotnet_msbuild fsproj.FullName @@ -116,14 +113,22 @@ let mkOptionsFromDesignTimeBuildAux "/p:ProvideCommandLineArgs=True" // See https://github.com/NuGet/Home/issues/13046 "/p:RestoreUseStaticGraphEvaluation=False" + // Avoid restoring with an existing lock file + "/p:RestoreLockedMode=false" + "/p:RestorePackagesWithLockFile=false" + // We trick NuGet into believing there is no lock file create, if it does exist it will try and create it. + " /p:NuGetLockFilePath=Fable.lock" // Pass in a fake version to avoid skipping the CoreCompile target $"/p:Version=%i{version}" ] |> List.filter (String.IsNullOrWhiteSpace >> not) |> String.concat " " + let targets = + "ResolveAssemblyReferencesDesignTime,ResolveProjectReferencesDesignTime,ResolvePackageDependenciesDesignTime,FindReferenceAssembliesForReferences,_GenerateCompileDependencyCache,_ComputeNonExistentFileProperty,BeforeBuild,BeforeCompile,CoreCompile" + let arguments = - $"/t:%s{targets} %s{properties} --getItem:FscCommandLineArgs %s{additionalArguments} --getItem:ProjectReference --getProperty:OutputType -warnAsMessage:NU1608" + $"/restore /t:%s{targets} %s{properties} --getItem:FscCommandLineArgs %s{additionalArguments} --getItem:ProjectReference --getProperty:OutputType -warnAsMessage:NU1608" let! json = dotnet_msbuild fsproj.FullName arguments let jsonDocument = JsonDocument.Parse json From 0160241143ecc75b228bccd2e992c071b36969a7 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 29 Jan 2024 17:42:38 +0100 Subject: [PATCH 06/16] Construct relative path based of identity. --- src/Fable.Cli/BuildalyzerCrackerResolver.fs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/BuildalyzerCrackerResolver.fs index 2f082a1942..d955d8454e 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/BuildalyzerCrackerResolver.fs @@ -150,7 +150,12 @@ let mkOptionsFromDesignTimeBuildAux let projectReferences = items.GetProperty("ProjectReference").EnumerateArray() - |> Seq.map (fun arg -> arg.GetProperty("FullPath").GetString()) + |> Seq.map (fun arg -> + let relativePath = arg.GetProperty("Identity").GetString() + + Path.Combine(fsproj.DirectoryName, relativePath) + |> Path.GetFullPath + ) |> Seq.toArray let outputType = properties.GetProperty("OutputType").GetString() From 5d50909305f7d374ebe1919673d0a49808724ca5 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 29 Jan 2024 18:25:05 +0100 Subject: [PATCH 07/16] Correctly pass configuration and defines to design time build. --- src/Fable.Cli/BuildalyzerCrackerResolver.fs | 36 ++++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/BuildalyzerCrackerResolver.fs index d955d8454e..155ad5d95c 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/BuildalyzerCrackerResolver.fs @@ -31,7 +31,6 @@ let private isFSharpFile (file: string) = /// Add additional Fable arguments let private mkOptions (projectFile: FileInfo) - (additionalDefines: string list) (compilerArgs: string array) : string array = @@ -44,16 +43,13 @@ let private mkOptions Path.Combine(projectFile.DirectoryName, line) ) - [| - yield! List.map (sprintf "--define:%s") additionalDefines - yield! arguments - |] + arguments type FullPath = string let private dotnet_msbuild (fsproj: FullPath) (args: string) : Async = backgroundTask { - let psi = ProcessStartInfo "dotnet" + let psi = ProcessStartInfo "/home/nojaf/.dotnet/dotnet" let pwd = Assembly.GetEntryAssembly().Location |> Path.GetDirectoryName psi.WorkingDirectory <- @@ -70,8 +66,10 @@ let private dotnet_msbuild (fsproj: FullPath) (args: string) : Async = let error = ps.StandardError.ReadToEnd() do! ps.WaitForExitAsync() + let fullCommand = $"dotnet msbuild %s{fsproj} %s{args}" + if not (String.IsNullOrWhiteSpace error) then - failwithf $"In %s{pwd}:\ndotnet %s{args} failed with\n%s{error}" + failwithf $"In %s{pwd}:\n%s{fullCommand}\nfailed with\n%s{error}" return output.Trim() } @@ -79,8 +77,7 @@ let private dotnet_msbuild (fsproj: FullPath) (args: string) : Async = let mkOptionsFromDesignTimeBuildAux (fsproj: FileInfo) - (additionalDefines: string list) - (additionalArguments: string) + (options: CrackerOptions) : Async = async { @@ -102,11 +99,21 @@ let mkOptionsFromDesignTimeBuildAux else tfs.Split ';' |> Array.head + let defines = + options.FableOptions.Define + |> List.map (fun s -> s.Trim()) + // Escaped `;` + |> String.concat "%3B" + let version = DateTime.UtcNow.Ticks % 3600L let properties = [ - "/p:Telplin=True" + "/p:Fable=True" + if not (String.IsNullOrWhiteSpace options.Configuration) then + $"/p:Configuration=%s{options.Configuration}" + if not (String.IsNullOrWhiteSpace defines) then + $"/p:DefineConstants=\"%s{defines}\"" $"/p:TargetFramework=%s{targetFramework}" "/p:DesignTimeBuild=True" "/p:SkipCompilerExecution=True" @@ -128,7 +135,7 @@ let mkOptionsFromDesignTimeBuildAux "ResolveAssemblyReferencesDesignTime,ResolveProjectReferencesDesignTime,ResolvePackageDependenciesDesignTime,FindReferenceAssembliesForReferences,_GenerateCompileDependencyCache,_ComputeNonExistentFileProperty,BeforeBuild,BeforeCompile,CoreCompile" let arguments = - $"/restore /t:%s{targets} %s{properties} --getItem:FscCommandLineArgs %s{additionalArguments} --getItem:ProjectReference --getProperty:OutputType -warnAsMessage:NU1608" + $"/restore /t:%s{targets} %s{properties} --getItem:FscCommandLineArgs --getItem:ProjectReference --getProperty:OutputType -warnAsMessage:NU1608" let! json = dotnet_msbuild fsproj.FullName arguments let jsonDocument = JsonDocument.Parse json @@ -146,7 +153,7 @@ let mkOptionsFromDesignTimeBuildAux $"Design time build for %s{fsproj.FullName} failed. CoreCompile was most likely skipped. `dotnet clean` might help here." else - let options = mkOptions fsproj additionalDefines options + let options = mkOptions fsproj options let projectReferences = items.GetProperty("ProjectReference").EnumerateArray() @@ -188,10 +195,7 @@ type MsBuildCrackerResolver() = // Bad I know... let result = - mkOptionsFromDesignTimeBuildAux - fsproj - options.FableOptions.Define - "" + mkOptionsFromDesignTimeBuildAux fsproj options |> Async.RunSynchronously result From b2169fc833925a554589282b62997625d6995d3a Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 29 Jan 2024 18:27:12 +0100 Subject: [PATCH 08/16] Don't use hard coded dotnet path --- src/Fable.Cli/BuildalyzerCrackerResolver.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/BuildalyzerCrackerResolver.fs index 155ad5d95c..82cebd3a9a 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/BuildalyzerCrackerResolver.fs @@ -49,7 +49,7 @@ type FullPath = string let private dotnet_msbuild (fsproj: FullPath) (args: string) : Async = backgroundTask { - let psi = ProcessStartInfo "/home/nojaf/.dotnet/dotnet" + let psi = ProcessStartInfo "dotnet" let pwd = Assembly.GetEntryAssembly().Location |> Path.GetDirectoryName psi.WorkingDirectory <- From 6a297f14cb3a10aee24d585f46313c6b7cf9cbb0 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 29 Jan 2024 18:37:44 +0100 Subject: [PATCH 09/16] Include configuration define --- src/Fable.Cli/BuildalyzerCrackerResolver.fs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/BuildalyzerCrackerResolver.fs index 82cebd3a9a..8f1204cbd9 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/BuildalyzerCrackerResolver.fs @@ -100,7 +100,13 @@ let mkOptionsFromDesignTimeBuildAux tfs.Split ';' |> Array.head let defines = - options.FableOptions.Define + [ + "TRACE" + if not (String.IsNullOrWhiteSpace options.Configuration) then + options.Configuration.ToUpper() + yield! options.FableOptions.Define + ] + |> List.map (fun s -> s.Trim()) // Escaped `;` |> String.concat "%3B" From 1e024938a7555c86871f11b4d9d5149b643d9967 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 29 Jan 2024 19:55:14 +0100 Subject: [PATCH 10/16] Capture original define constants from fsproj. --- src/Fable.Cli/BuildalyzerCrackerResolver.fs | 29 +++++++++++++++------ 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/BuildalyzerCrackerResolver.fs index 8f1204cbd9..92ab14908d 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/BuildalyzerCrackerResolver.fs @@ -67,6 +67,7 @@ let private dotnet_msbuild (fsproj: FullPath) (args: string) : Async = do! ps.WaitForExitAsync() let fullCommand = $"dotnet msbuild %s{fsproj} %s{args}" + // printfn "%s" fullCommand if not (String.IsNullOrWhiteSpace error) then failwithf $"In %s{pwd}:\n%s{fullCommand}\nfailed with\n%s{error}" @@ -82,28 +83,40 @@ let mkOptionsFromDesignTimeBuildAux = async { let! targetFrameworkJson = + let configuration = + if String.IsNullOrWhiteSpace options.Configuration then + "" + else + $"/p:Configuration=%s{options.Configuration} " + dotnet_msbuild fsproj.FullName - "--getProperty:TargetFrameworks --getProperty:TargetFramework" + $"%s{configuration} --getProperty:TargetFrameworks --getProperty:TargetFramework --getProperty:DefineConstants" - let targetFramework = - let tf, tfs = + let defineConstants, targetFramework = + let properties = JsonDocument.Parse targetFrameworkJson |> fun json -> json.RootElement.GetProperty "Properties" - |> fun properties -> - properties.GetProperty("TargetFramework").GetString(), - properties.GetProperty("TargetFrameworks").GetString() + + let defineConstants = + properties.GetProperty("DefineConstants").GetString().Split(';') + |> Array.filter (fun c -> c <> "DEBUG" || c <> "RELEASE") + + let tf, tfs = + properties.GetProperty("TargetFramework").GetString(), + properties.GetProperty("TargetFrameworks").GetString() if not (String.IsNullOrWhiteSpace tf) then - tf + defineConstants, tf else - tfs.Split ';' |> Array.head + defineConstants, tfs.Split ';' |> Array.head let defines = [ "TRACE" if not (String.IsNullOrWhiteSpace options.Configuration) then options.Configuration.ToUpper() + yield! defineConstants yield! options.FableOptions.Define ] From 31397d09f08d6601c65703366cd5b9301f6a8131 Mon Sep 17 00:00:00 2001 From: Maxime Mangel Date: Mon, 29 Jan 2024 21:35:36 +0100 Subject: [PATCH 11/16] Rename resolver file to MSBuildCrackerResolver --- src/Fable.Cli/Fable.Cli.fsproj | 2 +- ...uildalyzerCrackerResolver.fs => MSBuildCrackerResolver.fs} | 4 ++-- src/Fable.Cli/Main.fs | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename src/Fable.Cli/{BuildalyzerCrackerResolver.fs => MSBuildCrackerResolver.fs} (99%) diff --git a/src/Fable.Cli/Fable.Cli.fsproj b/src/Fable.Cli/Fable.Cli.fsproj index b876112013..63b349587e 100644 --- a/src/Fable.Cli/Fable.Cli.fsproj +++ b/src/Fable.Cli/Fable.Cli.fsproj @@ -131,7 +131,7 @@ - + diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/MSBuildCrackerResolver.fs similarity index 99% rename from src/Fable.Cli/BuildalyzerCrackerResolver.fs rename to src/Fable.Cli/MSBuildCrackerResolver.fs index 92ab14908d..5087fd79f9 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/MSBuildCrackerResolver.fs @@ -1,4 +1,4 @@ -module Fable.Cli.MsBuildCrackerResolver +module Fable.Cli.MSBuildCrackerResolver open System open System.Xml.Linq @@ -195,7 +195,7 @@ let mkOptionsFromDesignTimeBuildAux } } -type MsBuildCrackerResolver() = +type MSBuildCrackerResolver() = interface ProjectCrackerResolver with member _.GetProjectOptionsFromProjectFile diff --git a/src/Fable.Cli/Main.fs b/src/Fable.Cli/Main.fs index 8aa68fff6a..ee34d3a980 100644 --- a/src/Fable.Cli/Main.fs +++ b/src/Fable.Cli/Main.fs @@ -14,7 +14,7 @@ open Fable.Transforms open Fable.Transforms.State open Fable.Compiler.ProjectCracker open Fable.Compiler.Util -open Fable.Cli.MsBuildCrackerResolver +open Fable.Cli.MSBuildCrackerResolver module private Util = type PathResolver with @@ -466,7 +466,7 @@ type ProjectCracked Performance.measure <| fun () -> CrackerOptions(cliArgs) - |> getFullProjectOpts (MsBuildCrackerResolver()) + |> getFullProjectOpts (MSBuildCrackerResolver()) // We display "parsed" because "cracked" may not be understood by users Log.always From b482a0998919a64038e6909376ea5507d0ad675f Mon Sep 17 00:00:00 2001 From: nojaf Date: Fri, 9 Feb 2024 11:13:48 +0100 Subject: [PATCH 12/16] Add NonExistentFile property --- src/Fable.Cli/MSBuildCrackerResolver.fs | 32 ++++++++++++------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Fable.Cli/MSBuildCrackerResolver.fs b/src/Fable.Cli/MSBuildCrackerResolver.fs index 5087fd79f9..4968970c90 100644 --- a/src/Fable.Cli/MSBuildCrackerResolver.fs +++ b/src/Fable.Cli/MSBuildCrackerResolver.fs @@ -28,22 +28,19 @@ let private isFSharpFile (file: string) = fsharpFiles -/// Add additional Fable arguments -let private mkOptions +/// Transform F# files into full paths +let private mkOptionsFullPath (projectFile: FileInfo) (compilerArgs: string array) : string array = - let arguments = - compilerArgs - |> Array.map (fun (line: string) -> - if not (isFSharpFile line) then - line - else - Path.Combine(projectFile.DirectoryName, line) - ) - - arguments + compilerArgs + |> Array.map (fun (line: string) -> + if not (isFSharpFile line) then + line + else + Path.Combine(projectFile.DirectoryName, line) + ) type FullPath = string @@ -124,7 +121,10 @@ let mkOptionsFromDesignTimeBuildAux // Escaped `;` |> String.concat "%3B" - let version = DateTime.UtcNow.Ticks % 3600L + // When CoreCompile does not need a rebuild, MSBuild will skip that target and thus will not populate the FscCommandLineArgs items. + // To overcome this we want to force a design time build, using the NonExistentFile property helps prevent a cache hit. + let nonExistentFile = + Path.Combine("__NonExistentSubDir__", "__NonExistentFile__") let properties = [ @@ -144,8 +144,8 @@ let mkOptionsFromDesignTimeBuildAux "/p:RestorePackagesWithLockFile=false" // We trick NuGet into believing there is no lock file create, if it does exist it will try and create it. " /p:NuGetLockFilePath=Fable.lock" - // Pass in a fake version to avoid skipping the CoreCompile target - $"/p:Version=%i{version}" + // Avoid skipping the CoreCompile target via this property. + $"/p:NonExistentFile=\"%s{nonExistentFile}\"" ] |> List.filter (String.IsNullOrWhiteSpace >> not) |> String.concat " " @@ -172,7 +172,7 @@ let mkOptionsFromDesignTimeBuildAux $"Design time build for %s{fsproj.FullName} failed. CoreCompile was most likely skipped. `dotnet clean` might help here." else - let options = mkOptions fsproj options + let options = mkOptionsFullPath fsproj options let projectReferences = items.GetProperty("ProjectReference").EnumerateArray() From 3758c0a187baf8db9e4fcce7076ad764120ca6c0 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 12 Feb 2024 08:59:03 +0100 Subject: [PATCH 13/16] Pass additional defines as environment variables --- src/Fable.Cli/MSBuildCrackerResolver.fs | 86 +++++++------------------ 1 file changed, 24 insertions(+), 62 deletions(-) diff --git a/src/Fable.Cli/MSBuildCrackerResolver.fs b/src/Fable.Cli/MSBuildCrackerResolver.fs index 4968970c90..2ec63444e8 100644 --- a/src/Fable.Cli/MSBuildCrackerResolver.fs +++ b/src/Fable.Cli/MSBuildCrackerResolver.fs @@ -14,26 +14,14 @@ open System.Diagnostics open System.Text.Json -let private fsharpFiles = - set - [| - ".fs" - ".fsi" - ".fsx" - |] +let private fsharpFiles = set [| ".fs"; ".fsi"; ".fsx" |] let private isFSharpFile (file: string) = - Set.exists - (fun (ext: string) -> file.EndsWith(ext, StringComparison.Ordinal)) - fsharpFiles + Set.exists (fun (ext: string) -> file.EndsWith(ext, StringComparison.Ordinal)) fsharpFiles /// Transform F# files into full paths -let private mkOptionsFullPath - (projectFile: FileInfo) - (compilerArgs: string array) - : string array - = +let private mkOptionsFullPath (projectFile: FileInfo) (compilerArgs: string array) : string array = compilerArgs |> Array.map (fun (line: string) -> if not (isFSharpFile line) then @@ -44,18 +32,22 @@ let private mkOptionsFullPath type FullPath = string -let private dotnet_msbuild (fsproj: FullPath) (args: string) : Async = +let private dotnet_msbuild_with_defines (fsproj: FullPath) (args: string) (defines: string list) : Async = backgroundTask { let psi = ProcessStartInfo "dotnet" let pwd = Assembly.GetEntryAssembly().Location |> Path.GetDirectoryName - psi.WorkingDirectory <- - Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) + psi.WorkingDirectory <- Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) psi.Arguments <- $"msbuild \"%s{fsproj}\" %s{args}" psi.RedirectStandardOutput <- true psi.RedirectStandardError <- true psi.UseShellExecute <- false + + if not (List.isEmpty defines) then + let definesValue = defines |> String.concat ";" + psi.Environment.Add("DefineConstants", definesValue) + use ps = new Process() ps.StartInfo <- psi ps.Start() |> ignore @@ -73,11 +65,10 @@ let private dotnet_msbuild (fsproj: FullPath) (args: string) : Async = } |> Async.AwaitTask -let mkOptionsFromDesignTimeBuildAux - (fsproj: FileInfo) - (options: CrackerOptions) - : Async - = +let private dotnet_msbuild (fsproj: FullPath) (args: string) : Async = + dotnet_msbuild_with_defines fsproj args List.empty + +let mkOptionsFromDesignTimeBuildAux (fsproj: FileInfo) (options: CrackerOptions) : Async = async { let! targetFrameworkJson = let configuration = @@ -88,51 +79,32 @@ let mkOptionsFromDesignTimeBuildAux dotnet_msbuild fsproj.FullName - $"%s{configuration} --getProperty:TargetFrameworks --getProperty:TargetFramework --getProperty:DefineConstants" + $"%s{configuration} --getProperty:TargetFrameworks --getProperty:TargetFramework" - let defineConstants, targetFramework = + let targetFramework = let properties = JsonDocument.Parse targetFrameworkJson |> fun json -> json.RootElement.GetProperty "Properties" - let defineConstants = - properties.GetProperty("DefineConstants").GetString().Split(';') - |> Array.filter (fun c -> c <> "DEBUG" || c <> "RELEASE") - let tf, tfs = properties.GetProperty("TargetFramework").GetString(), properties.GetProperty("TargetFrameworks").GetString() if not (String.IsNullOrWhiteSpace tf) then - defineConstants, tf + tf else - defineConstants, tfs.Split ';' |> Array.head - - let defines = - [ - "TRACE" - if not (String.IsNullOrWhiteSpace options.Configuration) then - options.Configuration.ToUpper() - yield! defineConstants - yield! options.FableOptions.Define - ] + tfs.Split ';' |> Array.head - |> List.map (fun s -> s.Trim()) - // Escaped `;` - |> String.concat "%3B" // When CoreCompile does not need a rebuild, MSBuild will skip that target and thus will not populate the FscCommandLineArgs items. // To overcome this we want to force a design time build, using the NonExistentFile property helps prevent a cache hit. - let nonExistentFile = - Path.Combine("__NonExistentSubDir__", "__NonExistentFile__") + let nonExistentFile = Path.Combine("__NonExistentSubDir__", "__NonExistentFile__") let properties = [ "/p:Fable=True" if not (String.IsNullOrWhiteSpace options.Configuration) then $"/p:Configuration=%s{options.Configuration}" - if not (String.IsNullOrWhiteSpace defines) then - $"/p:DefineConstants=\"%s{defines}\"" $"/p:TargetFramework=%s{targetFramework}" "/p:DesignTimeBuild=True" "/p:SkipCompilerExecution=True" @@ -156,7 +128,7 @@ let mkOptionsFromDesignTimeBuildAux let arguments = $"/restore /t:%s{targets} %s{properties} --getItem:FscCommandLineArgs --getItem:ProjectReference --getProperty:OutputType -warnAsMessage:NU1608" - let! json = dotnet_msbuild fsproj.FullName arguments + let! json = dotnet_msbuild_with_defines fsproj.FullName arguments options.FableOptions.Define let jsonDocument = JsonDocument.Parse json let items = jsonDocument.RootElement.GetProperty "Items" let properties = jsonDocument.RootElement.GetProperty "Properties" @@ -179,8 +151,7 @@ let mkOptionsFromDesignTimeBuildAux |> Seq.map (fun arg -> let relativePath = arg.GetProperty("Identity").GetString() - Path.Combine(fsproj.DirectoryName, relativePath) - |> Path.GetFullPath + Path.Combine(fsproj.DirectoryName, relativePath) |> Path.GetFullPath ) |> Seq.toArray @@ -198,23 +169,14 @@ let mkOptionsFromDesignTimeBuildAux type MSBuildCrackerResolver() = interface ProjectCrackerResolver with - member _.GetProjectOptionsFromProjectFile - ( - isMain, - options: CrackerOptions, - projectFile - ) - = + member _.GetProjectOptionsFromProjectFile(isMain, options: CrackerOptions, projectFile) = let fsproj = FileInfo projectFile if not fsproj.Exists then - invalidArg - (nameof fsproj) - $"\"%s{fsproj.FullName}\" does not exist." + invalidArg (nameof fsproj) $"\"%s{fsproj.FullName}\" does not exist." // Bad I know... let result = - mkOptionsFromDesignTimeBuildAux fsproj options - |> Async.RunSynchronously + mkOptionsFromDesignTimeBuildAux fsproj options |> Async.RunSynchronously result From bba06085cecfa863c0a3a82900e4b4ec5891c6b5 Mon Sep 17 00:00:00 2001 From: nojaf Date: Mon, 12 Feb 2024 17:25:43 +0100 Subject: [PATCH 14/16] Add test flag for different cracker resolver --- src/Fable.Cli/Entry.fs | 4 +++- src/Fable.Cli/Main.fs | 27 +++++++++++++++++---------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/Fable.Cli/Entry.fs b/src/Fable.Cli/Entry.fs index dd02f12fc8..039d82a12c 100644 --- a/src/Fable.Cli/Entry.fs +++ b/src/Fable.Cli/Entry.fs @@ -123,6 +123,7 @@ let knownCliArgs () = [ "--trimRootModule" ], [] [ "--fableLib" ], [] [ "--replace" ], [] + [ "--test:MSBuildCracker" ], [] ] let printKnownCliArgs () = @@ -268,6 +269,7 @@ type Runner = args.Value("--precompiledLib") |> Option.map normalizeAbsolutePath let fableLib = args.Value "--fableLib" |> Option.map Path.normalizePath + let useMSBuildForCracking = args.FlagOr("--test:MSBuildCracker", false) do! match watch, outDir, fableLib with @@ -383,7 +385,7 @@ type Runner = None let startCompilation () = - State.Create(cliArgs, ?watchDelay = watchDelay) + State.Create(cliArgs, ?watchDelay = watchDelay, useMSBuildForCracking = useMSBuildForCracking) |> startCompilation |> Async.RunSynchronously diff --git a/src/Fable.Cli/Main.fs b/src/Fable.Cli/Main.fs index e17dce408f..c29e40ddf1 100644 --- a/src/Fable.Cli/Main.fs +++ b/src/Fable.Cli/Main.fs @@ -367,19 +367,20 @@ type ProjectCracked(cliArgs: CliArgs, crackerResponse: CrackerResponse, sourceFi member _.MapSourceFiles(f) = ProjectCracked(cliArgs, crackerResponse, Array.map f sourceFiles) - static member Init(cliArgs: CliArgs, ?evaluateOnly: bool) = + static member Init(cliArgs: CliArgs, useMSBuildForCracking, ?evaluateOnly: bool) = let evaluateOnly = defaultArg evaluateOnly false Log.always $"Parsing {cliArgs.ProjectFileAsRelativePath}..." let result, ms = Performance.measure <| fun () -> - CrackerOptions(cliArgs, evaluateOnly) - |> getFullProjectOpts (MSBuildCrackerResolver()) - // TODO: Should we have a switch to support both resolvers? - // or should we go with the MSBuild one and remove the other one? - // CrackerOptions(cliArgs, evaluateOnly) - // |> getFullProjectOpts (BuildalyzerCrackerResolver()) + let resolver: ProjectCrackerResolver = + if useMSBuildForCracking then + MSBuildCrackerResolver() + else + BuildalyzerCrackerResolver() + + CrackerOptions(cliArgs, evaluateOnly) |> getFullProjectOpts resolver // We display "parsed" because "cracked" may not be understood by users Log.always @@ -806,6 +807,7 @@ type State = Watcher: Watcher option SilentCompilation: bool RecompileAllFiles: bool + UseMSBuildForCracking: bool } member this.TriggeredByDependency(path: string, changes: ISet) = @@ -831,7 +833,7 @@ type State = this.DeduplicateDic.GetOrAdd(importDir, (fun _ -> set this.DeduplicateDic.Values |> addTargetDir)) } - static member Create(cliArgs, ?watchDelay, ?recompileAllFiles) = + static member Create(cliArgs, ?watchDelay, ?recompileAllFiles, ?useMSBuildForCracking) = { CliArgs = cliArgs ProjectCrackedAndFableCompiler = None @@ -841,6 +843,7 @@ type State = PendingFiles = [||] SilentCompilation = false RecompileAllFiles = defaultArg recompileAllFiles false + UseMSBuildForCracking = defaultArg useMSBuildForCracking false } let private getFilesToCompile @@ -1002,7 +1005,7 @@ let private compilationCycle (state: State) (changes: ISet) = let projCracked, fableCompiler, filesToCompile = match state.ProjectCrackedAndFableCompiler with | None -> - let projCracked = ProjectCracked.Init(cliArgs) + let projCracked = ProjectCracked.Init(cliArgs, state.UseMSBuildForCracking) projCracked, None, projCracked.SourceFilePaths | Some(projCracked, fableCompiler) -> @@ -1014,7 +1017,11 @@ let private compilationCycle (state: State) (changes: ISet) = let oldProjCracked = projCracked let newProjCracked = - ProjectCracked.Init({ cliArgs with NoCache = true }, evaluateOnly = true) + ProjectCracked.Init( + { cliArgs with NoCache = true }, + state.UseMSBuildForCracking, + evaluateOnly = true + ) // If only source files have changed, keep the project checker to speed up recompilation let fableCompiler = From 0c6d06884da8e84e8f9e2a38bc258b8ea2640bd0 Mon Sep 17 00:00:00 2001 From: Maxime Mangel Date: Mon, 12 Feb 2024 21:26:12 +0100 Subject: [PATCH 15/16] Remove debug code --- src/Fable.Cli/BuildalyzerCrackerResolver.fs | 2 +- src/Fable.Cli/Main.fs | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Fable.Cli/BuildalyzerCrackerResolver.fs b/src/Fable.Cli/BuildalyzerCrackerResolver.fs index 57e1a4dd2d..c5eca504c5 100644 --- a/src/Fable.Cli/BuildalyzerCrackerResolver.fs +++ b/src/Fable.Cli/BuildalyzerCrackerResolver.fs @@ -73,7 +73,7 @@ type BuildalyzerCrackerResolver() = let xmlComment = XComment( """This is a temporary file used by Fable to restore dependencies. - If you see this file in your project, you can delete it safely""" +If you see this file in your project, you can delete it safely""" ) // An fsproj/csproj should always have a root element diff --git a/src/Fable.Cli/Main.fs b/src/Fable.Cli/Main.fs index c29e40ddf1..7bffc1e32d 100644 --- a/src/Fable.Cli/Main.fs +++ b/src/Fable.Cli/Main.fs @@ -979,10 +979,6 @@ let private checkRunProcess (state: State) (projCracked: ProjectCracked) (compil | _, exeFile -> exeFile, runProc.Args if Option.isSome state.Watcher then - printfn "cliArgs.RunProcessEnv: %A" cliArgs.RunProcessEnv - printfn "workingDir: %A" workingDir - printfn "exeFile: %A" exeFile - printfn "args: %A" args Process.startWithEnv cliArgs.RunProcessEnv workingDir exeFile args let runProc = From 100bfc4f8524d186fbeb22bebfaa94d54d3b1e69 Mon Sep 17 00:00:00 2001 From: Maxime Mangel Date: Mon, 12 Feb 2024 21:31:16 +0100 Subject: [PATCH 16/16] Update changelog --- src/Fable.Cli/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md index 76491ea2c1..442913ea22 100644 --- a/src/Fable.Cli/CHANGELOG.md +++ b/src/Fable.Cli/CHANGELOG.md @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `Result.ToArray` * `Result.ToList` * `Result.ToOption` +* [GH-3721](https://github.com/fable-compiler/Fable/pull/3721) Add `--test:MSBuildCracker` flag allowing to use the experimental MSBuildCracker (by @nojaf) #### JavaScript