public async Task <Tuple <string, Project> > CompileProject(string projectFile) { var projectFileInfo = new FileInfo(projectFile); var projectFolder = projectFileInfo.Directory.FullName; // These two lines are just a weird hack because you get no files back from compilation.SyntaxTrees // if the user file isn't modified. Not sure why that's happening. // var projectUserFile = projectFolder + "\\" + projectFileInfo.Name + ".user"; // if (File.Exists(projectUserFile)) // File.SetLastWriteTime(projectUserFile, DateTime.Now); var jsCompilationUnit = new JsCompilationUnit { UseStrict = true }; var workspace = MSBuildWorkspace.Create(); var project = await Profiler.Time("Loading Project", async() => { string mscorlib = this.mscorlib; if (mscorlib == null) { mscorlib = FileUtils.GetWootzJsTargetFile(projectFile); } Project result; if (mscorlib != null) { var mscorlibProject = await workspace.OpenProjectAsync(mscorlib); result = await workspace.OpenProjectAsync(projectFile); result = result.AddProjectReference(new ProjectReference(mscorlibProject.Id)); result = result.RemoveMetadataReference(result.MetadataReferences.Single(x => x.Display.Contains("mscorlib.dll"))); } else { result = await workspace.OpenProjectAsync(projectFile); } return(result); }); var projectCompiler = new ProjectCompiler(project, jsCompilationUnit, defines); await projectCompiler.Compile(); // Write out the compiled Javascript file to the target location. var renderer = new JsRenderer(); // renderer.Builder.IsCompacting = true; Profiler.Time("Rendering javascript", () => jsCompilationUnit.Accept(renderer)); return(Tuple.Create(renderer.Output, project)); }
public async Task <Tuple <string, Project> > Compile(string projectFile) { var projectFileInfo = new FileInfo(projectFile); var projectFolder = projectFileInfo.Directory.FullName; // These two lines are just a weird hack because you get no files back from compilation.SyntaxTrees // if the user file isn't modified. Not sure why that's happening. var projectUserFile = projectFolder + "\\" + projectFileInfo.Name + ".user"; if (File.Exists(projectUserFile)) { File.SetLastWriteTime(projectUserFile, DateTime.Now); } var project = await MSBuildWorkspace.Create().OpenProjectAsync(projectFile); var projectName = project.AssemblyName; Compilation compilation = await project.GetCompilationAsync(); Context.Update(project.Solution, project, compilation); // Check for yield foreach (var syntaxTree in compilation.SyntaxTrees) { var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot(); var semanticModel = compilation.GetSemanticModel(syntaxTree); var yieldGenerator = new YieldGenerator(compilation, syntaxTree, semanticModel); compilationUnit = (CompilationUnitSyntax)compilationUnit.Accept(yieldGenerator); compilation = compilation.ReplaceSyntaxTree(syntaxTree, SyntaxFactory.SyntaxTree(compilationUnit, syntaxTree.FilePath)); } compilation = compilation.Clone(); Context.Update(project.Solution, project, compilation); // After the basic transformation happens, we need to fix up some references afterward foreach (var syntaxTree in compilation.SyntaxTrees) { var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot(); var semanticModel = compilation.GetSemanticModel(syntaxTree); var yieldFixer = new YieldGeneratorFixer(compilation, syntaxTree, semanticModel); compilationUnit = (CompilationUnitSyntax)compilationUnit.Accept(yieldFixer); compilation = compilation.ReplaceSyntaxTree(syntaxTree, SyntaxFactory.SyntaxTree(compilationUnit, syntaxTree.FilePath)); } Context.Update(project.Solution, project, compilation); // Check for async foreach (var syntaxTree in compilation.SyntaxTrees) { var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot(); var semanticModel = compilation.GetSemanticModel(syntaxTree); var asyncGenerator = new AsyncGenerator(compilation, syntaxTree, semanticModel); compilationUnit = (CompilationUnitSyntax)compilationUnit.Accept(asyncGenerator); compilation = compilation.ReplaceSyntaxTree(syntaxTree, SyntaxFactory.SyntaxTree(compilationUnit, syntaxTree.FilePath)); } Context.Update(project.Solution, project, compilation); var jsCompilationUnit = new JsCompilationUnit { UseStrict = true }; // If this is the runtime prjoect, declare the array to hold all the GetAssembly functions (this .js file // will be loaded first, and we only want to bother creating the array once. if (projectName == "mscorlib") { var assemblies = Js.Variable(SpecialNames.Assemblies, Js.Array()); jsCompilationUnit.Body.Local(assemblies); // This ensures that Function.$typeName returns `Function` -- this is important when using // a type function as a generic argument, since otherwise when we try to assembly a // unique key for the permuatation of type args including a type function, we would get // an empty string for that arg, which would break the cache. jsCompilationUnit.Body.Assign(Js.Reference("Function").Member(SpecialNames.TypeName), Js.Primitive("Function")); } // Declare assembly variable var assemblyVariable = Js.Variable("$" + projectName.MaskSpecialCharacters() + "$Assembly", Js.Null()); jsCompilationUnit.Body.Local(assemblyVariable); // Declare array to store all anonymous types var anonymousTypes = Js.Variable(compilation.Assembly.GetAssemblyAnonymousTypesArray(), Js.Array()); jsCompilationUnit.Body.Local(anonymousTypes); // Declare array to store all the type functions in the assembly var assemblyTypes = Js.Variable(compilation.Assembly.GetAssemblyTypesArray(), Js.Array()); jsCompilationUnit.Body.Local(assemblyTypes); // Build $GetAssemblyMethod, which lazily creates a new Assembly instance var globalIdioms = new Idioms(null); var getAssembly = Js.Function(); getAssembly.Body.If( assemblyVariable.GetReference().EqualTo(Js.Null()), assemblyVariable.GetReference().Assign(globalIdioms.CreateAssembly(compilation.Assembly, assemblyTypes.GetReference())) ); getAssembly.Body.Return(assemblyVariable.GetReference()); jsCompilationUnit.Body.Assign( Js.Reference(compilation.Assembly.GetAssemblyMethodName()), getAssembly); // Add $GetAssemblyMethod to global assemblies array jsCompilationUnit.Body.Express(Js.Reference("$assemblies").Member("push").Invoke(Js.Reference(compilation.Assembly.GetAssemblyMethodName()))); // Builds out all the namespace objects. Types live inside namepsaces, which are represented as // nested Javascript objects. For example, System.Text.StringBuilder is represented (in part) as: // // System = {}; // System.Text = {}; // System.Text.StringBuilder = function() { ... } // // This allows access to classes using dot notation in the expected way. var namespaceTransformer = new NamespaceTransformer(jsCompilationUnit.Body); foreach (var syntaxTree in compilation.SyntaxTrees) { var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot(); compilationUnit.Accept(namespaceTransformer); } var actions = new List <Tuple <INamedTypeSymbol, Action> >(); // Scan all syntax trees for anonymous type creation expressions. We transform them into class // declarations with a series of auto implemented properties. var anonymousTypeTransformer = new AnonymousTypeTransformer(jsCompilationUnit.Body, actions); foreach (var syntaxTree in compilation.SyntaxTrees) { var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot(); compilationUnit.Accept(anonymousTypeTransformer); } var diagnostics = compilation.GetDiagnostics(); foreach (var diagnostic in diagnostics) { Console.WriteLine("// " + diagnostic); } // Iterate through all the syntax trees and add entries into `actions` that correspond to type // declarations. foreach (var syntaxTree in compilation.SyntaxTrees) { var semanticModel = compilation.GetSemanticModel(syntaxTree); var compilationUnit = (CompilationUnitSyntax)syntaxTree.GetRoot(); var transformer = new JsTransformer(syntaxTree, semanticModel); var typeDeclarations = GetTypeDeclarations(compilationUnit); foreach (var type in typeDeclarations) { Action action = () => { var statements = (JsBlockStatement)type.Accept(transformer); jsCompilationUnit.Body.Aggregate(statements); }; actions.Add(Tuple.Create((INamedTypeSymbol)ModelExtensions.GetDeclaredSymbol(semanticModel, type), action)); } var delegateDeclarations = GetDelegates(compilationUnit); foreach (var type in delegateDeclarations) { Action action = () => { var statements = (JsBlockStatement)type.Accept(transformer); jsCompilationUnit.Body.Aggregate(statements); }; actions.Add(Tuple.Create((INamedTypeSymbol)ModelExtensions.GetDeclaredSymbol(semanticModel, type), action)); } } // Sort all the type declarations such that base types always come before subtypes. SweepSort(actions); foreach (var item in actions) { item.Item2(); } // If the project type is a console application, then invoke the Main method at the very // end of the file. var entryPoint = compilation.GetEntryPoint(CancellationToken.None); if (entryPoint != null) { jsCompilationUnit.Body.Express(globalIdioms.InvokeStatic(entryPoint)); } // Test minification // var minifier = new JsMinifier(); // jsCompilationUnit.Accept(minifier); // Write out the compiled Javascript file to the target location. var renderer = new JsRenderer(); jsCompilationUnit.Accept(renderer); return(Tuple.Create(renderer.Output, project)); }
public async Task <Tuple <string, Solution> > CompileSolution(string solutionFile) { // var solution = await Profiler.Time("Loading Project", async () => await MSBuildWorkspace.Create().OpenSolutionAsync(solutionFile)); var jsCompilationUnit = new JsCompilationUnit { UseStrict = true }; // Since we have all the types required by the solution, we can keep track of which symbols are used and which // are not. This of course means depending on reflection where you don't reference the actual symbol in code // will result in unexpected behavior. RemoveUnusedSymbols = true; var projectFiles = FileUtils.GetProjectFiles(solutionFile); var workspace = MSBuildWorkspace.Create(); var solution = await Profiler.Time("Loading Solution", async() => { string mscorlib = this.mscorlib; if (mscorlib == null) { mscorlib = projectFiles.Select(x => FileUtils.GetWootzJsTargetFile(x)).First(); } Solution result = workspace.CurrentSolution; if (mscorlib != null) { var mscorlibProject = await workspace.OpenProjectAsync(mscorlib); // result = result.AddProject(mscorlibProject.Id, mscorlibProject.Name, mscorlibProject.AssemblyName, mscorlibProject.Language); foreach (var projectFile in projectFiles) { var project = result.Projects.SingleOrDefault(x => x.FilePath.Equals(projectFile, StringComparison.InvariantCultureIgnoreCase)); if (project == null) { project = await workspace.OpenProjectAsync(projectFile); } // result = result.AddProject(project.Id, project.Name, project.AssemblyName, project.Language); // project = result.GetProject(project.Id); project = project.AddProjectReference(new ProjectReference(mscorlibProject.Id)); project = project.RemoveMetadataReference(project.MetadataReferences.Single(x => x.Display.Contains("mscorlib.dll"))); result = project.Solution; } } else { result = await workspace.OpenSolutionAsync(solutionFile); } return(result); }); var projectCompilers = SortProjectsByDependencies(solution).Select(x => new ProjectCompiler(x, jsCompilationUnit, defines)).ToArray(); foreach (var compiler in projectCompilers) { await compiler.Compile(); } // Write out the compiled Javascript file to the target location. var renderer = new JsRenderer(); Profiler.Time("Rendering javascript", () => jsCompilationUnit.Accept(renderer)); // solution.Projects return(Tuple.Create(renderer.Output, solution)); }