From e8580e3f30da56377cf7bd73d2e5c76832ea132e Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Thu, 16 Nov 2017 16:15:38 -0800 Subject: [PATCH 1/7] For FSI on coreclr use ResolutionEnvironment.CompilationAndEvaluation just like on desktop --- src/fsharp/CompileOps.fs | 8 +------- src/fsharp/fsi/fsi.fs | 2 -- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/fsharp/CompileOps.fs b/src/fsharp/CompileOps.fs index 0d35f05dc1c..a1c83c4bb00 100644 --- a/src/fsharp/CompileOps.fs +++ b/src/fsharp/CompileOps.fs @@ -5065,13 +5065,7 @@ module private ScriptPreprocessClosure = match codeContext with | CodeContext.Editing -> ResolutionEnvironment.EditingOrCompilation true | CodeContext.Compilation -> ResolutionEnvironment.EditingOrCompilation false - | CodeContext.CompilationAndEvaluation -> -#if FSI_TODO_NETCORE - // "CompilationAndEvaluation" assembly resolution for F# Interactive is not yet properly figured out on .NET Core - ResolutionEnvironment.EditingOrCompilation false -#else - ResolutionEnvironment.CompilationAndEvaluation -#endif + | CodeContext.CompilationAndEvaluation -> ResolutionEnvironment.CompilationAndEvaluation tcConfigB.framework <- false tcConfigB.useSimpleResolution <- useSimpleResolution // Indicates that there are some references not in BasicReferencesForScriptLoadClosure which should diff --git a/src/fsharp/fsi/fsi.fs b/src/fsharp/fsi/fsi.fs index c5e3d54509b..f348e3884ad 100644 --- a/src/fsharp/fsi/fsi.fs +++ b/src/fsharp/fsi/fsi.fs @@ -2464,8 +2464,6 @@ type FsiEvaluationSession (fsi: FsiEvaluationSessionHostConfig, argv:string[], i do tcConfigB.useFsiAuxLib <- fsi.UseFsiAuxLib #if FSI_TODO_NETCORE - // "CompilationAndEvaluation" assembly resolution for F# Interactive is not yet properly figured out on .NET Core - do tcConfigB.resolutionEnvironment <- ResolutionEnvironment.EditingOrCompilation false do tcConfigB.useSimpleResolution <- true do SetTargetProfile tcConfigB "netcore" // always assume System.Runtime codegen #endif From 5e17e3111f88b1a1f025ac54c8147052e12f347f Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Fri, 17 Nov 2017 11:08:57 -0800 Subject: [PATCH 2/7] Trim fsi from buildtasks dll --- .../FSharp.Build/FSharp.Build.fsproj | 2 - .../FSharp.Build-proto.fsproj | 6 -- .../CreateFSharpManifestResourceName.fsi | 9 --- src/fsharp/FSharp.Build/FSharp.Build.fsproj | 2 - src/fsharp/FSharp.Build/Fsc.fs | 72 +++++++++---------- src/fsharp/FSharp.Build/Fsc.fsi | 65 ----------------- 6 files changed, 35 insertions(+), 121 deletions(-) delete mode 100644 src/fsharp/FSharp.Build/CreateFSharpManifestResourceName.fsi delete mode 100644 src/fsharp/FSharp.Build/Fsc.fsi diff --git a/src/buildfromsource/FSharp.Build/FSharp.Build.fsproj b/src/buildfromsource/FSharp.Build/FSharp.Build.fsproj index 42febe9c5a5..95b158c905f 100644 --- a/src/buildfromsource/FSharp.Build/FSharp.Build.fsproj +++ b/src/buildfromsource/FSharp.Build/FSharp.Build.fsproj @@ -21,12 +21,10 @@ - - Microsoft.FSharp.Targets diff --git a/src/fsharp/FSharp.Build-proto/FSharp.Build-proto.fsproj b/src/fsharp/FSharp.Build-proto/FSharp.Build-proto.fsproj index a6505441b5b..b3e5a7c19d4 100644 --- a/src/fsharp/FSharp.Build-proto/FSharp.Build-proto.fsproj +++ b/src/fsharp/FSharp.Build-proto/FSharp.Build-proto.fsproj @@ -22,15 +22,9 @@ CompilerLocationUtils.fs - - CreateFSharpManifestResourceName.fsi - CreateFSharpManifestResourceName.fs - - Fsc.fsi - Fsc.fs diff --git a/src/fsharp/FSharp.Build/CreateFSharpManifestResourceName.fsi b/src/fsharp/FSharp.Build/CreateFSharpManifestResourceName.fsi deleted file mode 100644 index 549f1ab8366..00000000000 --- a/src/fsharp/FSharp.Build/CreateFSharpManifestResourceName.fsi +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -namespace Microsoft.FSharp.Build - -[] -type CreateFSharpManifestResourceName = - inherit Microsoft.Build.Tasks.CreateCSharpManifestResourceName - public new : unit -> CreateFSharpManifestResourceName - member UseStandardResourceNames : bool with get,set \ No newline at end of file diff --git a/src/fsharp/FSharp.Build/FSharp.Build.fsproj b/src/fsharp/FSharp.Build/FSharp.Build.fsproj index 5c8982e34c4..e15895c29cf 100644 --- a/src/fsharp/FSharp.Build/FSharp.Build.fsproj +++ b/src/fsharp/FSharp.Build/FSharp.Build.fsproj @@ -31,12 +31,10 @@ - - Microsoft.FSharp.Targets diff --git a/src/fsharp/FSharp.Build/Fsc.fs b/src/fsharp/FSharp.Build/Fsc.fs index 2d1bc51cea7..e64bd0f289c 100644 --- a/src/fsharp/FSharp.Build/Fsc.fs +++ b/src/fsharp/FSharp.Build/Fsc.fs @@ -3,17 +3,17 @@ namespace Microsoft.FSharp.Build open System -open System.Text open System.Diagnostics.CodeAnalysis +open System.Globalization open System.IO open System.Reflection +open System.Text open Microsoft.Build.Framework open Microsoft.Build.Utilities open Internal.Utilities [] [] - [] do() @@ -32,15 +32,15 @@ type FscCommandLineBuilder () = let builder = new CommandLineBuilder() let mutable args = [] // in reverse order let mutable srcs = [] // in reverse order + /// Return a list of the arguments (with no quoting for the cmd.exe shell) - member x.CapturedArguments() = - List.rev args + member x.CapturedArguments() = List.rev args + /// Return a list of the sources (with no quoting for the cmd.exe shell) - member x.CapturedFilenames() = - List.rev srcs + member x.CapturedFilenames() = List.rev srcs + /// Return a full command line (with quoting for the cmd.exe shell) - override x.ToString() = - builder.ToString() + override x.ToString() = builder.ToString() member x.AppendFileNamesIfNotNull(filenames:ITaskItem array, sep:string) = builder.AppendFileNamesIfNotNull(filenames, sep) @@ -120,8 +120,12 @@ type FscCommandLineBuilder () = //The goal is to have the most common/important flags available via the Fsc class, and the //rest can be "backdoored" through the .OtherFlags property. -type [] Fsc() as this = - inherit ToolTask() +type + [] + Fsc () as this = + + inherit ToolTask () + let mutable baseAddress : string = null let mutable capturedArguments : string list = [] // list of individual args, to pass to HostObject Compile() let mutable capturedFilenames : string list = [] // list of individual source filenames, to pass to HostObject Compile() @@ -178,15 +182,18 @@ type [ null with e-> false - do if not runningOnMono then - typeof.InvokeMember("YieldDuringToolExecution",(BindingFlags.Instance ||| BindingFlags.SetProperty ||| BindingFlags.Public),null,this,[| box true |]) |> ignore -#else - do this.YieldDuringToolExecution <- true // See bug 6483; this makes parallel build faster, and is fine to set unconditionally -#endif + // So we only set it if available (to avoid a compile-time dependency). + do + let runningOnMono = try System.Type.GetType("Mono.Runtime") <> null with e -> false + if not runningOnMono then + typeof.InvokeMember("YieldDuringToolExecution", + (BindingFlags.Instance ||| BindingFlags.SetProperty ||| BindingFlags.Public), + null, + this, + [| box true |], + CultureInfo.CurrentCulture) |> ignore let generateCommandLineBuilder () = let builder = new FscCommandLineBuilder() @@ -247,14 +254,14 @@ type [ "x86" | "X64" , _, _ -> "x64" | "ITANIUM", _, _ -> "Itanium" - | _ -> null) + | _ -> null) // Resources if resources <> null then for item in resources do match useStandardResourceNames with | true -> builder.AppendSwitchIfNotNull("--resource:", item.ItemSpec, [|item.GetMetadata("LogicalName"); item.GetMetadata("Access")|]) | false -> builder.AppendSwitchIfNotNull("--resource:", item.ItemSpec) - + // VersionFile builder.AppendSwitchIfNotNull("--versionfile:", versionFile) // References @@ -266,7 +273,7 @@ type [ null | _ -> referencePath.Split([|';'; ','|], StringSplitOptions.RemoveEmptyEntries) - + builder.AppendSwitchIfNotNull("--lib:", referencePathArray, ",") // TargetType builder.AppendSwitchIfNotNull("--target:", @@ -315,7 +322,7 @@ type [: Specify the codepage to use when opening source files member fsc.CodePage @@ -369,7 +376,7 @@ type [: Do not report the given specific warning. member fsc.DisabledWarnings with get() = disabledWarnings - and set(a) = disabledWarnings <- a + and set(a) = disabledWarnings <- a // --define : Define the given conditional compilation symbol. member fsc.DefineConstants @@ -478,7 +485,7 @@ type [: member fsc.VersionFile with get() = versionFile @@ -604,15 +612,9 @@ type [ base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands) | _ -> let sources = sources|>Array.map(fun i->i.ItemSpec) -#if FX_NO_CONVERTER + let baseCallDelegate = new Func(fun () -> fsc.BaseExecuteTool(pathToTool, responseFileCommands, commandLineCommands) ) -#else - let baseCall = fun (dummy : int) -> fsc.BaseExecuteTool(pathToTool, responseFileCommands, commandLineCommands) - // We are using a Converter rather than a "unit->int" because it is too hard to - // figure out how to pass an F# function object via reflection. - let baseCallDelegate = new System.Converter(baseCall) -#endif - try + try let ret = (host.GetType()).InvokeMember("Compile", BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.InvokeMethod ||| BindingFlags.Instance, null, host, [| baseCallDelegate; box (capturedArguments |> List.toArray); box (capturedFilenames |> List.toArray) |], @@ -651,7 +653,3 @@ type [] - do() diff --git a/src/fsharp/FSharp.Build/Fsc.fsi b/src/fsharp/FSharp.Build/Fsc.fsi deleted file mode 100644 index e05d4f9e69e..00000000000 --- a/src/fsharp/FSharp.Build/Fsc.fsi +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - -/// This namespace contains. MSBuild tasks for the FsYacc and FsLex tools. -namespace Microsoft.FSharp.Build -type Fsc = class - inherit Microsoft.Build.Utilities.ToolTask - new : unit -> Fsc - override GenerateCommandLineCommands : unit -> System.String - override GenerateFullPathToTool : unit -> System.String - override ToolName : System.String - override StandardErrorEncoding : System.Text.Encoding - override StandardOutputEncoding : System.Text.Encoding - - member internal InternalGenerateFullPathToTool : unit -> System.String - member internal InternalGenerateCommandLineCommands : unit -> System.String - member internal InternalGenerateResponseFileCommands : unit -> System.String - member internal InternalExecuteTool : string * string * string -> int - member internal GetCapturedArguments : unit -> string[] - member BaseAddress : string with get,set - member CodePage : string with get,set - member CommandLineArgs : Microsoft.Build.Framework.ITaskItem [] with get,set - member DebugSymbols : bool with get,set - member DebugType : string with get,set - member DefineConstants : Microsoft.Build.Framework.ITaskItem [] with get,set - member DelaySign : bool with get,set - member DisabledWarnings : string with get,set - member DocumentationFile : string with get,set - member DotnetFscCompilerPath : string with get,set - member Embed : string with get,set - member EmbedAllSources : bool with get,set - member GenerateInterfaceFile : string with get,set - member HighEntropyVA : bool with get,set - member KeyFile : string with get,set - member LCID : string with get,set - member NoFramework : bool with get,set - member Optimize : bool with get,set - member OtherFlags : string with get,set - member OutputAssembly : string with get,set - member PdbFile : string with get,set - member Platform : string with get,set - member Prefer32Bit : bool with get,set - member PreferredUILang : string with get,set - member ProvideCommandLineArgs : bool with get,set - member PublicSign : bool with get,set - member VersionFile : string with get,set - member References : Microsoft.Build.Framework.ITaskItem [] with get,set - member ReferencePath : string with get,set - member Resources : Microsoft.Build.Framework.ITaskItem [] with get,set - member SkipCompilerExecution : bool with get,set - member SourceLink : string with get,set - member Sources : Microsoft.Build.Framework.ITaskItem [] with get,set - member SubsystemVersion : string with get,set - member Tailcalls : bool with get,set - member TargetType : string with get,set - member ToolPath : string with get,set - member TargetProfile : string with get,set - member TreatWarningsAsErrors : bool with get,set - member UseStandardResourceNames : bool with get,set - member Utf8Output : bool with get,set - member VisualStudioStyleErrors : bool with get,set - member WarningLevel : string with get,set - member WarningsAsErrors : string with get,set - member Win32ResourceFile : string with get,set - member Win32ManifestFile : string with get,set - end From 7492d7744bb4d77b268269c372bdca77d9d7a966 Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Fri, 17 Nov 2017 15:49:54 -0800 Subject: [PATCH 3/7] Put back converter code --- src/fsharp/FSharp.Build/Fsc.fs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/fsharp/FSharp.Build/Fsc.fs b/src/fsharp/FSharp.Build/Fsc.fs index e64bd0f289c..e9950aec862 100644 --- a/src/fsharp/FSharp.Build/Fsc.fs +++ b/src/fsharp/FSharp.Build/Fsc.fs @@ -613,7 +613,14 @@ type | _ -> let sources = sources|>Array.map(fun i->i.ItemSpec) +#if FX_NO_CONVERTER let baseCallDelegate = new Func(fun () -> fsc.BaseExecuteTool(pathToTool, responseFileCommands, commandLineCommands) ) +#else + let baseCall = fun (dummy : int) -> fsc.BaseExecuteTool(pathToTool, responseFileCommands, commandLineCommands) + // We are using a Converter rather than a "unit->int" because it is too hard to + // figure out how to pass an F# function object via reflection. + let baseCallDelegate = new System.Converter(baseCall) +#endif try let ret = (host.GetType()).InvokeMember("Compile", BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.InvokeMethod ||| BindingFlags.Instance, null, host, From 06b25e71f8ad64b4d600eddcc6d86a3cee11ef77 Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Fri, 17 Nov 2017 15:53:50 -0800 Subject: [PATCH 4/7] We don't support Itanium any longer --- src/fsharp/FSharp.Build/Fsc.fs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/fsharp/FSharp.Build/Fsc.fs b/src/fsharp/FSharp.Build/Fsc.fs index e9950aec862..48fd613fef2 100644 --- a/src/fsharp/FSharp.Build/Fsc.fs +++ b/src/fsharp/FSharp.Build/Fsc.fs @@ -253,7 +253,6 @@ type | "ANYCPU", _, _ -> "anycpu" | "X86" , _, _ -> "x86" | "X64" , _, _ -> "x64" - | "ITANIUM", _, _ -> "Itanium" | _ -> null) // Resources if resources <> null then @@ -456,7 +455,6 @@ type // --platform : Limit which platforms this code can run on: // x86 // x64 - // Itanium // anycpu // anycpu32bitpreferred member fsc.Platform From 2801d4f4ea4322748e083f078844b41beb819173 Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Sat, 18 Nov 2017 00:48:11 -0800 Subject: [PATCH 5/7] Remove Itanium testcase --- vsintegration/tests/unittests/Tests.Build.fs | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/vsintegration/tests/unittests/Tests.Build.fs b/vsintegration/tests/unittests/Tests.Build.fs index e5bbddc166c..22faaef01a8 100644 --- a/vsintegration/tests/unittests/Tests.Build.fs +++ b/vsintegration/tests/unittests/Tests.Build.fs @@ -336,21 +336,6 @@ type Build() = "--highentropyva-" + Environment.NewLine) cmd - [] - member public this.TestPlatform2() = - let tool = new Microsoft.FSharp.Build.Fsc() - tool.Platform <- "itanium" - AssertEqual "itanium" tool.Platform - let cmd = tool.InternalGenerateResponseFileCommands() - printfn "cmd=\"%s\"" cmd - AssertEqual ("--optimize+" + Environment.NewLine + - "--platform:Itanium" + Environment.NewLine + - "--warnaserror:76" + Environment.NewLine + - "--fullpaths" + Environment.NewLine + - "--flaterrors" + Environment.NewLine + - "--highentropyva-" + Environment.NewLine) - cmd - [] member public this.TestPlatform3() = let tool = new Microsoft.FSharp.Build.Fsc() From 96f7b839fd317bc2d3c8a232d7c6455b9356560a Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Sat, 18 Nov 2017 15:32:13 -0800 Subject: [PATCH 6/7] Separate FSc and FSharpCommandLineBuilder, feedback --- .../FSharp.Build/FSharp.Build.fsproj | 1 + .../FSharp.Build-proto.fsproj | 5 +- src/fsharp/FSharp.Build/FSharp.Build.fsproj | 7 +- .../FSharp.Build/FSharpCommandLineBuilder.fs | 110 ++++++++++ src/fsharp/FSharp.Build/Fsc.fs | 188 ++++-------------- 5 files changed, 154 insertions(+), 157 deletions(-) create mode 100644 src/fsharp/FSharp.Build/FSharpCommandLineBuilder.fs diff --git a/src/buildfromsource/FSharp.Build/FSharp.Build.fsproj b/src/buildfromsource/FSharp.Build/FSharp.Build.fsproj index 95b158c905f..0bbcd502150 100644 --- a/src/buildfromsource/FSharp.Build/FSharp.Build.fsproj +++ b/src/buildfromsource/FSharp.Build/FSharp.Build.fsproj @@ -21,6 +21,7 @@ + diff --git a/src/fsharp/FSharp.Build-proto/FSharp.Build-proto.fsproj b/src/fsharp/FSharp.Build-proto/FSharp.Build-proto.fsproj index b3e5a7c19d4..e7ccc386e61 100644 --- a/src/fsharp/FSharp.Build-proto/FSharp.Build-proto.fsproj +++ b/src/fsharp/FSharp.Build-proto/FSharp.Build-proto.fsproj @@ -25,7 +25,10 @@ CreateFSharpManifestResourceName.fs - + + FSharpCommandLineBuilder.fs + + Fsc.fs diff --git a/src/fsharp/FSharp.Build/FSharp.Build.fsproj b/src/fsharp/FSharp.Build/FSharp.Build.fsproj index e15895c29cf..5bc7c9f75bc 100644 --- a/src/fsharp/FSharp.Build/FSharp.Build.fsproj +++ b/src/fsharp/FSharp.Build/FSharp.Build.fsproj @@ -20,17 +20,12 @@ false false - - - - - - + diff --git a/src/fsharp/FSharp.Build/FSharpCommandLineBuilder.fs b/src/fsharp/FSharp.Build/FSharpCommandLineBuilder.fs new file mode 100644 index 00000000000..23b15513d9f --- /dev/null +++ b/src/fsharp/FSharp.Build/FSharpCommandLineBuilder.fs @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace Microsoft.FSharp.Build + +open System +open System.Text +open Microsoft.Build.Framework +open Microsoft.Build.Utilities +open Internal.Utilities + +[] +[] +do() + +//#if FX_RESHAPED_REFLECTION +//open Microsoft.FSharp.Core.ReflectionAdapters +//#endif + +type FSharpCommandLineBuilder () = + + // In addition to generating a command-line that will be handed to cmd.exe, we also generate + // an array of individual arguments. The former needs to be quoted (and cmd.exe will strip the + // quotes while parsing), whereas the latter is not. See bug 4357 for background; this helper + // class gets us out of the business of unparsing-then-reparsing arguments. + + let builder = new CommandLineBuilder() + let mutable args = [] // in reverse order + let mutable srcs = [] // in reverse order + + /// Return a list of the arguments (with no quoting for the cmd.exe shell) + member x.CapturedArguments() = List.rev args + + /// Return a list of the sources (with no quoting for the cmd.exe shell) + member x.CapturedFilenames() = List.rev srcs + + /// Return a full command line (with quoting for the cmd.exe shell) + override x.ToString() = builder.ToString() + + member x.AppendFileNamesIfNotNull(filenames:ITaskItem array, sep:string) = + builder.AppendFileNamesIfNotNull(filenames, sep) + // do not update "args", not used + for item in filenames do + let tmp = new CommandLineBuilder() + tmp.AppendSwitchUnquotedIfNotNull("", item.ItemSpec) // we don't want to quote the filename, this is a way to get that + let s = tmp.ToString() + if s <> String.Empty then + srcs <- tmp.ToString() :: srcs + + member x.AppendSwitchIfNotNull(switch:string, values:string array, sep:string) = + builder.AppendSwitchIfNotNull(switch, values, sep) + let tmp = new CommandLineBuilder() + tmp.AppendSwitchUnquotedIfNotNull(switch, values, sep) + let s = tmp.ToString() + if s <> String.Empty then + args <- s :: args + + member x.AppendSwitchIfNotNull(switch:string, value:string, ?metadataNames:string array) = + let metadataNames = defaultArg metadataNames [||] + builder.AppendSwitchIfNotNull(switch, value) + let tmp = new CommandLineBuilder() + tmp.AppendSwitchUnquotedIfNotNull(switch, value) + let providedMetaData = + metadataNames + |> Array.filter (String.IsNullOrWhiteSpace >> not) + if providedMetaData.Length > 0 then + tmp.AppendTextUnquoted "," + tmp.AppendTextUnquoted (providedMetaData|> String.concat ",") + let s = tmp.ToString() + if s <> String.Empty then + args <- s :: args + + member x.AppendSwitchUnquotedIfNotNull(switch:string, value:string) = + assert(switch = "") // we only call this method for "OtherFlags" + // Unfortunately we still need to mimic what cmd.exe does, but only for "OtherFlags". + let ParseCommandLineArgs(commandLine:string) = // returns list in reverse order + let mutable args = [] + let mutable i = 0 // index into commandLine + let len = commandLine.Length + while i < len do + // skip whitespace + while i < len && System.Char.IsWhiteSpace(commandLine, i) do + i <- i + 1 + if i < len then + // parse an argument + let sb = new StringBuilder() + let mutable finished = false + let mutable insideQuote = false + while i < len && not finished do + match commandLine.[i] with + | '"' -> insideQuote <- not insideQuote; i <- i + 1 + | c when not insideQuote && System.Char.IsWhiteSpace(c) -> finished <- true + | c -> sb.Append(c) |> ignore; i <- i + 1 + args <- sb.ToString() :: args + args + builder.AppendSwitchUnquotedIfNotNull(switch, value) + let tmp = new CommandLineBuilder() + tmp.AppendSwitchUnquotedIfNotNull(switch, value) + let s = tmp.ToString() + if s <> String.Empty then + args <- ParseCommandLineArgs(s) @ args + + member x.AppendSwitch(switch:string) = + builder.AppendSwitch(switch) + args <- switch :: args + + member internal x.GetCapturedArguments() = + [| + yield! x.CapturedArguments() + yield! x.CapturedFilenames() + |] diff --git a/src/fsharp/FSharp.Build/Fsc.fs b/src/fsharp/FSharp.Build/Fsc.fs index 48fd613fef2..4bac5d1ae05 100644 --- a/src/fsharp/FSharp.Build/Fsc.fs +++ b/src/fsharp/FSharp.Build/Fsc.fs @@ -3,126 +3,24 @@ namespace Microsoft.FSharp.Build open System -open System.Diagnostics.CodeAnalysis open System.Globalization open System.IO open System.Reflection -open System.Text open Microsoft.Build.Framework open Microsoft.Build.Utilities open Internal.Utilities -[] -[] -[] -do() - - #if FX_RESHAPED_REFLECTION open Microsoft.FSharp.Core.ReflectionAdapters #endif -type FscCommandLineBuilder () = - - // In addition to generating a command-line that will be handed to cmd.exe, we also generate - // an array of individual arguments. The former needs to be quoted (and cmd.exe will strip the - // quotes while parsing), whereas the latter is not. See bug 4357 for background; this helper - // class gets us out of the business of unparsing-then-reparsing arguments. - - let builder = new CommandLineBuilder() - let mutable args = [] // in reverse order - let mutable srcs = [] // in reverse order - - /// Return a list of the arguments (with no quoting for the cmd.exe shell) - member x.CapturedArguments() = List.rev args - - /// Return a list of the sources (with no quoting for the cmd.exe shell) - member x.CapturedFilenames() = List.rev srcs - - /// Return a full command line (with quoting for the cmd.exe shell) - override x.ToString() = builder.ToString() - - member x.AppendFileNamesIfNotNull(filenames:ITaskItem array, sep:string) = - builder.AppendFileNamesIfNotNull(filenames, sep) - // do not update "args", not used - for item in filenames do - let tmp = new CommandLineBuilder() - tmp.AppendSwitchUnquotedIfNotNull("", item.ItemSpec) // we don't want to quote the filename, this is a way to get that - let s = tmp.ToString() - if s <> String.Empty then - srcs <- tmp.ToString() :: srcs - - member x.AppendSwitchIfNotNull(switch:string, values:string array, sep:string) = - builder.AppendSwitchIfNotNull(switch, values, sep) - let tmp = new CommandLineBuilder() - tmp.AppendSwitchUnquotedIfNotNull(switch, values, sep) - let s = tmp.ToString() - if s <> String.Empty then - args <- s :: args - - member x.AppendSwitchIfNotNull(switch:string, value:string, ?metadataNames:string array) = - let metadataNames = defaultArg metadataNames [||] - builder.AppendSwitchIfNotNull(switch, value) - let tmp = new CommandLineBuilder() - tmp.AppendSwitchUnquotedIfNotNull(switch, value) - let providedMetaData = - metadataNames - |> Array.filter (String.IsNullOrWhiteSpace >> not) - if providedMetaData.Length > 0 then - tmp.AppendTextUnquoted "," - tmp.AppendTextUnquoted (providedMetaData|> String.concat ",") - let s = tmp.ToString() - if s <> String.Empty then - args <- s :: args - - member x.AppendSwitchUnquotedIfNotNull(switch:string, value:string) = - assert(switch = "") // we only call this method for "OtherFlags" - // Unfortunately we still need to mimic what cmd.exe does, but only for "OtherFlags". - let ParseCommandLineArgs(commandLine:string) = // returns list in reverse order - let mutable args = [] - let mutable i = 0 // index into commandLine - let len = commandLine.Length - while i < len do - // skip whitespace - while i < len && System.Char.IsWhiteSpace(commandLine, i) do - i <- i + 1 - if i < len then - // parse an argument - let sb = new StringBuilder() - let mutable finished = false - let mutable insideQuote = false - while i < len && not finished do - match commandLine.[i] with - | '"' -> insideQuote <- not insideQuote; i <- i + 1 - | c when not insideQuote && System.Char.IsWhiteSpace(c) -> finished <- true - | c -> sb.Append(c) |> ignore; i <- i + 1 - args <- sb.ToString() :: args - args - builder.AppendSwitchUnquotedIfNotNull(switch, value) - let tmp = new CommandLineBuilder() - tmp.AppendSwitchUnquotedIfNotNull(switch, value) - let s = tmp.ToString() - if s <> String.Empty then - args <- ParseCommandLineArgs(s) @ args - - member x.AppendSwitch(switch:string) = - builder.AppendSwitch(switch) - args <- switch :: args - - member internal x.GetCapturedArguments() = - [| - yield! x.CapturedArguments() - yield! x.CapturedFilenames() - |] - //There are a lot of flags on fsc.exe. //For now, not all of them are represented in the "Fsc class" object model. //The goal is to have the most common/important flags available via the Fsc class, and the //rest can be "backdoored" through the .OtherFlags property. -type - [] - Fsc () as this = +[] +type public Fsc () as this = inherit ToolTask () @@ -164,9 +62,9 @@ type let mutable targetProfile : string = null let mutable targetType : string = null let mutable toolExe : string = "fsc.exe" - let mutable toolPath : string = - let locationOfThisDll = - try Some(System.IO.Path.GetDirectoryName(typeof.Assembly.Location)) + let mutable toolPath : string = + let locationOfThisDll = + try Some(Path.GetDirectoryName(typeof.Assembly.Location)) with _ -> None match FSharpEnvironment.BinFolderOfDefaultFSharpCompiler(locationOfThisDll) with | Some s -> s @@ -183,20 +81,10 @@ type let mutable utf8output : bool = false // See bug 6483; this makes parallel build faster, and is fine to set unconditionally - // The property YieldDuringToolExecution is not available on Mono. - // So we only set it if available (to avoid a compile-time dependency). - do - let runningOnMono = try System.Type.GetType("Mono.Runtime") <> null with e -> false - if not runningOnMono then - typeof.InvokeMember("YieldDuringToolExecution", - (BindingFlags.Instance ||| BindingFlags.SetProperty ||| BindingFlags.Public), - null, - this, - [| box true |], - CultureInfo.CurrentCulture) |> ignore + do this.YieldDuringToolExecution <- true let generateCommandLineBuilder () = - let builder = new FscCommandLineBuilder() + let builder = new FSharpCommandLineBuilder() // OutputAssembly builder.AppendSwitchIfNotNull("-o:", outputAssembly) // CodePage @@ -205,7 +93,7 @@ type if debugSymbols then builder.AppendSwitch("-g") // DebugType - builder.AppendSwitchIfNotNull("--debug:", + builder.AppendSwitchIfNotNull("--debug:", if debugType = null then null else match debugType.ToUpperInvariant() with | "NONE" -> null @@ -226,7 +114,7 @@ type // DefineConstants if defineConstants <> null then for item in defineConstants do - builder.AppendSwitchIfNotNull("--define:", item.ItemSpec) + builder.AppendSwitchIfNotNull("--define:", item.ItemSpec) // DocumentationFile builder.AppendSwitchIfNotNull("--doc:", documentationFile) // GenerateInterfaceFile @@ -245,7 +133,7 @@ type // PdbFile builder.AppendSwitchIfNotNull("--pdb:", pdbFile) // Platform - builder.AppendSwitchIfNotNull("--platform:", + builder.AppendSwitchIfNotNull("--platform:", let ToUpperInvariant (s:string) = if s = null then null else s.ToUpperInvariant() match ToUpperInvariant(platform), prefer32bit, ToUpperInvariant(targetType) with | "ANYCPU", true, "EXE" @@ -302,9 +190,9 @@ type let warningsAsErrorsArray = match warningsAsErrors with | null -> [|"76"|] - | _ -> (warningsAsErrors + " 76 ").Split([|' '; ';'; ','|], StringSplitOptions.RemoveEmptyEntries) + | _ -> (warningsAsErrors + " 76 ").Split([|' '; ';'; ','|], StringSplitOptions.RemoveEmptyEntries) - builder.AppendSwitchIfNotNull("--warnaserror:", warningsAsErrorsArray, ",") + builder.AppendSwitchIfNotNull("--warnaserror:", warningsAsErrorsArray, ",") // Win32ResourceFile builder.AppendSwitchIfNotNull("--win32res:", win32res) @@ -312,9 +200,9 @@ type // Win32ManifestFile builder.AppendSwitchIfNotNull("--win32manifest:", win32manifest) - // VisualStudioStyleErrors + // VisualStudioStyleErrors if vserrors then - builder.AppendSwitch("--vserrors") + builder.AppendSwitch("--vserrors") builder.AppendSwitchIfNotNull("--LCID:", vslcid) builder.AppendSwitchIfNotNull("--preferreduilang:", preferredUILang) @@ -405,7 +293,7 @@ type member fsc.GenerateInterfaceFile with get() = generateInterfaceFile - and set(s) = generateInterfaceFile <- s + and set(s) = generateInterfaceFile <- s // --keyfile : // Sign the assembly the given keypair file, as produced @@ -441,13 +329,13 @@ type with get() = otherFlags and set(s) = otherFlags <- s - // -o : Name the output file. + // -o : Name the output file member fsc.OutputAssembly with get() = outputAssembly and set(s) = outputAssembly <- s // --pdb : - // Name the debug output file. + // Name the debug output file member fsc.PdbFile with get() = pdbFile and set(s) = pdbFile <- s @@ -459,28 +347,28 @@ type // anycpu32bitpreferred member fsc.Platform with get() = platform - and set(s) = platform <- s + and set(s) = platform <- s // indicator whether anycpu32bitpreferred is applicable or not member fsc.Prefer32Bit with get() = prefer32bit - and set(s) = prefer32bit <- s + and set(s) = prefer32bit <- s member fsc.PreferredUILang with get() = preferredUILang - and set(s) = preferredUILang <- s + and set(s) = preferredUILang <- s - member fsc.ProvideCommandLineArgs + member fsc.ProvideCommandLineArgs with get() = provideCommandLineArgs and set(p) = provideCommandLineArgs <- p member fsc.PublicSign with get() = publicSign - and set(s) = publicSign <- s + and set(s) = publicSign <- s // -r : Reference an F# or .NET assembly. - member fsc.References - with get() = references + member fsc.References + with get() = references and set(a) = references <- a // --lib @@ -494,7 +382,7 @@ type with get() = resources and set(a) = resources <- a - member fsc.SkipCompilerExecution + member fsc.SkipCompilerExecution with get() = skipCompilerExecution and set(p) = skipCompilerExecution <- p @@ -504,8 +392,8 @@ type and set(s) = sourceLink <- s // source files - member fsc.Sources - with get() = sources + member fsc.Sources + with get() = sources and set(a) = sources <- a member fsc.TargetProfile @@ -540,17 +428,17 @@ type with get() = versionFile and set(s) = versionFile <- s - // For specifying a win32 native resource file (.res) + // For specifying a win32 native resource file (.res) member fsc.Win32ResourceFile with get() = win32res and set(s) = win32res <- s - + // For specifying a win32 manifest file member fsc.Win32ManifestFile with get() = win32manifest and set(m) = win32manifest <- m - // For specifying the warning level (0-4) + // For specifying the warning level (0-4) member fsc.WarningLevel with get() = warningLevel and set(s) = warningLevel <- s @@ -597,7 +485,7 @@ type /// Intercept the call to ExecuteTool to handle the host compile case. override fsc.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands) = if provideCommandLineArgs then - commandLineArgs <- + commandLineArgs <- fsc.GetCapturedArguments() |> Array.map (fun (arg: string) -> TaskItem(arg) :> ITaskItem) |> Array.toList @@ -620,12 +508,12 @@ type let baseCallDelegate = new System.Converter(baseCall) #endif try - let ret = - (host.GetType()).InvokeMember("Compile", BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.InvokeMethod ||| BindingFlags.Instance, null, host, + let ret = + (host.GetType()).InvokeMember("Compile", BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.InvokeMethod ||| BindingFlags.Instance, null, host, [| baseCallDelegate; box (capturedArguments |> List.toArray); box (capturedFilenames |> List.toArray) |], - System.Globalization.CultureInfo.InvariantCulture) + CultureInfo.InvariantCulture) unbox ret - with + with | :? System.Reflection.TargetInvocationException as tie when (match tie.InnerException with | :? Microsoft.Build.Exceptions.BuildAbortedException -> true | _ -> false) -> fsc.Log.LogError(tie.InnerException.Message, [| |]) -1 // ok, this is what happens when VS IDE cancels the build, no need to assert, just log the build-canceled error and return -1 to denote task failed @@ -634,7 +522,7 @@ type reraise() override fsc.GenerateCommandLineCommands() = - let builder = new FscCommandLineBuilder() + let builder = new FSharpCommandLineBuilder() if not (String.IsNullOrEmpty(dotnetFscCompilerPath)) then builder.AppendSwitch(dotnetFscCompilerPath) builder.ToString() @@ -647,13 +535,13 @@ type fsc.GenerateCommandLineCommands() // expose this to internal components (for nunit testing) - member internal fsc.InternalGenerateResponseFileCommands() = + member internal fsc.InternalGenerateResponseFileCommands() = fsc.GenerateResponseFileCommands() member internal fsc.InternalExecuteTool(pathToTool, responseFileCommands, commandLineCommands) = fsc.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands) - member internal fsc.GetCapturedArguments() = + member internal fsc.GetCapturedArguments() = [| yield! capturedArguments yield! capturedFilenames From b3d94c1d91f59db775f36cb137d77b949dd05e51 Mon Sep 17 00:00:00 2001 From: Kevin Ransom Date: Sat, 18 Nov 2017 23:20:32 -0800 Subject: [PATCH 7/7] Move from Converter To Func --- .../FSharp.Build/FSharpCommandLineBuilder.fs | 4 -- src/fsharp/FSharp.Build/Fsc.fs | 50 ++++++++++++------- .../FSharp.ProjectSystem.FSharp/Project.fs | 18 ++++++- 3 files changed, 47 insertions(+), 25 deletions(-) diff --git a/src/fsharp/FSharp.Build/FSharpCommandLineBuilder.fs b/src/fsharp/FSharp.Build/FSharpCommandLineBuilder.fs index 23b15513d9f..8ea25e12d61 100644 --- a/src/fsharp/FSharp.Build/FSharpCommandLineBuilder.fs +++ b/src/fsharp/FSharp.Build/FSharpCommandLineBuilder.fs @@ -12,10 +12,6 @@ open Internal.Utilities [] do() -//#if FX_RESHAPED_REFLECTION -//open Microsoft.FSharp.Core.ReflectionAdapters -//#endif - type FSharpCommandLineBuilder () = // In addition to generating a command-line that will be handed to cmd.exe, we also generate diff --git a/src/fsharp/FSharp.Build/Fsc.fs b/src/fsharp/FSharp.Build/Fsc.fs index 4bac5d1ae05..f397c50914c 100644 --- a/src/fsharp/FSharp.Build/Fsc.fs +++ b/src/fsharp/FSharp.Build/Fsc.fs @@ -3,6 +3,7 @@ namespace Microsoft.FSharp.Build open System +open System.Diagnostics open System.Globalization open System.IO open System.Reflection @@ -498,28 +499,39 @@ type public Fsc () as this = | null -> base.ExecuteTool(pathToTool, responseFileCommands, commandLineCommands) | _ -> let sources = sources|>Array.map(fun i->i.ItemSpec) - -#if FX_NO_CONVERTER - let baseCallDelegate = new Func(fun () -> fsc.BaseExecuteTool(pathToTool, responseFileCommands, commandLineCommands) ) -#else - let baseCall = fun (dummy : int) -> fsc.BaseExecuteTool(pathToTool, responseFileCommands, commandLineCommands) - // We are using a Converter rather than a "unit->int" because it is too hard to - // figure out how to pass an F# function object via reflection. - let baseCallDelegate = new System.Converter(baseCall) -#endif + let invokeCompiler baseCallDelegate = + try + let ret = + (host.GetType()).InvokeMember("Compile", BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.InvokeMethod ||| BindingFlags.Instance, null, host, + [| baseCallDelegate; box (capturedArguments |> List.toArray); box (capturedFilenames |> List.toArray) |], + CultureInfo.InvariantCulture) + unbox ret + with + | :? TargetInvocationException as tie when (match tie.InnerException with | :? Microsoft.Build.Exceptions.BuildAbortedException -> true | _ -> false) -> + fsc.Log.LogError(tie.InnerException.Message, [| |]) + -1 // ok, this is what happens when VS IDE cancels the build, no need to assert, just log the build-canceled error and return -1 to denote task failed + | e -> reraise() + + // Todo: Remove !FX_NO_CONVERTER code path for VS2017.7 + // Earlier buildtasks usesd System.Converter for cross platform we are moving to Func + // This is so that during the interim, earlier VS's will still load the OSS project + let baseCallDelegate = Func(fun () -> fsc.BaseExecuteTool(pathToTool, responseFileCommands, commandLineCommands) ) try - let ret = - (host.GetType()).InvokeMember("Compile", BindingFlags.Public ||| BindingFlags.NonPublic ||| BindingFlags.InvokeMethod ||| BindingFlags.Instance, null, host, - [| baseCallDelegate; box (capturedArguments |> List.toArray); box (capturedFilenames |> List.toArray) |], - CultureInfo.InvariantCulture) - unbox ret + invokeCompiler baseCallDelegate with - | :? System.Reflection.TargetInvocationException as tie when (match tie.InnerException with | :? Microsoft.Build.Exceptions.BuildAbortedException -> true | _ -> false) -> - fsc.Log.LogError(tie.InnerException.Message, [| |]) - -1 // ok, this is what happens when VS IDE cancels the build, no need to assert, just log the build-canceled error and return -1 to denote task failed | e -> - System.Diagnostics.Debug.Assert(false, "HostObject received by Fsc task did not have a Compile method or the compile method threw an exception. "+(e.ToString())) - reraise() +#if !FX_NO_CONVERTER + try + let baseCall = fun (dummy : int) -> fsc.BaseExecuteTool(pathToTool, responseFileCommands, commandLineCommands) + // We are using a Converter rather than a "unit->int" because it is too hard to + // figure out how to pass an F# function object via reflection. + let baseCallDelegate = new System.Converter(baseCall) + invokeCompiler baseCallDelegate + with + | e -> +#endif + Debug.Assert(false, "HostObject received by Fsc task did not have a Compile method or the compile method threw an exception. "+(e.ToString())) + reraise() override fsc.GenerateCommandLineCommands() = let builder = new FSharpCommandLineBuilder() diff --git a/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs b/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs index 1a4732cf1e5..8e11b8651f6 100644 --- a/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs +++ b/vsintegration/src/FSharp.ProjectSystem.FSharp/Project.fs @@ -1290,8 +1290,7 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem let result = base.InvokeMsBuild(target, extraProperties) result - // Fulfill HostObject contract with Fsc task, and enable 'capture' of compiler flags for the project. - member x.Compile(compile:System.Converter, flags:string[], sources:string[]) = + member x.CoreCompile(flags:string[], sources:string[]) = // Note: This method may be called from non-UI thread! The Fsc task in FSharp.Build.dll invokes this method via reflection, and // the Fsc task is typically created by MSBuild on a background thread. So be careful. #if DEBUG @@ -1306,11 +1305,26 @@ namespace rec Microsoft.VisualStudio.FSharp.ProjectSystem if projectSite.State = ProjectSiteOptionLifetimeState.Opening then // This is the first time, so set up interface for language service to talk to us projectSite.Open(x.CreateRunningProjectSite()) + + // ===================================================================================================== + // Todo: x.Compile(compile:System.Converter, flags:string[], sources:string[]) for VS2017.7 + // Earlier buildtasks usesd System.Converter for cross platform we are moving to Func + // This is so that during the interim, earlier VS's will still load the OSS project + // ===================================================================================================== + member x.Compile(compile:System.Converter, flags:string[], sources:string[]) = + x.CoreCompile(flags, sources) if actuallyBuild then compile.Invoke(0) else 0 + member x.Compile(compile:Func, flags:string[], sources:string[]) = + x.CoreCompile(flags, sources) + if actuallyBuild then + compile.Invoke() + else + 0 + member __.CompilationSourceFiles = match sourcesAndFlags with None -> [| |] | Some (sources,_) -> sources member __.CompilationOptions = match sourcesAndFlags with None -> [| |] | Some (_,flags) -> flags member __.CompilationReferences = match normalizedRefs with None -> [| |] | Some refs -> refs