diff --git a/FSharp.Compiler.Service.sln b/FSharp.Compiler.Service.sln index d00e651dc0..03de2676e4 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,10 @@ 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.Exe", "src\fsharp\FSharp.Compiler.Service.ProjectCracker.Exe\FSharp.Compiler.Service.ProjectCracker.Exe.fsproj", "{B1BDD96D-47E1-4E65-8107-FBAE23A06DB4}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FSharp.Compiler.Service.ProjectCracker", "src\fsharp\FSharp.Compiler.Service.ProjectCracker\FSharp.Compiler.Service.ProjectCracker.fsproj", "{893C3CD9-5AF8-4027-A667-21E62FC2C703}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -190,6 +194,36 @@ 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 + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Debug|Any CPU.Build.0 = Debug|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Debug|x86.ActiveCfg = Debug|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Proto|Any CPU.ActiveCfg = Release|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Proto|Any CPU.Build.0 = Release|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Proto|Mixed Platforms.ActiveCfg = Release|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Proto|Mixed Platforms.Build.0 = Release|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Proto|x86.ActiveCfg = Release|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Release|Any CPU.ActiveCfg = Release|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Release|Any CPU.Build.0 = Release|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {893C3CD9-5AF8-4027-A667-21E62FC2C703}.Release|x86.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/build.fsx b/build.fsx index 473f895196..b1e59a3d0a 100644 --- a/build.fsx +++ b/build.fsx @@ -141,6 +141,12 @@ Target "NuGet" (fun _ -> Version = release.NugetVersion OutputPath = buildDir ReleaseNotes = toLines release.Notes }) + Paket.Pack (fun p -> + { p with + TemplateFile = "nuget/projectcracker.template" + Version = release.NugetVersion + OutputPath = buildDir + ReleaseNotes = toLines release.Notes }) ) diff --git a/nuget/projectcracker.template b/nuget/projectcracker.template new file mode 100644 index 0000000000..4a15816b51 --- /dev/null +++ b/nuget/projectcracker.template @@ -0,0 +1,21 @@ +type file +id FSharp.Compiler.Service.ProjectCracker +description + Adds cracking capabilities. +authors + Microsoft Corporation, Robin Neatherway +summary + Cracking projects +licenseurl https://github.com/fsharp/FSharp.Compiler.Service/blob/master/LICENSE +projecturl https://github.com/fsharp/FSharp.Compiler.Service +iconurl https://raw.github.com/fsharp/FSharp.Compiler.Service/master/misc/logo.png +tags + F#, fsharp, msbuild, editor +files + ../bin/v4.5/FSharp.Compiler.Service.ProjectCracker.Exe.exe ==> lib/net45 + ../bin/v4.5/FSharp.Compiler.Service.ProjectCracker.Exe.?db ==> lib/net45 + ../bin/v4.5/FSharp.Compiler.Service.ProjectCracker.Exe.exe.?db ==> lib/net45 + ../bin/v4.5/FSharp.Compiler.Service.ProjectCracker.dll ==> lib/net45 + ../bin/v4.5/FSharp.Compiler.Service.ProjectCracker.XML ==> lib/net45 + ../bin/v4.5/FSharp.Compiler.Service.ProjectCracker.?db ==> lib/net45 + ../bin/v4.5/FSharp.Compiler.Service.ProjectCracker.dll.?db ==> lib/net45 \ No newline at end of file diff --git a/src/fsharp/FSharp.Compiler.Service.ProjectCracker.Exe/App.config b/src/fsharp/FSharp.Compiler.Service.ProjectCracker.Exe/App.config new file mode 100644 index 0000000000..3c6dcfe227 --- /dev/null +++ b/src/fsharp/FSharp.Compiler.Service.ProjectCracker.Exe/App.config @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/fsharp/FSharp.Compiler.Service.ProjectCracker.Exe/FSharp.Compiler.Service.ProjectCracker.Exe.fsproj b/src/fsharp/FSharp.Compiler.Service.ProjectCracker.Exe/FSharp.Compiler.Service.ProjectCracker.Exe.fsproj new file mode 100644 index 0000000000..bfc08be931 --- /dev/null +++ b/src/fsharp/FSharp.Compiler.Service.ProjectCracker.Exe/FSharp.Compiler.Service.ProjectCracker.Exe.fsproj @@ -0,0 +1,96 @@ + + + + + Debug + AnyCPU + 2.0 + b1bdd96d-47e1-4e65-8107-fbae23a06db4 + Exe + FSharp.Compiler.Service.ProjectCracker.Exe + FSharp.Compiler.Service.ProjectCracker.Exe + v4.5 + 4.3.0.0 + ..\..\..\ + FSharp.Compiler.Service.ProjectCracker.Exe + $(OtherFlags) --staticlink:FSharp.Core + $(NoWarn);40 + true + FSharp.Compiler.Service.ProjectCracker.Exe + ..\..\..\bin\$(TargetFrameworkVersion) + ..\..\..\bin\$(TargetFrameworkVersion)\FSharp.Compiler.Service.ProjectCracker.Exe.XML + + + true + full + false + false + DEBUG;TRACE + 3 + AnyCPU + true + + + pdbonly + true + true + TRACE + 3 + AnyCPU + true + + + 11 + + + + + $(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets + + + + + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets + + + + + + + + + + + + + + + True + + + True + + + True + + + + + False + + + + + + + + + + + \ No newline at end of file diff --git a/src/fsharp/FSharp.Compiler.Service.ProjectCracker.Exe/Program.fs b/src/fsharp/FSharp.Compiler.Service.ProjectCracker.Exe/Program.fs new file mode 100644 index 0000000000..7659db1804 --- /dev/null +++ b/src/fsharp/FSharp.Compiler.Service.ProjectCracker.Exe/Program.fs @@ -0,0 +1,451 @@ +namespace FSharp.Compiler.Service.ProjectCracker.Exe + +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 + +type ProjectOptions = + { + ProjectFile: string + Options: string[] + ReferencedProjectOptions: (string * ProjectOptions)[] + LogOutput: string + } + +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() + 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, _ -> () |] + + // Workaround for Mono 4.2, which doesn't populate the subproject + // details anymore outside of a solution context. See https://github.com/mono/mono/commit/76c6a08e730393927b6851709cdae1d397cbcc3a#diff-59afd196a55d61d5d1eaaef7bd49d1e5 + // and some explanation from the author at https://github.com/fsharp/FSharp.Compiler.Service/pull/455#issuecomment-154103963 + // + // In particular we want the output path, which we can get from + // fully parsing that project itself. We also have to specially parse + // C# referenced projects, as we don't look at them otherwise. + let referencedProjectOutputs = + if runningOnMono then + [| yield! Array.map (fun (s,_) -> "-r:" + s) referencedProjectOptions + for file in parsedProject.ProjectReferences do + let ext = Path.GetExtension(file) + if ext = ".csproj" || ext = ".vbproj" then + let parsedProject = FSharpProjectFileInfo.Parse(file, properties=properties, enableLogging=false) + match parsedProject.OutputFile with + | None -> () + | Some f -> yield "-r:" + f |] + else + [||] + + let options = { ProjectFile = file + Options = Array.append (Array.ofList (parsedProject.Options)) + referencedProjectOutputs + 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 text = Array.exists (fun (s: string) -> s = "--text") argv + let argv = Array.filter (fun (s: string) -> s <> "--text") argv + + let ret, opts = + if argv.Length >= 2 then + let projectFile = argv.[0] + let enableLogging = match Boolean.TryParse(argv.[1]) with + | true, true -> true + | _ -> false + try + addMSBuildv14BackupResolution () + let props = pairs (List.ofArray argv.[2..]) + let opts = getOptions argv.[0] enableLogging props + 0, opts + with e -> + 2, { ProjectFile = projectFile; + Options = [||]; + ReferencedProjectOptions = [||]; + LogOutput = e.ToString() } + else + 1, { ProjectFile = ""; + Options = [||]; + ReferencedProjectOptions = [||]; + LogOutput = "At least two arguments required." } + + if text then + printfn "%A" opts + else + let fmt = new BinaryFormatter() + use out = new StreamWriter(System.Console.OpenStandardOutput()) + fmt.Serialize(out.BaseStream, opts) + ret 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..2b1f1e14aa --- /dev/null +++ b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/FSharp.Compiler.Service.ProjectCracker.fsproj @@ -0,0 +1,85 @@ + + + + + Debug + AnyCPU + 2.0 + 893c3cd9-5af8-4027-a667-21e62fc2c703 + Library + FSharp.Compiler.Service.ProjectCracker + FSharp.Compiler.Service.ProjectCracker + v4.5 + ..\..\..\ + 4.3.0.0 + FSharp.Compiler.Service.ProjectCracker + ..\..\..\bin\$(TargetFrameworkVersion) + ..\..\..\bin\$(TargetFrameworkVersion)\FSharp.Compiler.Service.ProjectCracker.XML + + + true + full + false + false + DEBUG;TRACE + 3 + AnyCPU + true + + + pdbonly + true + true + TRACE + 3 + AnyCPU + true + + + + + False + + + + + + + + + + + FSharp.Compiler.Service.ProjectCracker.Exe + {b1bdd96d-47e1-4e65-8107-fbae23a06db4} + True + + + FSharp.Compiler.Service + {2e4d67b4-522d-4cf7-97e4-ba940f0b18f3} + True + + + + 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/ProjectCracker.fs b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/ProjectCracker.fs new file mode 100644 index 0000000000..c0fdafb216 --- /dev/null +++ b/src/fsharp/FSharp.Compiler.Service.ProjectCracker/ProjectCracker.fs @@ -0,0 +1,53 @@ +namespace FSharp.Compiler.Service + +open System.Diagnostics +open System.Text +open System.IO +open System +open System.Runtime + +open Microsoft.FSharp.Compiler.SourceCodeServices + +type ProjectCracker = + + 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 logMap = ref Map.empty + + let rec convert (opts: FSharp.Compiler.Service.ProjectCracker.Exe.ProjectOptions) : FSharpProjectOptions = + let referencedProjects = Array.map (fun (a, b) -> a, convert b) opts.ReferencedProjectOptions + logMap := Map.add opts.ProjectFile opts.LogOutput !logMap + { 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.exe") + p.StartInfo.Arguments <- arguments.ToString() + p.StartInfo.UseShellExecute <- false + p.StartInfo.CreateNoWindow <- true + p.StartInfo.RedirectStandardOutput <- true + ignore <| p.Start() + + let fmt = new Serialization.Formatters.Binary.BinaryFormatter() + let opts = fmt.Deserialize(p.StandardOutput.BaseStream) :?> FSharp.Compiler.Service.ProjectCracker.Exe.ProjectOptions + p.WaitForExit() + + convert opts, !logMap + + static member GetProjectOptionsFromProjectFile(projectFileName : string, ?properties : (string * string) list, ?loadedTimeStamp) = + fst (ProjectCracker.GetProjectOptionsFromProjectFileLogged(projectFileName, ?properties=properties, ?loadedTimeStamp=loadedTimeStamp)) diff --git a/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj b/src/fsharp/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj index 726aff06b8..6b492fbcf1 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 @@ - +