From ed4b246a032f1e9be50eae756830ef43d35828ca Mon Sep 17 00:00:00 2001 From: Youssef Victor Date: Tue, 20 Jun 2023 21:39:29 +0300 Subject: [PATCH] perf: Improve sync generator performance --- src/Uno.UWPSyncGenerator/DocGenerator.cs | 4 +- src/Uno.UWPSyncGenerator/Generator.cs | 104 ++++++++++++----------- src/Uno.UWPSyncGenerator/Program.cs | 84 +++++++++--------- 3 files changed, 95 insertions(+), 97 deletions(-) diff --git a/src/Uno.UWPSyncGenerator/DocGenerator.cs b/src/Uno.UWPSyncGenerator/DocGenerator.cs index 671c1d34a885..0a5ef2daed2c 100644 --- a/src/Uno.UWPSyncGenerator/DocGenerator.cs +++ b/src/Uno.UWPSyncGenerator/DocGenerator.cs @@ -25,7 +25,7 @@ class DocGenerator : Generator private IGrouping>[] _viewsGrouped; private HashSet<(string name, string namespaceString)> _kosherFrameworkViews; - public override void Build(string basePath, string baseName, string sourceAssembly) + public override async Task Build(string basePath, string baseName, string sourceAssembly) { _sb = new MarkdownStringBuilder(); @@ -36,7 +36,7 @@ public override void Build(string basePath, string baseName, string sourceAssemb try { - base.Build(basePath, baseName, sourceAssembly); + await base.Build(basePath, baseName, sourceAssembly); } catch (Exception e) { diff --git a/src/Uno.UWPSyncGenerator/Generator.cs b/src/Uno.UWPSyncGenerator/Generator.cs index c2ab28d42c73..74ce53019232 100644 --- a/src/Uno.UWPSyncGenerator/Generator.cs +++ b/src/Uno.UWPSyncGenerator/Generator.cs @@ -4,7 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; -using System.Text; +using System.Threading.Tasks; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.MSBuild; using Uno.Extensions; @@ -99,7 +99,7 @@ abstract class Generator private INamedTypeSymbol _iOSBaseSymbol; private INamedTypeSymbol _androidBaseSymbol; private INamedTypeSymbol _macOSBaseSymbol; - private Compilation _referenceCompilation; + private static Compilation s_referenceCompilation; private Compilation _unitTestsCompilation; private Compilation _netstdReferenceCompilation; @@ -136,36 +136,45 @@ abstract class Generator static Generator() { RegisterAssemblyLoader(); - } - - public virtual void Build(string basePath, string baseName, string sourceAssembly) - { Directory.SetCurrentDirectory(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); InitializeRoslyn(); + } + public virtual async Task Build(string basePath, string baseName, string sourceAssembly) + { Console.WriteLine($"Generating for {baseName} {sourceAssembly}"); - _referenceCompilation = LoadUWPReferenceProject(@"..\..\..\Uno.UWPSyncGenerator.Reference\references.txt"); + s_referenceCompilation ??= await LoadUWPReferenceProject(@"..\..\..\Uno.UWPSyncGenerator.Reference\references.txt"); + + _dependencyPropertySymbol = s_referenceCompilation.GetTypeByMetadataName(BaseXamlNamespace + ".DependencyProperty"); + + var iOSCompilationTask = LoadProject($@"{basePath}\{baseName}.netcoremobile.csproj", "net7.0-ios"); + var androidCompilationTask = LoadProject($@"{basePath}\{baseName}.netcoremobile.csproj", "net7.0-android"); + var unitTestsCompilationTask = LoadProject($@"{basePath}\{baseName}.Tests.csproj", "net7.0"); + var macCompilationTask = LoadProject($@"{basePath}\{baseName}.netcoremobile.csproj", "net7.0-macos"); - _dependencyPropertySymbol = _referenceCompilation.GetTypeByMetadataName(BaseXamlNamespace + ".DependencyProperty"); + var netstdReferenceCompilationTask = LoadProject($@"{basePath}\{baseName}.Reference.csproj", "net7.0"); + var wasmCompilationTask = LoadProject($@"{basePath}\{baseName}.Wasm.csproj", "net7.0"); + var skiaCompilationTask = LoadProject($@"{basePath}\{baseName}.Skia.csproj", "net7.0"); - _iOSCompilation = LoadProject($@"{basePath}\{baseName}.netcoremobile.csproj", "net7.0-ios"); - _androidCompilation = LoadProject($@"{basePath}\{baseName}.netcoremobile.csproj", "net7.0-android"); - _unitTestsCompilation = LoadProject($@"{basePath}\{baseName}.Tests.csproj", "net7.0"); - _macCompilation = LoadProject($@"{basePath}\{baseName}.netcoremobile.csproj", "net7.0-macos"); + await Task.WhenAll(iOSCompilationTask, androidCompilationTask, unitTestsCompilationTask, macCompilationTask, netstdReferenceCompilationTask, wasmCompilationTask, skiaCompilationTask); - _netstdReferenceCompilation = LoadProject($@"{basePath}\{baseName}.Reference.csproj", "net7.0"); - _wasmCompilation = LoadProject($@"{basePath}\{baseName}.Wasm.csproj", "net7.0"); - _skiaCompilation = LoadProject($@"{basePath}\{baseName}.Skia.csproj", "net7.0"); + _iOSCompilation = await iOSCompilationTask; + _androidCompilation = await androidCompilationTask; + _unitTestsCompilation = await unitTestsCompilationTask; + _macCompilation = await macCompilationTask; + _netstdReferenceCompilation = await netstdReferenceCompilationTask; + _wasmCompilation = await wasmCompilationTask; + _skiaCompilation = await skiaCompilationTask; _iOSBaseSymbol = _iOSCompilation.GetTypeByMetadataName("UIKit.UIView"); _androidBaseSymbol = _androidCompilation.GetTypeByMetadataName("Android.Views.View"); _macOSBaseSymbol = _macCompilation.GetTypeByMetadataName("AppKit.NSView"); - FlagsAttributeSymbol = _referenceCompilation.GetTypeByMetadataName("System.FlagsAttribute"); - UIElementSymbol = _referenceCompilation.GetTypeByMetadataName(BaseXamlNamespace + ".UIElement"); + FlagsAttributeSymbol = s_referenceCompilation.GetTypeByMetadataName("System.FlagsAttribute"); + UIElementSymbol = s_referenceCompilation.GetTypeByMetadataName(BaseXamlNamespace + ".UIElement"); - var origins = from externalRedfs in _referenceCompilation.ExternalReferences + var origins = from externalRedfs in s_referenceCompilation.ExternalReferences let fileNameWithoutExtension = Path.GetFileNameWithoutExtension(externalRedfs.Display) where fileNameWithoutExtension.StartsWith("Windows.Foundation", StringComparison.Ordinal) || fileNameWithoutExtension.StartsWith("Microsoft.WinUI", StringComparison.Ordinal) @@ -178,12 +187,12 @@ where fileNameWithoutExtension.StartsWith("Windows.Foundation", StringComparison || fileNameWithoutExtension.StartsWith("Windows.ApplicationModel.Calls.CallsPhoneContract", StringComparison.Ordinal) || fileNameWithoutExtension.StartsWith("Windows.UI.Xaml.Hosting.HostingContract", StringComparison.Ordinal) || fileNameWithoutExtension.StartsWith("Microsoft.Web.WebView2.Core", StringComparison.Ordinal) - let asm = _referenceCompilation.GetAssemblyOrModuleSymbol(externalRedfs) as IAssemblySymbol + let asm = s_referenceCompilation.GetAssemblyOrModuleSymbol(externalRedfs) as IAssemblySymbol where asm != null select asm; - List excludeNamespaces = new List(); - List includeNamespaces = new List(); + var excludeNamespaces = new List(); + var includeNamespaces = new List(); #if !HAS_UNO_WINUI // For UWP compilation we need to ignore these namespaces when not explicitly generating @@ -1311,7 +1320,7 @@ private bool IsNotUWPMapping(INamedTypeSymbol type, IMethodSymbol method) } else { - var type2 = _referenceCompilation.GetTypeByMetadataName(uwpIface); + var type2 = s_referenceCompilation.GetTypeByMetadataName(uwpIface); INamedTypeSymbol build() { @@ -1348,7 +1357,7 @@ private bool IsNotUWPMapping(INamedTypeSymbol type, IPropertySymbol property) if (uwpIface != null) { - var type2 = _referenceCompilation.GetTypeByMetadataName(uwpIface); + var type2 = s_referenceCompilation.GetTypeByMetadataName(uwpIface); var t3 = type2.Construct(iface.TypeArguments.ToArray()); @@ -1736,7 +1745,7 @@ private IMethodSymbol FindMatchingMethod(ITypeSymbol symbol, IMethodSymbol sourc static Dictionary<(string projectFile, string targetFramework), Compilation> _projects = new(); - private static Compilation LoadProject(string projectFile, string targetFramework = null) + private static async Task LoadProject(string projectFile, string targetFramework = null) { var key = (projectFile, targetFramework); @@ -1746,40 +1755,36 @@ private static Compilation LoadProject(string projectFile, string targetFramewor return compilation; } - return _projects[key] = InnerLoadProject(projectFile, targetFramework); + return _projects[key] = await InnerLoadProject(projectFile, targetFramework); } - private static Compilation LoadUWPReferenceProject(string referencesFile) + private static async Task LoadUWPReferenceProject(string referencesFile) { var ws = new AdhocWorkspace(); var p = ws.AddProject("uwpref", LanguageNames.CSharp); - - foreach (var reference in File.ReadAllLines(referencesFile)) - { - p = p.AddMetadataReference(MetadataReference.CreateFromFile(reference)); - } - - return p.GetCompilationAsync().Result; + p = p.AddMetadataReferences(File.ReadAllLines(referencesFile).Select(reference => MetadataReference.CreateFromFile(reference))); + return await p.GetCompilationAsync(); } - private static Compilation InnerLoadProject(string projectFile, string targetFramework = null) + private static async Task InnerLoadProject(string projectFile, string targetFramework = null) { Console.WriteLine($"Loading for {targetFramework}: {Path.GetFileName(projectFile)}"); var properties = new Dictionary - { - // { "VisualStudioVersion", "15.0" }, - // { "Configuration", "Debug" }, - //{ "BuildingInsideVisualStudio", "true" }, - { "SkipUnoResourceGeneration", "true" }, // Required to avoid loading a non-existent task - { "DocsGeneration", "true" }, // Detect that source generation is running - { "LangVersion", CSharpLangVersion }, - { "NoBuild", "True" }, - //{ "DesignTimeBuild", "true" }, - //{ "UseHostCompilerIfAvailable", "false" }, - //{ "UseSharedCompilation", "false" }, - }; + { + // { "VisualStudioVersion", "15.0" }, + // { "Configuration", "Debug" }, + //{ "BuildingInsideVisualStudio", "true" }, + { "SkipUnoResourceGeneration", "true" }, // Required to avoid loading a non-existent task + { "DocsGeneration", "true" }, // Detect that source generation is running + { "LangVersion", CSharpLangVersion }, + { "NoBuild", "True" }, + { "RunAnalyzers", "false" } + //{ "DesignTimeBuild", "true" }, + //{ "UseHostCompilerIfAvailable", "false" }, + //{ "UseSharedCompilation", "false" }, + }; if (targetFramework != null) { @@ -1793,7 +1798,7 @@ private static Compilation InnerLoadProject(string projectFile, string targetFra ws.WorkspaceFailed += (s, e) => Console.WriteLine(e.Diagnostic.ToString()); - var project = ws.OpenProjectAsync(projectFile).Result; + var project = await ws.OpenProjectAsync(projectFile); var generatedDocs = project.Documents .Where(d => d.FilePath.Contains("\\Generated\\")) @@ -1808,7 +1813,7 @@ private static Compilation InnerLoadProject(string projectFile, string targetFra .Where(p => p.MetadataReferences.None()) .ToArray(); - if (metadataLessProjects.Any()) + if (metadataLessProjects.Length > 0) { // In this case, this may mean that Rolsyn failed to execute some msbuild task that loads the // references in a UWA project (or NuGet 3.0+ with project.json, more specifically). For these @@ -1829,8 +1834,7 @@ private static Compilation InnerLoadProject(string projectFile, string targetFra ); } - return project - .GetCompilationAsync().Result; + return await project.GetCompilationAsync(); } public static IEnumerable GetNamespaceTypes(INamespaceSymbol sym) diff --git a/src/Uno.UWPSyncGenerator/Program.cs b/src/Uno.UWPSyncGenerator/Program.cs index 14151c622d72..956179caf635 100644 --- a/src/Uno.UWPSyncGenerator/Program.cs +++ b/src/Uno.UWPSyncGenerator/Program.cs @@ -1,11 +1,5 @@ using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; using System.Threading.Tasks; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.MSBuild; namespace Uno.UWPSyncGenerator { @@ -15,7 +9,7 @@ class Program const string DocMode = "doc"; const string AllMode = "all"; - static void Main(string[] args) + static async Task Main(string[] args) { if (args.Length == 0) { @@ -27,66 +21,66 @@ static void Main(string[] args) if (mode == SyncMode || mode == AllMode) { - new SyncGenerator().Build(@"..\..\..\Uno.Foundation", "Uno.Foundation", "Windows.Foundation.FoundationContract"); - new SyncGenerator().Build(@"..\..\..\Uno.UWP", "Uno", "Windows.Foundation.UniversalApiContract"); - new SyncGenerator().Build(@"..\..\..\Uno.UWP", "Uno", "Windows.Phone.PhoneContract"); - new SyncGenerator().Build(@"..\..\..\Uno.UWP", "Uno", "Windows.Networking.Connectivity.WwanContract"); - new SyncGenerator().Build(@"..\..\..\Uno.UWP", "Uno", "Windows.ApplicationModel.Calls.CallsPhoneContract"); + await new SyncGenerator().Build(@"..\..\..\Uno.Foundation", "Uno.Foundation", "Windows.Foundation.FoundationContract"); + await new SyncGenerator().Build(@"..\..\..\Uno.UWP", "Uno", "Windows.Foundation.UniversalApiContract"); + await new SyncGenerator().Build(@"..\..\..\Uno.UWP", "Uno", "Windows.Phone.PhoneContract"); + await new SyncGenerator().Build(@"..\..\..\Uno.UWP", "Uno", "Windows.Networking.Connectivity.WwanContract"); + await new SyncGenerator().Build(@"..\..\..\Uno.UWP", "Uno", "Windows.ApplicationModel.Calls.CallsPhoneContract"); // When adding support for a new WinRT contract here, ensure to add it to the list of origins in Generator.cs // and to the list of supported contracts in ApiInformation.shared.cs #if HAS_UNO_WINUI - new SyncGenerator().Build(@"..\..\..\Uno.Foundation", "Uno.Foundation", "Microsoft.Foundation"); + await new SyncGenerator().Build(@"..\..\..\Uno.Foundation", "Uno.Foundation", "Microsoft.Foundation"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Foundation"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Foundation"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Graphics"); - new SyncGenerator().Build(@"..\..\..\Uno.UI.Dispatching", "Uno.UI.Dispatching", "Microsoft.UI.Dispatching"); - new SyncGenerator().Build(@"..\..\..\Uno.UI.Composition", "Uno.UI.Composition", "Microsoft.UI.Composition"); - new SyncGenerator().Build(@"..\..\..\Uno.UI.Dispatching", "Uno.UI.Dispatching", "Microsoft.UI"); - new SyncGenerator().Build(@"..\..\..\Uno.UI.Composition", "Uno.UI.Composition", "Microsoft.UI"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Graphics"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI.Dispatching", "Uno.UI.Dispatching", "Microsoft.UI.Dispatching"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI.Composition", "Uno.UI.Composition", "Microsoft.UI.Composition"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI.Dispatching", "Uno.UI.Dispatching", "Microsoft.UI"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI.Composition", "Uno.UI.Composition", "Microsoft.UI"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Text"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.ApplicationModel.Resources"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Web.WebView2.Core"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Text"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.ApplicationModel.Resources"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Web.WebView2.Core"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Input"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Windowing"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Input"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Windowing"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Xaml"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Xaml"); #else - new SyncGenerator().Build(@"..\..\..\Uno.UI.Composition", "Uno.UI.Composition", "Windows.Foundation.UniversalApiContract"); - new SyncGenerator().Build(@"..\..\..\Uno.UI.Dispatching", "Uno.UI.Dispatching", "Windows.Foundation.UniversalApiContract"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Windows.Foundation.UniversalApiContract"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Windows.UI.Xaml.Hosting.HostingContract"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Xaml"); - new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Web.WebView2.Core"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI.Composition", "Uno.UI.Composition", "Windows.Foundation.UniversalApiContract"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI.Dispatching", "Uno.UI.Dispatching", "Windows.Foundation.UniversalApiContract"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Windows.Foundation.UniversalApiContract"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Windows.UI.Xaml.Hosting.HostingContract"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Xaml"); + await new SyncGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Web.WebView2.Core"); #endif } if (mode == DocMode || mode == AllMode) { #if HAS_UNO_WINUI - new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.ApplicationModel.Resources"); - new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Web.WebView2.Core"); + await new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.ApplicationModel.Resources"); + await new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Web.WebView2.Core"); - new DocGenerator().Build(@"..\..\..\Uno.UI.Dispatching", "Uno.UI.Dispatching", "Microsoft.UI.Dispatching"); - new DocGenerator().Build(@"..\..\..\Uno.UI.Composition", "Uno.UI.Composition", "Microsoft.UI.Composition"); + await new DocGenerator().Build(@"..\..\..\Uno.UI.Dispatching", "Uno.UI.Dispatching", "Microsoft.UI.Dispatching"); + await new DocGenerator().Build(@"..\..\..\Uno.UI.Composition", "Uno.UI.Composition", "Microsoft.UI.Composition"); - new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Foundation"); - new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Composition"); - new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Dispatching"); - new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Input"); - new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Graphics"); - new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Windowing"); - new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI"); + await new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Foundation"); + await new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Composition"); + await new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Dispatching"); + await new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Input"); + await new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.Graphics"); + await new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Windowing"); + await new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI"); - new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Xaml"); + await new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Microsoft.UI.Xaml"); #else - new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Windows.Foundation.UniversalApiContract"); + await new DocGenerator().Build(@"..\..\..\Uno.UI", "Uno.UI", "Windows.Foundation.UniversalApiContract"); #endif } }