From f1e4fb3280a2bf7aa9b991242b444529ea997819 Mon Sep 17 00:00:00 2001 From: Xavier Zwirtz Date: Thu, 12 Feb 2015 22:23:13 -0600 Subject: [PATCH 1/8] Included FunScript.Compiler in solution. --- src/FunScript.sln | 8 +++++++- src/extra/FunScript.Compiler/FunScript.Compiler.fsproj | 7 +++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/FunScript.sln b/src/FunScript.sln index 94612b8..de74a11 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,8 @@ 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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -39,6 +41,10 @@ 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 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj b/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj index 1c3ca3b..9f7c0fb 100644 --- a/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj +++ b/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj @@ -67,9 +67,12 @@ - + + + FunScript - E0916E67-D3B0-4C3A-AD18-4146882FCEDD + {e0916e67-d3b0-4c3a-ad18-4146882fcedd} + True + \ 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/main/FunScript/Attributes/AssemblyMain.fs b/src/main/FunScript/Attributes/AssemblyMain.fs new file mode 100644 index 0000000..f3a5c35 --- /dev/null +++ b/src/main/FunScript/Attributes/AssemblyMain.fs @@ -0,0 +1,4 @@ +namespace FunScript + +type AssemblyMain() = + inherit System.Attribute() \ No newline at end of file diff --git a/src/main/FunScript/Compiler.fs b/src/main/FunScript/Compiler.fs index 698e1ac..b46238e 100644 --- a/src/main/FunScript/Compiler.fs +++ b/src/main/FunScript/Compiler.fs @@ -1,6 +1,7 @@ module FunScript.Compiler open FunScript +open System.Reflection //TODO: Use IoC here. MiniIoC perhaps? let createComponents(isEventMappingEnabled) = @@ -58,6 +59,27 @@ type Compiler = static member Compile(expression, ?components, ?noReturn, ?shouldCompress, ?isEventMappingEnabled) = let components = defaultArg components [] Compiler.CompileImpl(expression, (fun existingComponents -> existingComponents @ components), noReturn, shouldCompress, isEventMappingEnabled) - + + static member CompileAssembly(assembly : Assembly, ?noReturn: bool, ?shouldCompress : bool, ?isEventMappingEnabled: bool) = + let assemblyMains = + let types = assembly.GetTypes() + let flags = BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Static + let mains = + [ for typ in types do + for mi in typ.GetMethods(flags) do + if System.Attribute.IsDefined(mi, typedefof) then yield mi ] + mains + + match assemblyMains with + | _ when assemblyMains.Length > 1 -> + invalidOp "More than one method marked with [] found." + | [] -> + invalidOp "No method marked with [] found." + | [main] -> + let mainExpr = Microsoft.FSharp.Quotations.Expr.Call(main, []) + Compiler.CompileImpl(mainExpr, (fun existingComponents -> existingComponents), noReturn, shouldCompress, isEventMappingEnabled) + | _ -> + failwith "never" + let compile expr = Compiler.Compile(expr) -let compileWithoutReturn expr = Compiler.Compile(expr, noReturn=true) +let compileWithoutReturn expr = Compiler.Compile(expr, noReturn=true) \ No newline at end of file diff --git a/src/main/FunScript/FunScript.fsproj b/src/main/FunScript/FunScript.fsproj index 9caf8df..dcff42c 100644 --- a/src/main/FunScript/FunScript.fsproj +++ b/src/main/FunScript/FunScript.fsproj @@ -49,6 +49,7 @@ + diff --git a/src/tests/FunScript.Tests/AssemblyCompileTests.fs b/src/tests/FunScript.Tests/AssemblyCompileTests.fs new file mode 100644 index 0000000..c1cc284 --- /dev/null +++ b/src/tests/FunScript.Tests/AssemblyCompileTests.fs @@ -0,0 +1,24 @@ +#if INTERACTIVE +#load "Interactive.fsx" +open FunScript.Tests.Common +#endif +[] +module FunScript.Tests.CompileTests + +open System +open System.Reflection +open NUnit.Framework + + +[] +let ``Project compiles to js``() = + let compiled = FunScript.Compiler.Compiler.CompileAssembly(Assembly.Load("Simple"), false, false, true) + let expected = + """var Page__main$; + Page__main$ = (function(unitVar0) + { + var foo = "bar"; + return foo; + }); + return Page__main$()""" + Assert.AreEqual(expected, compiled) \ No newline at end of file diff --git a/src/tests/FunScript.Tests/FunScript.Tests.fsproj b/src/tests/FunScript.Tests/FunScript.Tests.fsproj index b336f33..62c0811 100644 --- a/src/tests/FunScript.Tests/FunScript.Tests.fsproj +++ b/src/tests/FunScript.Tests/FunScript.Tests.fsproj @@ -83,6 +83,7 @@ + @@ -136,6 +137,21 @@ True + + Simple + {e8eda6fd-20d3-4849-95da-ed2b0ae0a869} + True + + + WithBindings + {5bb88a86-e7de-43ba-b809-a0dddad7f21c} + True + + + FunScript.Compiler + {4aad0f56-af0f-46bd-811e-fec88ebed669} + True + FunScript.Rx {3d25cab2-83a4-4d3b-9986-ac068fe29307} From 4827b0106a5c48dfee2f3771f01a3777ba952a6d Mon Sep 17 00:00:00 2001 From: Xavier Zwirtz Date: Mon, 16 Feb 2015 13:50:22 -0600 Subject: [PATCH 5/8] Switched FunScript.Compiler to not reference FunScript itself. Instead uses project its compiling FunScript reference. --- src/FunScript.sln | 18 +++ src/extra/FunScript.Compiler/Compile.fs | 124 ++++++++++-------- .../FunScript.Compiler.fsproj | 9 +- src/extra/FunScript.Compiler/Program.fs | 6 +- 4 files changed, 91 insertions(+), 66 deletions(-) diff --git a/src/FunScript.sln b/src/FunScript.sln index de74a11..9e5bfa2 100644 --- a/src/FunScript.sln +++ b/src/FunScript.sln @@ -15,6 +15,12 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "FunScript.Rx", "extra\FunSc 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 @@ -45,10 +51,22 @@ Global {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/extra/FunScript.Compiler/Compile.fs b/src/extra/FunScript.Compiler/Compile.fs index 614a21b..cc4d761 100644 --- a/src/extra/FunScript.Compiler/Compile.fs +++ b/src/extra/FunScript.Compiler/Compile.fs @@ -11,59 +11,64 @@ 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 - let AssemblyToFunScript (assembly : Assembly) = - // Find the main method in this assembly - // Could offer lot more flexiblity here ... - let mainCompileExpr = - printfn "Searching for main function..." -// let types = Assembly.GetExecutingAssembly().GetTypes() -// let flags = BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Static -// let mains = -// [ for typ in types do -// for mi in typ.GetMethods(flags) do -// if mi.Name = "compileMethod" then yield mi ] - let types = assembly.GetTypes() - let flags = BindingFlags.NonPublic ||| BindingFlags.Public ||| BindingFlags.Static - let mains = - [ for typ in types do - for mi in typ.GetMethods(flags) do - if mi.Name = "main" then yield mi ] - let mainExpr = - match mains with - | [it] -> Some(Expr.Call(it, [])) - //| [it] -> Expr.TryGetReflectedDefinition(it) - | _ -> None - mainExpr - -// let testExpr = -// <@@ -// let x = true -// x -// @@> - - match mainCompileExpr with - | None -> - {Success=false;Errors=[{Message="Could not find main function in any module."}];CompiledFunScript=""} - | _ -> - let sw = System.Diagnostics.Stopwatch.StartNew() - let source = FunScript.Compiler.Compiler.Compile(mainCompileExpr.Value, []) - printfn "Generated JavaScript in %f sec..." (float sw.ElapsedMilliseconds / 1000.0) - {Success=true;Errors=Seq.empty;CompiledFunScript=source} + 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} - type ExceptionOrAssembly= E of System.Exception | A of Assembly - let LoadDependencies (dependencyPaths : seq) = let unorderedDependencies = @@ -110,18 +115,18 @@ compileDomain.add_AssemblyResolve(handler) *) - let results : seq = + let results : seq> = TopSort(unorderedDependencies, pred, comparer) |> Seq.map(fun d -> try - A(Assembly.LoadFrom(d.Path)) + T(Assembly.LoadFrom(d.Path)) with | ex -> - E(ex)) + Exception(ex)) results - let ProjectToFunScript (projectPath: string) = + let ProjectToFunScript (projectPath: string) : CompileResult = let checker = FSharpChecker.Create() let projectOpts = checker.GetProjectOptionsFromProjectFile(projectPath) @@ -133,12 +138,12 @@ let dependencyResults = LoadDependencies(dependencyPaths) let loadErrors = dependencyResults |> Seq.choose(fun x -> match x with - | A _ -> None - | E e -> Some e) + | T _ -> None + | Exception e -> Some e) let dependencies = dependencyResults |> Seq.choose(fun x -> match x with - | E _ -> None - | A a -> Some a) + | Exception _ -> None + | T a -> Some a) match loadErrors with |errors when Seq.length(errors) > 0 -> @@ -149,10 +154,17 @@ 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, assembly = scs.CompileToDynamicAssembly(Seq.toArray options, Some(stdout, stderr)) let errors, exitCode = scs.Compile(Seq.toArray options) let handler = System.ResolveEventHandler(fun sender -> @@ -162,15 +174,17 @@ ) System.AppDomain.CurrentDomain.add_AssemblyResolve(handler) -// let errors = [] -// let exitCode = 0 - let assembly = - Assembly.LoadFrom("C:\\Projects\\FunScript.CommandLine\\FunScript.CommandLine.Example\\bin\\debug\\FunScript.CommandLine.Example.dll") + let outAssembly = + Assembly.LoadFrom(outAssemblyPath) |> Some match exitCode with | i when (i < 1) -> - AssemblyToFunScript(assembly.Value) + 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 diff --git a/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj b/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj index 759c517..fc774c8 100644 --- a/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj +++ b/src/extra/FunScript.Compiler/FunScript.Compiler.fsproj @@ -28,7 +28,7 @@ true true - --project-path "C:\Projects\FunScript.CommandLine\FunScript.CommandLine.Example\FunScript.CommandLine.Example.fsproj" --std-out + --project-path "C:\Projects\FunScript\src\compileTestData\WithBindings\WithBindings.fsproj" --std-out pdbonly @@ -93,13 +93,6 @@ - - - FunScript - {e0916e67-d3b0-4c3a-ad18-4146882fcedd} - True - -