diff --git a/src/FunScript.sln b/src/FunScript.sln index 94612b8..9e5bfa2 100644 --- a/src/FunScript.sln +++ b/src/FunScript.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.30723.0 +VisualStudioVersion = 12.0.30324.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FunScript", "main\FunScript\FunScript.fsproj", "{E0916E67-D3B0-4C3A-AD18-4146882FCEDD}" EndProject @@ -13,6 +13,14 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FunScript.Interop", "main\F EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FunScript.Rx", "extra\FunScript.Rx\FunScript.Rx.fsproj", "{3D25CAB2-83A4-4D3B-9986-AC068FE29307}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FunScript.Compiler", "extra\FunScript.Compiler\FunScript.Compiler.fsproj", "{4AAD0F56-AF0F-46BD-811E-FEC88EBED669}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "CompileTestData", "CompileTestData", "{71F5249A-EDF9-4C57-B3B8-3CEE26F8A9A6}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Simple", "compileTestData\simple\Simple.fsproj", "{E8EDA6FD-20D3-4849-95DA-ED2B0AE0A869}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WithBindings", "compileTestData\WithBindings\WithBindings.fsproj", "{5BB88A86-E7DE-43BA-B809-A0DDDAD7F21C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,10 +47,26 @@ Global {3D25CAB2-83A4-4D3B-9986-AC068FE29307}.Debug|Any CPU.Build.0 = Debug|Any CPU {3D25CAB2-83A4-4D3B-9986-AC068FE29307}.Release|Any CPU.ActiveCfg = Release|Any CPU {3D25CAB2-83A4-4D3B-9986-AC068FE29307}.Release|Any CPU.Build.0 = Release|Any CPU + {4AAD0F56-AF0F-46BD-811E-FEC88EBED669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4AAD0F56-AF0F-46BD-811E-FEC88EBED669}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4AAD0F56-AF0F-46BD-811E-FEC88EBED669}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4AAD0F56-AF0F-46BD-811E-FEC88EBED669}.Release|Any CPU.Build.0 = Release|Any CPU + {E8EDA6FD-20D3-4849-95DA-ED2B0AE0A869}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8EDA6FD-20D3-4849-95DA-ED2B0AE0A869}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8EDA6FD-20D3-4849-95DA-ED2B0AE0A869}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8EDA6FD-20D3-4849-95DA-ED2B0AE0A869}.Release|Any CPU.Build.0 = Release|Any CPU + {5BB88A86-E7DE-43BA-B809-A0DDDAD7F21C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5BB88A86-E7DE-43BA-B809-A0DDDAD7F21C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5BB88A86-E7DE-43BA-B809-A0DDDAD7F21C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5BB88A86-E7DE-43BA-B809-A0DDDAD7F21C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {E8EDA6FD-20D3-4849-95DA-ED2B0AE0A869} = {71F5249A-EDF9-4C57-B3B8-3CEE26F8A9A6} + {5BB88A86-E7DE-43BA-B809-A0DDDAD7F21C} = {71F5249A-EDF9-4C57-B3B8-3CEE26F8A9A6} + EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution $0.TextStylePolicy = $2 $1.inheritsSet = null diff --git a/src/compileTestData/Simple/App.config b/src/compileTestData/Simple/App.config new file mode 100644 index 0000000..a338d00 --- /dev/null +++ b/src/compileTestData/Simple/App.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/compileTestData/Simple/Program.fs b/src/compileTestData/Simple/Program.fs new file mode 100644 index 0000000..6154fb6 --- /dev/null +++ b/src/compileTestData/Simple/Program.fs @@ -0,0 +1,18 @@ +[] +module Page + +module x = + let makeHello who = + sprintf "hello %s" who + +module y = + let yMakeHello who = + x.makeHello who + module z = + let zMakeHello who = + x.makeHello who + +[] +let main() = + let hello = y.z.zMakeHello "world" + hello \ No newline at end of file diff --git a/src/compileTestData/Simple/Simple.fsproj b/src/compileTestData/Simple/Simple.fsproj new file mode 100644 index 0000000..627e8e8 --- /dev/null +++ b/src/compileTestData/Simple/Simple.fsproj @@ -0,0 +1,88 @@ + + + + + Debug + AnyCPU + 2.0 + e8eda6fd-20d3-4849-95da-ed2b0ae0a869 + Library + Simple + Simple + v4.5 + true + 4.3.1.0 + Simple + + + true + full + false + false + bin\Debug\ + DEBUG;TRACE + 3 + AnyCPU + bin\Debug\Simple.XML + true + + + pdbonly + true + true + bin\Release\ + TRACE + 3 + AnyCPU + bin\Release\Simple.XML + true + + + + + True + + + + + + + + + + + + FunScript.TypeScript + {164139cf-07d4-468f-b6a8-9b92504c38e4} + True + + + FunScript + {e0916e67-d3b0-4c3a-ad18-4146882fcedd} + 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/compileTestData/WithBindings/App.config b/src/compileTestData/WithBindings/App.config new file mode 100644 index 0000000..a338d00 --- /dev/null +++ b/src/compileTestData/WithBindings/App.config @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/compileTestData/WithBindings/Program.fs b/src/compileTestData/WithBindings/Program.fs new file mode 100644 index 0000000..0a89e7c --- /dev/null +++ b/src/compileTestData/WithBindings/Program.fs @@ -0,0 +1,15 @@ +[] +module Page + +open FunScript +open FunScript.TypeScript + +let hello () = + Globals.window.alert("Hello world!") + +let jq(selector : string) = Globals.Dollar.Invoke selector +let (?) jq name = jq("#" + name) + +[] +let main() = + jq?helloWorld.click(fun _ -> hello() :> obj) \ No newline at end of file diff --git a/src/compileTestData/WithBindings/WithBindings.fsproj b/src/compileTestData/WithBindings/WithBindings.fsproj new file mode 100644 index 0000000..831036d --- /dev/null +++ b/src/compileTestData/WithBindings/WithBindings.fsproj @@ -0,0 +1,97 @@ + + + + + Debug + AnyCPU + 2.0 + {5bb88a86-e7de-43ba-b809-a0dddad7f21c} + Library + WithBindings + WithBindings + v4.5 + true + 4.3.1.0 + WithBindings + + + true + full + false + false + bin\Debug\ + DEBUG;TRACE + 3 + AnyCPU + bin\Debug\WithBindings.XML + true + + + pdbonly + true + true + bin\Release\ + TRACE + 3 + AnyCPU + bin\Release\WithBindings.XML + true + + + + ..\..\packages\FunScript.TypeScript.Binding.jquery.1.1.0.37\lib\net40\FunScript.TypeScript.Binding.jquery.dll + True + + + ..\..\packages\FunScript.TypeScript.Binding.lib.1.1.0.37\lib\net40\FunScript.TypeScript.Binding.lib.dll + True + + + + True + + + + + + + + + + + + + FunScript.TypeScript + {164139cf-07d4-468f-b6a8-9b92504c38e4} + True + + + FunScript + {e0916e67-d3b0-4c3a-ad18-4146882fcedd} + 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/compileTestData/WithBindings/packages.config b/src/compileTestData/WithBindings/packages.config new file mode 100644 index 0000000..7b1f00d --- /dev/null +++ b/src/compileTestData/WithBindings/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/extra/FunScript.Compiler/App.config b/src/extra/FunScript.Compiler/App.config index c9bdc98..a477f7d 100644 --- a/src/extra/FunScript.Compiler/App.config +++ b/src/extra/FunScript.Compiler/App.config @@ -1,4 +1,4 @@ - + @@ -6,10 +6,10 @@ - - - - + + + + diff --git a/src/extra/FunScript.Compiler/Compile.fs b/src/extra/FunScript.Compiler/Compile.fs new file mode 100644 index 0000000..cc4d761 --- /dev/null +++ b/src/extra/FunScript.Compiler/Compile.fs @@ -0,0 +1,196 @@ +module Compile + + open System.Reflection + open System.Collections.Generic + open Microsoft.FSharp.Quotations + open Microsoft.FSharp.Compiler.SourceCodeServices + open Mono.Cecil + + type CompileError = {Message:string} + type CompileResult = { + Success: bool + Errors: seq + CompiledFunScript: string + } with + static member MakeSingleError message = + {Success=false;Errors=[{Message=message}];CompiledFunScript=""} + static member MakeSuccess source = + {Success=true;Errors=Seq.empty;CompiledFunScript=source} + + [] + let compileMethod() = + let x = true + x + + type ExceptionOrT<'t> = + | Exception of System.Exception + | T of 't + + let AssemblyToFunScript (assembly : Assembly) (funScriptAssembly: Assembly) : CompileResult = + //let funScriptAssembly = Assembly.Load("FunScript") + let compilerTypeName = "FunScript.Compiler+Compiler" + let compileMethodName = "CompileAssembly"//"Compile" + + let getFunScriptType fullName = + funScriptAssembly.GetTypes() + |> Seq.tryFind(fun x -> x.FullName = fullName) + + let funScriptCompiler = getFunScriptType compilerTypeName + + match funScriptCompiler with + | None -> + CompileResult.MakeSingleError(sprintf "Could not find type \"%s\" in FunScript assembly" compilerTypeName) + | Some funScriptCompiler -> + let compileMethod = + funScriptCompiler.GetMethods(BindingFlags.Public ||| BindingFlags.Static) + |> Seq.tryFind(fun x -> x.Name = compileMethodName) + match compileMethod with + | None -> + CompileResult.MakeSingleError(sprintf "Could not find method \"%s\" on type \"%s\" in FunScript assembly" compileMethodName compilerTypeName) + | Some compileMethod -> + let sw = System.Diagnostics.Stopwatch.StartNew() + + let invokeParams : array = [| assembly; None; None; None |] + + let source : ExceptionOrT = + try + T(compileMethod.Invoke(null, invokeParams) :?> string) + with ex -> + Exception(ex) + + match source with + | Exception ex -> + CompileResult.MakeSingleError(sprintf "Exception during compile:\n%s" "" )//ex.ToString() + | T source -> + CompileResult.MakeSuccess(source) + + let FSharpErrorInfoPrettyMessage (error : Microsoft.FSharp.Compiler.FSharpErrorInfo) = + sprintf "%s %i:%i\n error: %s" error.FileName error.StartLineAlternate error.EndLineAlternate error.Message + + type AssemblyRefernce = {Path:string;ReflectedAssembly:AssemblyDefinition;AssemblyName:AssemblyName} + + let LoadDependencies (dependencyPaths : seq) = + + let unorderedDependencies = + dependencyPaths + |> Seq.map(fun path -> + let assem = AssemblyDefinition.ReadAssembly(path) + {Path=path;ReflectedAssembly=assem;AssemblyName=AssemblyName(assem.FullName)} + ) + + let TopSort(roots: seq<'T>, pred: 'T -> seq<'T>, nodeIdentity: IEqualityComparer<'T>) = + seq { + let visited = HashSet(nodeIdentity) + let rec visit node = + seq { + if visited.Add(node) then + for pN in pred node do + yield! visit pN + yield node + } + for node in roots do + yield! visit node + } + + let comparer = + HashIdentity.FromFunctions + (fun a -> hash a.Path) + (fun a b -> a.Path = b.Path) + + let pred (a: AssemblyRefernce) = + a.ReflectedAssembly.MainModule.AssemblyReferences + |> Seq.choose (fun r -> + let n = AssemblyName(r.FullName) + match unorderedDependencies |> Seq.tryFind(fun x -> x.AssemblyName.FullName = n.FullName) with + | None -> None + | Some assemRef -> + assemRef + |> Some) + + (* + Seperate compile domain support. + let handler = ResolveEventHandler(fun sender -> + fun eventArgs -> + null) + compileDomain.add_AssemblyResolve(handler) + *) + + let results : seq> = + TopSort(unorderedDependencies, pred, comparer) + |> Seq.map(fun d -> + try + T(Assembly.LoadFrom(d.Path)) + with + | ex -> + Exception(ex)) + + results + + let ProjectToFunScript (projectPath: string) : CompileResult = + let checker = FSharpChecker.Create() + let projectOpts = checker.GetProjectOptionsFromProjectFile(projectPath) + + let dependencyPaths = + projectOpts.OtherOptions + |> Seq.filter (fun x -> x.StartsWith("-r:")) + |> Seq.map (fun x -> x.Substring(3)) + + let dependencyResults = LoadDependencies(dependencyPaths) + let loadErrors = dependencyResults |> Seq.choose(fun x -> + match x with + | T _ -> None + | Exception e -> Some e) + let dependencies = dependencyResults |> Seq.choose(fun x -> + match x with + | Exception _ -> None + | T a -> Some a) + + match loadErrors with + |errors when Seq.length(errors) > 0 -> + let dependencyErrors = errors |> Seq.map(fun x -> {Message=x.Message}) + {Success=false;Errors=dependencyErrors;CompiledFunScript=""} + |_ -> + let scs = Microsoft.FSharp.Compiler.SimpleSourceCodeServices.SimpleSourceCodeServices() + let requiredOptions = Seq.ofList [ "--validate-type-providers" ] + + let baseOptions = Seq.ofArray projectOpts.OtherOptions + + let outParam = "--out:" + let outAssemblyPath = + baseOptions + |> Seq.find(fun x -> x.StartsWith outParam) + + let outAssemblyPath = outAssemblyPath.Substring(outParam.Length) + + let options = Seq.concat [baseOptions; requiredOptions] |> Seq.distinct + + //let errors, exitCode, assembly = scs.CompileToDynamicAssembly(Seq.toArray options, Some(stdout, stderr)) + let errors, exitCode = scs.Compile(Seq.toArray options) + + let handler = System.ResolveEventHandler(fun sender -> + fun eventArgs -> + dependencies |> Seq.find(fun x -> + x.FullName = eventArgs.Name) + ) + + System.AppDomain.CurrentDomain.add_AssemblyResolve(handler) + let outAssembly = + Assembly.LoadFrom(outAssemblyPath) + |> Some + + match exitCode with + | i when (i < 1) -> + match dependencies |> Seq.tryFind(fun x -> x.GetName().Name = "FunScript") with + | None -> + {Success=false;Errors=[{Message="Project must reference FunScript"}];CompiledFunScript=""} + | Some funScriptAssembly -> + AssemblyToFunScript outAssembly.Value funScriptAssembly + | _ -> + let compileErrors = + match errors with + | (_) when(Seq.isEmpty errors)-> + seq [{Message=sprintf "Compiler exited with code %i" exitCode}] + | _ -> + seq {for err in errors + do yield {Message=FSharpErrorInfoPrettyMessage(err)} } + {Success=false;Errors=compileErrors;CompiledFunScript=""} \ No newline at end of file diff --git a/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj b/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj index 1c3ca3b..fc774c8 100644 --- a/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj +++ b/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj @@ -26,6 +26,9 @@ AnyCPU bin\Debug\FunScript.Compiler.XML true + true + + --project-path "C:\Projects\FunScript\src\compileTestData\WithBindings\WithBindings.fsproj" --std-out pdbonly @@ -56,21 +59,39 @@ - + + + + + ..\..\packages\FSharp.Compiler.Service.0.0.82\lib\net45\FSharp.Compiler.Service.dll + True + True + + ..\..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.dll + True + + + ..\..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Mdb.dll + True + + + ..\..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Pdb.dll + True + + + ..\..\packages\Mono.Cecil.0.9.5.4\lib\net40\Mono.Cecil.Rocks.dll + True + - - FunScript - E0916E67-D3B0-4C3A-AD18-4146882FCEDD -