From 2e948b7bdc3cb6ce913d3fea4053b37e10cb15a0 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Mon, 2 Nov 2015 00:14:34 +0000 Subject: [PATCH 01/14] Move FCS project cracking out of process This aims to fix many problems stemming from two main issues: * FCS depends on MSBuild v12, and not all machines have this installed. * It's hard to apply robust assembly redirects, as this is up to the application referencing FCS. These are needed to redirect MSBuild v4 to whatever is being used (for custom MSBuild tasks), and also to allow redirecting to MSBuild v14 if necessary. The API is preserved, with an additional function, `GetProjectOptionsFromProjectFileLogged` to allow easier access to the logs for presentation to users and bug-reporting. The functionality is implemented by FCS calling a new console application, FSharp.Compiler.Service.ProjectCracker.exe, and the response is transmitted via serialization using JSON to types shared by a common source file, so there is no binary dependency between the two. The ProjectCracker static links to FSharp.Core, as it is in the same directory as FCS.dll. Thus bundling a separate copy would force the choice of version on all users of FCS, which is not acceptable. --- FSharp.Compiler.Service.sln | 19 +- .../App.config | 18 + ...arp.Compiler.Service.ProjectCracker.fsproj | 95 ++++ .../Program.fs | 414 ++++++++++++++++++ .../ProjectCrackerOptions.fs | 9 + .../FSharp.Compiler.Service.fsproj | 27 +- src/fsharp/vs/service.fs | 408 ++--------------- src/fsharp/vs/service.fsi | 49 +-- tests/service/ProjectOptionsTests.fs | 108 ++--- tests/service/data/ToolsVersion12.fsproj | 7 - 10 files changed, 670 insertions(+), 484 deletions(-) create mode 100644 src/fsharp/FSharp.Compiler.Service.ProjectCracker/App.config create mode 100644 src/fsharp/FSharp.Compiler.Service.ProjectCracker/FSharp.Compiler.Service.ProjectCracker.fsproj create mode 100644 src/fsharp/FSharp.Compiler.Service.ProjectCracker/Program.fs create mode 100644 src/fsharp/FSharp.Compiler.Service.ProjectCracker/ProjectCrackerOptions.fs diff --git a/FSharp.Compiler.Service.sln b/FSharp.Compiler.Service.sln index d00e651dc0..62f3624993 100644 --- a/FSharp.Compiler.Service.sln +++ b/FSharp.Compiler.Service.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +VisualStudioVersion = 12.0.30501.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{B6B68AE6-E7A4-4D43-9B34-FFA74BFE192B}" ProjectSection(SolutionItems) = preProject @@ -57,6 +57,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fsc", "samples\FscExe\Fsc.f EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharp_Analysis", "tests\service\data\CSharp_Analysis\CSharp_Analysis.csproj", "{887630A3-4B1D-40EA-B8B3-2D842E9C40DB}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Service.ProjectCracker", "src\fsharp\FSharp.Compiler.Service.ProjectCracker\FSharp.Compiler.Service.ProjectCracker.fsproj", "{B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -190,6 +192,21 @@ Global {887630A3-4B1D-40EA-B8B3-2D842E9C40DB}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU {887630A3-4B1D-40EA-B8B3-2D842E9C40DB}.Release|Mixed Platforms.Build.0 = Release|Any CPU {887630A3-4B1D-40EA-B8B3-2D842E9C40DB}.Release|x86.ActiveCfg = Release|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Debug|x86.ActiveCfg = Debug|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Proto|Any CPU.ActiveCfg = Release|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Proto|Any CPU.Build.0 = Release|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Proto|Mixed Platforms.ActiveCfg = Release|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Proto|Mixed Platforms.Build.0 = Release|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Proto|x86.ActiveCfg = Release|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Release|Any CPU.Build.0 = Release|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/fsharp/FSharp.Compiler.Service.ProjectCracker/App.config b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/App.config new file mode 100644 index 0000000000..3c6dcfe227 --- /dev/null +++ b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/App.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/fsharp/FSharp.Compiler.Service.ProjectCracker/FSharp.Compiler.Service.ProjectCracker.fsproj b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/FSharp.Compiler.Service.ProjectCracker.fsproj new file mode 100644 index 0000000000..7c3cd205a9 --- /dev/null +++ b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/FSharp.Compiler.Service.ProjectCracker.fsproj @@ -0,0 +1,95 @@ + + + + + Debug + AnyCPU + 2.0 + b1bdd96d-47e1-4e65-8107-fbae23a06db4 + Exe + FSharp.Compiler.Service.ProjectCracker + FSharp.Compiler.Service.ProjectCracker + v4.5 + 4.3.0.0 + ..\..\..\ + FSharp.Compiler.Service.ProjectCracker + $(OtherFlags) --staticlink:FSharp.Core + $(NoWarn);40 + + + true + full + false + false + bin\Debug\ + DEBUG;TRACE + 3 + AnyCPU + bin\Debug\FSharp.Compiler.Service.ProjectCracker.XML + true + + + pdbonly + true + true + bin\Release\ + TRACE + 3 + AnyCPU + bin\Release\FSharp.Compiler.Service.ProjectCracker.XML + true + + + + + + + + True + + + True + + + True + + + + + False + + + + + + + + + + + + + + 11 + + + + + $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets + + + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets + + + + + + \ No newline at end of file diff --git a/src/fsharp/FSharp.Compiler.Service.ProjectCracker/Program.fs b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/Program.fs new file mode 100644 index 0000000000..ed1e01e752 --- /dev/null +++ b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/Program.fs @@ -0,0 +1,414 @@ +namespace FSharp.Compiler.Service.ProjectCracker + +open Microsoft.Build.Framework +open Microsoft.Build.Utilities +open System.Text +open System.IO +open System +open System.Reflection +open System.Runtime.Serialization.Formatters.Binary +open System.Runtime.Serialization.Json + +module Program = + let runningOnMono = + try match System.Type.GetType("Mono.Runtime") with null -> false | _ -> true + with e -> false + + type internal BasicStringLogger() = + inherit Logger() + + let sb = new StringBuilder() + + let log (e: BuildEventArgs) = + sb.Append(e.Message) |> ignore + sb.AppendLine() |> ignore + + override x.Initialize(eventSource:IEventSource) = + sb.Clear() |> ignore + eventSource.AnyEventRaised.Add(log) + + member x.Log = sb.ToString() + + type internal HostCompile() = + member th.Compile(_, _, _) = 0 + interface ITaskHost + + //---------------------------------------------------------------------------- + // FSharpProjectFileInfo + // + [] + type FSharpProjectFileInfo (fsprojFileName:string, ?properties, ?enableLogging) = + + let properties = defaultArg properties [] + let enableLogging = defaultArg enableLogging false + let mkAbsolute dir v = + if Path.IsPathRooted v then v + else Path.Combine(dir, v) + + let mkAbsoluteOpt dir v = Option.map (mkAbsolute dir) v + + let logOpt = + if enableLogging then + let log = new BasicStringLogger() + do log.Verbosity <- Microsoft.Build.Framework.LoggerVerbosity.Diagnostic + Some log + else + None + + // Use the old API on Mono, with ToolsVersion = 12.0 + let CrackProjectUsingOldBuildAPI(fsprojFile:string) = + let engine = new Microsoft.Build.BuildEngine.Engine() + try + engine.DefaultToolsVersion <- "12.0" + with | _ -> engine.DefaultToolsVersion <- "4.0" + + Option.iter (fun l -> engine.RegisterLogger(l)) logOpt + + let bpg = Microsoft.Build.BuildEngine.BuildPropertyGroup() + + bpg.SetProperty("BuildingInsideVisualStudio", "true") + for (prop, value) in properties do + bpg.SetProperty(prop, value) + + engine.GlobalProperties <- bpg + + let projectFromFile (fsprojFile:string) = + // We seem to need to pass 12.0/4.0 in here for some unknown reason + let project = new Microsoft.Build.BuildEngine.Project(engine, engine.DefaultToolsVersion) + do project.Load(fsprojFile) + project + + let project = projectFromFile fsprojFile + project.Build([| "ResolveReferences" |]) |> ignore + let directory = Path.GetDirectoryName project.FullFileName + + let getProp (p: Microsoft.Build.BuildEngine.Project) s = + let v = p.GetEvaluatedProperty s + if String.IsNullOrWhiteSpace v then None + else Some v + + let outFileOpt = + match mkAbsoluteOpt directory (getProp project "OutDir") with + | None -> None + | Some d -> mkAbsoluteOpt d (getProp project "TargetFileName") + + let getItems s = + let fs = project.GetEvaluatedItemsByName(s) + [ for f in fs -> mkAbsolute directory f.FinalItemSpec ] + + let projectReferences = + [ for i in project.GetEvaluatedItemsByName("ProjectReference") do + yield mkAbsolute directory i.FinalItemSpec + ] + + let references = + [ for i in project.GetEvaluatedItemsByName("ReferencePath") do + yield i.FinalItemSpec + for i in project.GetEvaluatedItemsByName("ChildProjectReferences") do + yield i.FinalItemSpec ] + // Duplicate slashes sometimes appear in the output here, which prevents + // them from matching keys used in FSharpProjectOptions.ReferencedProjects + |> List.map (fun (s: string) -> s.Replace("//","/")) + + outFileOpt, directory, getItems, references, projectReferences, getProp project, project.FullFileName + + let CrackProjectUsingNewBuildAPI(fsprojFile) = + let fsprojFullPath = try Path.GetFullPath(fsprojFile) with _ -> fsprojFile + let fsprojAbsDirectory = Path.GetDirectoryName fsprojFullPath + + use _pwd = + let dir = Environment.CurrentDirectory + Environment.CurrentDirectory <- fsprojAbsDirectory + { new System.IDisposable with member x.Dispose() = Environment.CurrentDirectory <- dir } + use engine = new Microsoft.Build.Evaluation.ProjectCollection() + let host = new HostCompile() + engine.HostServices.RegisterHostObject(fsprojFullPath, "CoreCompile", "Fsc", host) + + let projectInstanceFromFullPath (fsprojFullPath: string) = + use stream = new IO.StreamReader(fsprojFullPath) + use xmlReader = System.Xml.XmlReader.Create(stream) + + let project = engine.LoadProject(xmlReader, FullPath=fsprojFullPath) + + project.SetGlobalProperty("BuildingInsideVisualStudio", "true") |> ignore + project.SetGlobalProperty("VisualStudioVersion", "12.0") |> ignore + for (prop, value) in properties do + project.SetProperty(prop, value) |> ignore + + project.CreateProjectInstance() + + let project = projectInstanceFromFullPath fsprojFullPath + let directory = project.Directory + + let getprop (p: Microsoft.Build.Execution.ProjectInstance) s = + let v = p.GetPropertyValue s + if String.IsNullOrWhiteSpace v then None + else Some v + + let outFileOpt = getprop project "TargetPath" + + let log = match logOpt with + | None -> [] + | Some l -> [l :> ILogger] + + project.Build([| "Build" |], log) |> ignore + + let getItems s = [ for f in project.GetItems(s) -> mkAbsolute directory f.EvaluatedInclude ] + + let projectReferences = + [ for cp in project.GetItems("ProjectReference") do + yield cp.GetMetadataValue("FullPath") + ] + + let references = + [ for i in project.GetItems("ReferencePath") do + yield i.EvaluatedInclude + for i in project.GetItems("ChildProjectReferences") do + yield i.EvaluatedInclude ] + + outFileOpt, directory, getItems, references, projectReferences, getprop project, project.FullPath + + let outFileOpt, directory, getItems, references, projectReferences, getProp, fsprojFullPath = + try + if runningOnMono then + CrackProjectUsingOldBuildAPI(fsprojFileName) + else + CrackProjectUsingNewBuildAPI(fsprojFileName) + with + | :? Microsoft.Build.BuildEngine.InvalidProjectFileException as e -> + raise (Microsoft.Build.Exceptions.InvalidProjectFileException( + e.ProjectFile, + e.LineNumber, + e.ColumnNumber, + e.EndLineNumber, + e.EndColumnNumber, + e.Message, + e.ErrorSubcategory, + e.ErrorCode, + e.HelpKeyword)) + | :? ArgumentException as e -> raise (IO.FileNotFoundException(e.Message)) + + + let logOutput = match logOpt with None -> "" | Some l -> l.Log + let pages = getItems "Page" + let embeddedResources = getItems "EmbeddedResource" + let files = getItems "Compile" + let resources = getItems "Resource" + let noaction = getItems "None" + let content = getItems "Content" + + let split (s : string option) (cs : char []) = + match s with + | None -> [||] + | Some s -> + if String.IsNullOrWhiteSpace s then [||] + else s.Split(cs, StringSplitOptions.RemoveEmptyEntries) + + let getbool (s : string option) = + match s with + | None -> false + | Some s -> + match (Boolean.TryParse s) with + | (true, result) -> result + | (false, _) -> false + + let fxVer = getProp "TargetFrameworkVersion" + let optimize = getProp "Optimize" |> getbool + let assemblyNameOpt = getProp "AssemblyName" + let tailcalls = getProp "Tailcalls" |> getbool + let outputPathOpt = getProp "OutputPath" + let docFileOpt = getProp "DocumentationFile" + let outputTypeOpt = getProp "OutputType" + let debugTypeOpt = getProp "DebugType" + let baseAddressOpt = getProp "BaseAddress" + let sigFileOpt = getProp "GenerateSignatureFile" + let keyFileOpt = getProp "KeyFile" + let pdbFileOpt = getProp "PdbFile" + let platformOpt = getProp "Platform" + let targetTypeOpt = getProp "TargetType" + let versionFileOpt = getProp "VersionFile" + let targetProfileOpt = getProp "TargetProfile" + let warnLevelOpt = getProp "Warn" + let subsystemVersionOpt = getProp "SubsystemVersion" + let win32ResOpt = getProp "Win32ResourceFile" + let heOpt = getProp "HighEntropyVA" |> getbool + let win32ManifestOpt = getProp "Win32ManifestFile" + let debugSymbols = getProp "DebugSymbols" |> getbool + let prefer32bit = getProp "Prefer32Bit" |> getbool + let warnAsError = getProp "TreatWarningsAsErrors" |> getbool + let defines = split (getProp "DefineConstants") [| ';'; ','; ' ' |] + let nowarn = split (getProp "NoWarn") [| ';'; ','; ' ' |] + let warningsAsError = split (getProp "WarningsAsErrors") [| ';'; ','; ' ' |] + let libPaths = split (getProp "ReferencePath") [| ';'; ',' |] + let otherFlags = split (getProp "OtherFlags") [| ' ' |] + let isLib = (outputTypeOpt = Some "Library") + + let docFileOpt = + match docFileOpt with + | None -> None + | Some docFile -> Some(mkAbsolute directory docFile) + + + let options = + [ yield "--simpleresolution" + yield "--noframework" + match outFileOpt with + | None -> () + | Some outFile -> yield "--out:" + outFile + match docFileOpt with + | None -> () + | Some docFile -> yield "--doc:" + docFile + match baseAddressOpt with + | None -> () + | Some baseAddress -> yield "--baseaddress:" + baseAddress + match keyFileOpt with + | None -> () + | Some keyFile -> yield "--keyfile:" + keyFile + match sigFileOpt with + | None -> () + | Some sigFile -> yield "--sig:" + sigFile + match pdbFileOpt with + | None -> () + | Some pdbFile -> yield "--pdb:" + pdbFile + match versionFileOpt with + | None -> () + | Some versionFile -> yield "--versionfile:" + versionFile + match warnLevelOpt with + | None -> () + | Some warnLevel -> yield "--warn:" + warnLevel + match subsystemVersionOpt with + | None -> () + | Some s -> yield "--subsystemversion:" + s + if heOpt then yield "--highentropyva+" + match win32ResOpt with + | None -> () + | Some win32Res -> yield "--win32res:" + win32Res + match win32ManifestOpt with + | None -> () + | Some win32Manifest -> yield "--win32manifest:" + win32Manifest + match targetProfileOpt with + | None -> () + | Some targetProfile -> yield "--targetprofile:" + targetProfile + yield "--fullpaths" + yield "--flaterrors" + if warnAsError then yield "--warnaserror" + yield + if isLib then "--target:library" + else "--target:exe" + for symbol in defines do + if not (String.IsNullOrWhiteSpace symbol) then yield "--define:" + symbol + for nw in nowarn do + if not (String.IsNullOrWhiteSpace nw) then yield "--nowarn:" + nw + for nw in warningsAsError do + if not (String.IsNullOrWhiteSpace nw) then yield "--warnaserror:" + nw + yield if debugSymbols then "--debug+" + else "--debug-" + yield if optimize then "--optimize+" + else "--optimize-" + yield if tailcalls then "--tailcalls+" + else "--tailcalls-" + match debugTypeOpt with + | None -> () + | Some debugType -> + match debugType.ToUpperInvariant() with + | "NONE" -> () + | "PDBONLY" -> yield "--debug:pdbonly" + | "FULL" -> yield "--debug:full" + | _ -> () + match platformOpt |> Option.map (fun o -> o.ToUpperInvariant()), prefer32bit, + targetTypeOpt |> Option.map (fun o -> o.ToUpperInvariant()) with + | Some "ANYCPU", true, Some "EXE" | Some "ANYCPU", true, Some "WINEXE" -> yield "--platform:anycpu32bitpreferred" + | Some "ANYCPU", _, _ -> yield "--platform:anycpu" + | Some "X86", _, _ -> yield "--platform:x86" + | Some "X64", _, _ -> yield "--platform:x64" + | Some "ITANIUM", _, _ -> yield "--platform:Itanium" + | _ -> () + match targetTypeOpt |> Option.map (fun o -> o.ToUpperInvariant()) with + | Some "LIBRARY" -> yield "--target:library" + | Some "EXE" -> yield "--target:exe" + | Some "WINEXE" -> yield "--target:winexe" + | Some "MODULE" -> yield "--target:module" + | _ -> () + yield! otherFlags + for f in resources do + yield "--resource:" + f + for i in libPaths do + yield "--lib:" + mkAbsolute directory i + for r in references do + yield "-r:" + r + yield! files ] + + member x.Options = options + member x.FrameworkVersion = fxVer + member x.ProjectReferences = projectReferences + member x.References = references + member x.CompileFiles = files + member x.ResourceFiles = resources + member x.EmbeddedResourceFiles = embeddedResources + member x.ContentFiles = content + member x.OtherFiles = noaction + member x.PageFiles = pages + member x.OutputFile = outFileOpt + member x.Directory = directory + member x.AssemblyName = assemblyNameOpt + member x.OutputPath = outputPathOpt + member x.FullPath = fsprojFullPath + member x.LogOutput = logOutput + static member Parse(fsprojFileName:string, ?properties, ?enableLogging) = new FSharpProjectFileInfo(fsprojFileName, ?properties=properties, ?enableLogging=enableLogging) + + + let getOptions file enableLogging properties = + let rec getOptions file : Option * ProjectOptions = + let parsedProject = FSharpProjectFileInfo.Parse(file, properties=properties, enableLogging=enableLogging) + let referencedProjectOptions = + [| for file in parsedProject.ProjectReferences do + if Path.GetExtension(file) = ".fsproj" then + match getOptions file with + | Some outFile, opts -> yield outFile, opts + | None, _ -> () |] + + let options = { ProjectFile = file + Options = Array.ofList parsedProject.Options + ReferencedProjectOptions = referencedProjectOptions + LogOutput = parsedProject.LogOutput } + parsedProject.OutputFile, options + + snd (getOptions file) + + let addMSBuildv14BackupResolution () = + let onResolveEvent = new ResolveEventHandler(fun sender evArgs -> + let requestedAssembly = AssemblyName(evArgs.Name) + if requestedAssembly.Name.StartsWith("Microsoft.Build") && + not (requestedAssembly.Name.EndsWith(".resources")) then + requestedAssembly.Version <- Version("14.0.0.0") + Assembly.Load (requestedAssembly) + else + null) + AppDomain.CurrentDomain.add_AssemblyResolve(onResolveEvent) + + let rec pairs l = + match l with + | [] | [_] -> [] + | x::y::rest -> (x,y) :: pairs rest + + [] + let main argv = + let ret, opts = + try + addMSBuildv14BackupResolution () + if argv.Length >= 2 then + let projectFile = argv.[0] + let enableLogging = match Boolean.TryParse(argv.[1]) with + | true, true -> true + | _ -> false + let props = pairs (List.ofArray argv.[2..]) + let opts = getOptions argv.[0] enableLogging props + 0, opts + else + 1, { ProjectFile = ""; Options = [||]; ReferencedProjectOptions = [||]; LogOutput = "At least two arguments required." } + with e -> + 2, { ProjectFile = ""; Options = [||]; ReferencedProjectOptions = [||]; LogOutput = e.ToString() } + + let ser = new DataContractJsonSerializer(typeof) + ser.WriteObject(Console.OpenStandardOutput(), opts) + ret diff --git a/src/fsharp/FSharp.Compiler.Service.ProjectCracker/ProjectCrackerOptions.fs b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/ProjectCrackerOptions.fs new file mode 100644 index 0000000000..94f9e16927 --- /dev/null +++ b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/ProjectCrackerOptions.fs @@ -0,0 +1,9 @@ +namespace FSharp.Compiler.Service.ProjectCracker + +type ProjectOptions = + { + ProjectFile: string + Options: string[] + ReferencedProjectOptions: (string * ProjectOptions)[] + LogOutput: string + } diff --git a/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj b/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj index 726aff06b8..3f68358cf0 100644 --- a/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj +++ b/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj @@ -1,4 +1,4 @@ - + + \ No newline at end of file diff --git a/src/fsharp/FSharp.Compiler.Service.ProjectCrackerReader/Program.fs b/src/fsharp/FSharp.Compiler.Service.ProjectCrackerReader/Program.fs new file mode 100644 index 0000000000..e3d8bac817 --- /dev/null +++ b/src/fsharp/FSharp.Compiler.Service.ProjectCrackerReader/Program.fs @@ -0,0 +1,51 @@ +namespace FSharp.Compiler.Service + +open System.Diagnostics +open System.Text +open System.IO +open System +open System.Runtime.Serialization.Formatters.Binary + +open FSharp.Compiler.Service.ProjectCracker + +type ProjectCrackerReader = + + static member GetProjectOptionsFromProjectFileLogged(projectFileName : string, ?properties : (string * string) list, ?loadedTimeStamp, ?enableLogging) = + let loadedTimeStamp = defaultArg loadedTimeStamp DateTime.MaxValue // Not 'now', we don't want to force reloading + let properties = defaultArg properties [] + let enableLogging = defaultArg enableLogging false + + let rec convert (opts: FSharp.Compiler.Service.ProjectCracker.ProjectOptions) : FSharpProjectOptions = + let referencedProjects = Array.map (fun (a, b) -> a, convert b) opts.ReferencedProjectOptions + { ProjectFileName = opts.ProjectFile + ProjectFileNames = [| |] + OtherOptions = opts.Options + ReferencedProjects = referencedProjects + IsIncompleteTypeCheckEnvironment = false + UseScriptResolutionRules = false + LoadTime = loadedTimeStamp + UnresolvedReferences = None } + + let arguments = new StringBuilder() + arguments.Append(projectFileName) |> ignore + arguments.Append(' ').Append(enableLogging.ToString()) |> ignore + for k, v in properties do + arguments.Append(' ').Append(k).Append(' ').Append(v) |> ignore + + let p = new System.Diagnostics.Process() + p.StartInfo.FileName <- Path.Combine(Path.GetDirectoryName(Reflection.Assembly.GetExecutingAssembly().Location), + "FSharp.Compiler.Service.ProjectCracker.exe") + p.StartInfo.Arguments <- arguments.ToString() + p.StartInfo.UseShellExecute <- false + p.StartInfo.CreateNoWindow <- true + p.StartInfo.RedirectStandardOutput <- true + ignore <| p.Start() + + let ser = new System.Runtime.Serialization.Json.DataContractJsonSerializer(typeof) + let opts = ser.ReadObject(p.StandardOutput.BaseStream) :?> FSharp.Compiler.Service.ProjectCracker.ProjectOptions + p.WaitForExit() + + convert opts, opts.LogOutput + + let GetProjectOptionsFromProjectFile(projectFileName : string, ?properties : (string * string) list, ?loadedTimeStamp) = + fst (ic.GetProjectOptionsFromProjectFileLogged(projectFileName, ?properties=properties, ?loadedTimeStamp=loadedTimeStamp)) From 1fb9eae3f21b0f500605c661d3489aeb547400f5 Mon Sep 17 00:00:00 2001 From: Robin Neatherway Date: Tue, 24 Nov 2015 22:40:53 +0000 Subject: [PATCH 03/14] wip --- FSharp.Compiler.Service.sln | 4 +- ...ompiler.Service.ProjectCracker.Exe.fsproj} | 46 +++++++++---------- .../Program.fs | 2 +- .../ProjectCrackerOptions.fs | 2 +- ...rp.Compiler.Service.ProjectCracker.fsproj} | 11 +++-- .../Program.fs | 18 ++++---- src/fsharp/vs/service.fs | 41 +---------------- src/fsharp/vs/service.fsi | 12 +---- 8 files changed, 46 insertions(+), 90 deletions(-) rename src/fsharp/FSharp.Compiler.Service.ProjectCracker/{FSharp.Compiler.Service.ProjectCracker.fsproj => FSharp.Compiler.Service.ProjectCracker.Exe.fsproj} (98%) rename src/fsharp/FSharp.Compiler.Service.ProjectCrackerReader/{FSharp.Compiler.Service.ProjectCrackerReader.fsproj => FSharp.Compiler.Service.ProjectCracker.fsproj} (90%) diff --git a/FSharp.Compiler.Service.sln b/FSharp.Compiler.Service.sln index f1e082a171..681531ba4e 100644 --- a/FSharp.Compiler.Service.sln +++ b/FSharp.Compiler.Service.sln @@ -57,9 +57,9 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Fsc", "samples\FscExe\Fsc.f EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharp_Analysis", "tests\service\data\CSharp_Analysis\CSharp_Analysis.csproj", "{887630A3-4B1D-40EA-B8B3-2D842E9C40DB}" EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Service.ProjectCracker", "src\fsharp\FSharp.Compiler.Service.ProjectCracker\FSharp.Compiler.Service.ProjectCracker.fsproj", "{B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}" +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Service.ProjectCracker.Exe", "src\fsharp\FSharp.Compiler.Service.ProjectCracker\FSharp.Compiler.Service.ProjectCracker.Exe.fsproj", "{B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}" EndProject -Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Service.ProjectCrackerReader", "src\fsharp\FSharp.Compiler.Service.ProjectCrackerReader\FSharp.Compiler.Service.ProjectCrackerReader.fsproj", "{893C3CD9-5AF8-4027-A667-21E62FC2C703}" +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Service.ProjectCracker", "src\fsharp\FSharp.Compiler.Service.ProjectCrackerReader\FSharp.Compiler.Service.ProjectCracker.fsproj", "{893C3CD9-5AF8-4027-A667-21E62FC2C703}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/fsharp/FSharp.Compiler.Service.ProjectCracker/FSharp.Compiler.Service.ProjectCracker.fsproj b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/FSharp.Compiler.Service.ProjectCracker.Exe.fsproj similarity index 98% rename from src/fsharp/FSharp.Compiler.Service.ProjectCracker/FSharp.Compiler.Service.ProjectCracker.fsproj rename to src/fsharp/FSharp.Compiler.Service.ProjectCracker/FSharp.Compiler.Service.ProjectCracker.Exe.fsproj index 8359059fe2..fd95a9ec32 100644 --- a/src/fsharp/FSharp.Compiler.Service.ProjectCracker/FSharp.Compiler.Service.ProjectCracker.fsproj +++ b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/FSharp.Compiler.Service.ProjectCracker.Exe.fsproj @@ -18,7 +18,7 @@ v4.5 true 4.3.1.0 - FSharp.Compiler.Service.ProjectCracker + FSharp.Compiler.Service.ProjectCracker.Exe true @@ -43,6 +43,27 @@ bin\Release\FSharp.Compiler.Service.ProjectCracker.XML true + + 11 + + + + + $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets + + + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets + + + + + + + + + @@ -75,31 +96,8 @@ - - - - - - - - - 11 - - - - - $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets - - - - - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets - - - -