From b4bc9aa8bc7f9b142c764e912ed0860ea7cb82f1 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Thu, 16 Jul 2020 09:38:43 +0200 Subject: [PATCH] [xharness] De-asyncify template expansion. This avoids: * Getting the resource stream for every template expansion * Reading the resource stream * A lot of async code. Instead just read the resource stream once, into a string, and return that. This speeds up the startup by ~10% (from ~0.96s to ~0.83s on my machine). --- .../Managed/InfoPlistGeneratorTests.cs | 14 +- .../Templates/Managed/InfoPlistGenerator.cs | 17 +- .../Managed/RegisterTypeGenerator.cs | 15 +- .../Templates/Managed/XamariniOSTemplate.cs | 168 +++++++++--------- 4 files changed, 99 insertions(+), 115 deletions(-) diff --git a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared.Tests/TestImporter/Templates/Managed/InfoPlistGeneratorTests.cs b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared.Tests/TestImporter/Templates/Managed/InfoPlistGeneratorTests.cs index 677220bf853d..d46487d8b13d 100644 --- a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared.Tests/TestImporter/Templates/Managed/InfoPlistGeneratorTests.cs +++ b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared.Tests/TestImporter/Templates/Managed/InfoPlistGeneratorTests.cs @@ -12,20 +12,14 @@ public class InfoPlistGeneratorTests { [Test] public void GenerateCodeNullTemplateFile () { - Assert.ThrowsAsync (() => - InfoPlistGenerator.GenerateCodeAsync (null, "Project Name")); + Assert.Throws (() => + InfoPlistGenerator.GenerateCode (null, "Project Name")); } [Test] public void GenerateCodeNullProjectName () { - var tmp = Path.GetTempFileName (); - File.WriteAllText (tmp, "Hello"); - using (var stream = new FileStream (tmp, FileMode.Open)) { - Assert.ThrowsAsync (() => InfoPlistGenerator.GenerateCodeAsync (stream, null)); - } - - File.Delete (tmp); + Assert.Throws (() => InfoPlistGenerator.GenerateCode ("Hello", null)); } [Test] @@ -39,7 +33,7 @@ public async Task GenerateCode () await file.WriteAsync (fakeTemplate); } - var result = await InfoPlistGenerator.GenerateCodeAsync (File.OpenRead (templatePath), projectName); + var result = InfoPlistGenerator.GenerateCode (File.ReadAllText (templatePath), projectName); try { StringAssert.DoesNotContain (InfoPlistGenerator.ApplicationNameReplacement, result); StringAssert.DoesNotContain (InfoPlistGenerator.IndentifierReplacement, result); diff --git a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/InfoPlistGenerator.cs b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/InfoPlistGenerator.cs index 204d255c029d..fde11c047caf 100644 --- a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/InfoPlistGenerator.cs +++ b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/InfoPlistGenerator.cs @@ -11,23 +11,18 @@ public class InfoPlistGenerator { public static readonly string IndentifierReplacement = "%BUNDLE INDENTIFIER%"; public static readonly string WatchAppIndentifierReplacement = "%WATCHAPP INDENTIFIER%"; - public static async Task GenerateCodeAsync (Stream template, string projectName) + public static string GenerateCode (string template, string projectName) { if (template == null) throw new ArgumentNullException (nameof (template)); if (projectName == null) throw new ArgumentNullException (nameof (projectName)); // got the lines we want to add, read the template and substitute - using (var reader = new StreamReader (template)) { - var result = await reader.ReadToEndAsync (); - result = result.Replace (ApplicationNameReplacement, projectName); - result = result.Replace (IndentifierReplacement, $"com.xamarin.bcltests.{projectName}"); - result = result.Replace (WatchAppIndentifierReplacement, $"com.xamarin.bcltests.{projectName}.container"); - return result; - } + var result = template; + result = result.Replace (ApplicationNameReplacement, projectName); + result = result.Replace (IndentifierReplacement, $"com.xamarin.bcltests.{projectName}"); + result = result.Replace (WatchAppIndentifierReplacement, $"com.xamarin.bcltests.{projectName}.container"); + return result; } - - // Generates the code for the type registration using the give path to the template to use - public static string GenerateCode (Stream template, string projectName) => GenerateCodeAsync (template, projectName).Result; } } diff --git a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/RegisterTypeGenerator.cs b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/RegisterTypeGenerator.cs index ab315fd5950c..15855488ded6 100644 --- a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/RegisterTypeGenerator.cs +++ b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/RegisterTypeGenerator.cs @@ -13,8 +13,7 @@ public static class RegisterTypeGenerator { static readonly string KeysReplacement = "%KEY VALUES%"; static readonly string IsxUnitReplacement = "%IS XUNIT%"; - public static async Task GenerateCodeAsync ((string FailureMessage, Dictionary Types) typeRegistration, bool isXunit, - Stream template) + public static string GenerateCode ((string FailureMessage, Dictionary Types) typeRegistration, bool isXunit, string template) { var importStringBuilder = new StringBuilder (); var keyValuesStringBuilder = new StringBuilder (); @@ -35,13 +34,11 @@ public static async Task GenerateCodeAsync ((string FailureMessage, Dict } // got the lines we want to add, read the template and substitute - using (var reader = new StreamReader (template)) { - var result = await reader.ReadToEndAsync (); - result = result.Replace (UsingReplacement, importStringBuilder.ToString ()); - result = result.Replace (KeysReplacement, keyValuesStringBuilder.ToString ()); - result = result.Replace (IsxUnitReplacement, isXunit ? "true" : "false"); - return result; - } + var result = template; + result = result.Replace (UsingReplacement, importStringBuilder.ToString ()); + result = result.Replace (KeysReplacement, keyValuesStringBuilder.ToString ()); + result = result.Replace (IsxUnitReplacement, isXunit ? "true" : "false"); + return result; } } } diff --git a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs index bac688757dc6..2e7c05d93c77 100644 --- a/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs +++ b/tests/xharness/Microsoft.DotNet.XHarness.iOS.Shared/TestImporter/Templates/Managed/XamariniOSTemplate.cs @@ -99,22 +99,32 @@ public class XamariniOSTemplate : ITemplatedProject { bool srcGenerated = false; object srcGeneratedLock = new object (); - Stream GetTemplateStream (string templateName) + Dictionary templates = new Dictionary (); + string GetTemplateStream (string templateName) { - var resources = GetType ().Assembly.GetManifestResourceNames (); - var name = GetType ().Assembly.GetManifestResourceNames ().Where (a => a.EndsWith (templateName, StringComparison.Ordinal)).FirstOrDefault (); - return GetType ().Assembly.GetManifestResourceStream (name); + lock (templates) { + if (!templates.TryGetValue (templateName, out var template)) { + var asm = GetType ().Assembly; + var resources = asm.GetManifestResourceNames (); + var name = resources.Where (a => a.EndsWith (templateName, StringComparison.Ordinal)).FirstOrDefault (); + using var stream = asm.GetManifestResourceStream (name); + using var reader = new StreamReader (stream); + template = reader.ReadToEnd (); + templates [templateName] = template; + } + return template; + } } - public Stream GetPlistTemplate (Platform platform) => GetTemplateStream (plistTemplateMatches [platform]); + public string GetPlistTemplate (Platform platform) => GetTemplateStream (plistTemplateMatches [platform]); - public Stream GetPlistTemplate (WatchAppType appType) => GetTemplateStream (watchOSPlistTemplateMatches [appType]); + public string GetPlistTemplate (WatchAppType appType) => GetTemplateStream (watchOSPlistTemplateMatches [appType]); - public Stream GetProjectTemplate (Platform platform) => GetTemplateStream (projectTemplateMatches [platform]); + public string GetProjectTemplate (Platform platform) => GetTemplateStream (projectTemplateMatches [platform]); - public Stream GetProjectTemplate (WatchAppType appType) => GetTemplateStream (watchOSProjectTemplateMatches [appType]); + public string GetProjectTemplate (WatchAppType appType) => GetTemplateStream (watchOSProjectTemplateMatches [appType]); - public Stream GetRegisterTypeTemplate () => GetTemplateStream (registerTemplateResourceName); + public string GetRegisterTypeTemplate () => GetTemplateStream (registerTemplateResourceName); void BuildSrcTree (string srcOuputPath) { @@ -401,19 +411,17 @@ string GenerateWatchProject (string projectName, string template, string infoPli return result; } - async Task GenerateWatchAppAsync (string projectName, Stream template, string infoPlistPath) + string GenerateWatchApp (string projectName, string template, string infoPlistPath) { - using (var reader = new StreamReader (template)) { - var result = await reader.ReadToEndAsync (); - result = result.Replace (NameKey, projectName); - result = result.Replace (WatchOSTemplatePathKey, WatchAppTemplatePath); - result = result.Replace (PlistKey, infoPlistPath); - result = result.Replace (WatchOSCsporjExtensionKey, GetProjectPath (projectName, WatchAppType.Extension).Replace ("/", "\\")); - return result; - } + var result = template; + result = result.Replace (NameKey, projectName); + result = result.Replace (WatchOSTemplatePathKey, WatchAppTemplatePath); + result = result.Replace (PlistKey, infoPlistPath); + result = result.Replace (WatchOSCsporjExtensionKey, GetProjectPath (projectName, WatchAppType.Extension).Replace ("/", "\\")); + return result; } - async Task GenerateWatchExtensionAsync (string projectName, Stream template, string infoPlistPath, string registerPath, (string FailureMessage, List<(string assembly, string hintPath)> Assemblies) info) + string GenerateWatchExtension (string projectName, string template, string infoPlistPath, string registerPath, (string FailureMessage, List<(string assembly, string hintPath)> Assemblies) info) { var rootAssembliesPath = AssemblyLocator.GetAssembliesRootLocation (Platform.WatchOS).Replace ("/", "\\"); var sb = new StringBuilder (); @@ -426,18 +434,16 @@ async Task GenerateWatchExtensionAsync (string projectName, Stream templ } } - using (var reader = new StreamReader (template)) { - var result = await reader.ReadToEndAsync (); - result = result.Replace (DownloadPathKey, rootAssembliesPath); - result = result.Replace (TestingFrameworksKey, GetTestingFrameworksImports (Platform.WatchOS)); - result = result.Replace (NameKey, projectName); - result = result.Replace (WatchOSTemplatePathKey, WatchExtensionTemplatePath); - result = result.Replace (PlistKey, infoPlistPath); - result = result.Replace (RegisterTypeKey, GetRegisterTypeNode (registerPath)); - result = result.Replace (ReferencesKey, sb.ToString ()); - result = result.Replace (ContentKey, GenerateIncludeFilesNode (projectName, info, Platform.WatchOS)); - return result; - } + var result = template; + result = result.Replace (DownloadPathKey, rootAssembliesPath); + result = result.Replace (TestingFrameworksKey, GetTestingFrameworksImports (Platform.WatchOS)); + result = result.Replace (NameKey, projectName); + result = result.Replace (WatchOSTemplatePathKey, WatchExtensionTemplatePath); + result = result.Replace (PlistKey, infoPlistPath); + result = result.Replace (RegisterTypeKey, GetRegisterTypeNode (registerPath)); + result = result.Replace (ReferencesKey, sb.ToString ()); + result = result.Replace (ContentKey, GenerateIncludeFilesNode (projectName, info, Platform.WatchOS)); + return result; } // internal implementations that generate each of the diff projects @@ -468,7 +474,7 @@ async Task GenerateWatchOSTestProjectsAsync (IEnumerable<(str var projectData = new Dictionary (); foreach (var appType in new [] { WatchAppType.Extension, WatchAppType.App }) { (string plist, string project) data; - var plist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (appType), projectDefinition.Name); + var plist = InfoPlistGenerator.GenerateCode (GetPlistTemplate (appType), projectDefinition.Name); data.plist = GetPListPath (generatedCodeDir, appType); using (var file = new StreamWriter (data.plist, false)) { // false is do not append await file.WriteAsync (plist); @@ -477,11 +483,11 @@ async Task GenerateWatchOSTestProjectsAsync (IEnumerable<(str string generatedProject; switch (appType) { case WatchAppType.App: - generatedProject = await GenerateWatchAppAsync (projectDefinition.Name, GetProjectTemplate (appType), data.plist); + generatedProject = GenerateWatchApp (projectDefinition.Name, GetProjectTemplate (appType), data.plist); break; default: var info = projectDefinition.GetAssemblyInclusionInformation (Platform.WatchOS); - generatedProject = await GenerateWatchExtensionAsync (projectDefinition.Name, GetProjectTemplate (appType), data.plist, registerTypePath, info); + generatedProject = GenerateWatchExtension (projectDefinition.Name, GetProjectTemplate (appType), data.plist, registerTypePath, info); failure ??= info.FailureMessage; break; } @@ -493,7 +499,7 @@ async Task GenerateWatchOSTestProjectsAsync (IEnumerable<(str projectData [appType] = data; } // foreach app type - var rootPlist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (Platform.WatchOS), projectDefinition.Name); + var rootPlist = InfoPlistGenerator.GenerateCode (GetPlistTemplate (Platform.WatchOS), projectDefinition.Name); var infoPlistPath = GetPListPath (generatedCodeDir, Platform.WatchOS); using (var file = new StreamWriter (infoPlistPath, false)) { // false is do not append await file.WriteAsync (rootPlist); @@ -506,8 +512,7 @@ async Task GenerateWatchOSTestProjectsAsync (IEnumerable<(str await file.WriteAsync (generatedRootProject); } var typesPerAssembly = projectDefinition.GetTypeForAssemblies (AssemblyLocator.GetAssembliesRootLocation (Platform.iOS), Platform.WatchOS); - var registerCode = await RegisterTypeGenerator.GenerateCodeAsync (typesPerAssembly, - projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); + var registerCode = RegisterTypeGenerator.GenerateCode (typesPerAssembly, projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); using (var file = new StreamWriter (registerTypePath, false)) { // false is do not append await file.WriteAsync (registerCode); } @@ -537,7 +542,7 @@ async Task GenerateWatchOSTestProjectsAsync (IEnumerable<(str /// A path to the template used to generate the path. /// The path to the info plist of the project. /// - async Task GenerateAsync (string projectName, string registerPath, (string FailureMessage, List<(string assembly, string hintPath)> Assemblies) info, Stream template, string infoPlistPath, Platform platform) + string Generate (string projectName, string registerPath, (string FailureMessage, List<(string assembly, string hintPath)> Assemblies) info, string template, string infoPlistPath, Platform platform) { var downloadPath = AssemblyLocator.GetAssembliesRootLocation (platform).Replace ("/", "\\"); // fix possible issues with the paths to be included in the msbuild xml @@ -553,18 +558,16 @@ async Task GenerateAsync (string projectName, string registerPath, (stri } var projectGuid = GuidGenerator?.Invoke (projectName) ?? Guid.NewGuid (); - using (var reader = new StreamReader (template)) { - var result = await reader.ReadToEndAsync (); - result = result.Replace (DownloadPathKey, downloadPath); - result = result.Replace (TestingFrameworksKey, GetTestingFrameworksImports (platform)); - result = result.Replace (ProjectGuidKey, projectGuid.ToString ().ToUpperInvariant ()); - result = result.Replace (NameKey, projectName); - result = result.Replace (ReferencesKey, sb.ToString ()); - result = result.Replace (RegisterTypeKey, GetRegisterTypeNode (registerPath)); - result = result.Replace (PlistKey, infoPlistPath); - result = result.Replace (ContentKey, GenerateIncludeFilesNode (projectName, info, platform)); - return result; - } + var result = template; + result = result.Replace (DownloadPathKey, downloadPath); + result = result.Replace (TestingFrameworksKey, GetTestingFrameworksImports (platform)); + result = result.Replace (ProjectGuidKey, projectGuid.ToString ().ToUpperInvariant ()); + result = result.Replace (NameKey, projectName); + result = result.Replace (ReferencesKey, sb.ToString ()); + result = result.Replace (RegisterTypeKey, GetRegisterTypeNode (registerPath)); + result = result.Replace (PlistKey, infoPlistPath); + result = result.Replace (ContentKey, GenerateIncludeFilesNode (projectName, info, platform)); + return result; } async Task GenerateiOSTestProjectsAsync (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform) @@ -593,20 +596,19 @@ async Task GenerateiOSTestProjectsAsync (IEnumerable<(string string projectPath = GetProjectPath (projectDefinition.Name, platform); string failure = null; try { - var plist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (platform), projectDefinition.Name); + var plist = InfoPlistGenerator.GenerateCode (GetPlistTemplate (platform), projectDefinition.Name); var infoPlistPath = GetPListPath (generatedCodeDir, platform); using (var file = new StreamWriter (infoPlistPath, false)) { // false is do not append await file.WriteAsync (plist); } var info = projectDefinition.GetAssemblyInclusionInformation (platform); - var generatedProject = await GenerateAsync (projectDefinition.Name, registerTypePath, info, GetProjectTemplate (platform), infoPlistPath, platform); + var generatedProject = Generate (projectDefinition.Name, registerTypePath, info, GetProjectTemplate (platform), infoPlistPath, platform); using (var file = new StreamWriter (projectPath, false)) { // false is do not append await file.WriteAsync (generatedProject); } var typesPerAssembly = projectDefinition.GetTypeForAssemblies (AssemblyLocator.GetAssembliesRootLocation (platform), platform); - var registerCode = await RegisterTypeGenerator.GenerateCodeAsync (typesPerAssembly, - projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); + var registerCode = RegisterTypeGenerator.GenerateCode (typesPerAssembly, projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); using (var file = new StreamWriter (registerTypePath, false)) { // false is do not append await file.WriteAsync (registerCode); @@ -626,7 +628,7 @@ async Task GenerateiOSTestProjectsAsync (IEnumerable<(string #region Mac OS project generation - async Task GenerateMacAsync (string projectName, string registerPath, (string FailureMessage, List<(string assembly, string hintPath)> Assemblies) info, Stream template, string infoPlistPath, Platform platform) + string GenerateMac (string projectName, string registerPath, (string FailureMessage, List<(string assembly, string hintPath)> Assemblies) info, string template, string infoPlistPath, Platform platform) { var downloadPath = Path.Combine (AssemblyLocator.GetAssembliesRootLocation (platform), "mac-bcl", platform == Platform.MacOSFull ? "xammac_net_4_5" : "xammac").Replace ("/", "\\"); infoPlistPath = infoPlistPath.Replace ('/', '\\'); @@ -641,32 +643,30 @@ async Task GenerateMacAsync (string projectName, string registerPath, (s } var projectGuid = GuidGenerator?.Invoke (projectName) ?? Guid.NewGuid (); - using (var reader = new StreamReader (template)) { - var result = await reader.ReadToEndAsync (); - result = result.Replace (DownloadPathKey, downloadPath); - result = result.Replace (TestingFrameworksKey, GetTestingFrameworksImports (platform)); - result = result.Replace (ProjectGuidKey, projectGuid.ToString ().ToUpperInvariant ()); - result = result.Replace (NameKey, projectName); - result = result.Replace (ReferencesKey, sb.ToString ()); - result = result.Replace (RegisterTypeKey, GetRegisterTypeNode (registerPath)); - result = result.Replace (PlistKey, infoPlistPath); - result = result.Replace (ContentKey, GenerateIncludeFilesNode (projectName, info, platform)); - switch (platform) { - case Platform.MacOSFull: - result = result.Replace (TargetFrameworkVersionKey, "v4.5.2"); - result = result.Replace (TargetExtraInfoKey, - "true"); - result = result.Replace (DefineConstantsKey, "ADD_BCL_EXCLUSIONS;XAMMAC_4_5"); - break; - case Platform.MacOSModern: - result = result.Replace (TargetFrameworkVersionKey, "v2.0"); - result = result.Replace (TargetExtraInfoKey, - "Xamarin.Mac"); - result = result.Replace (DefineConstantsKey, "ADD_BCL_EXCLUSIONS;MOBILE;XAMMAC"); - break; - } - return result; + var result = template; + result = result.Replace (DownloadPathKey, downloadPath); + result = result.Replace (TestingFrameworksKey, GetTestingFrameworksImports (platform)); + result = result.Replace (ProjectGuidKey, projectGuid.ToString ().ToUpperInvariant ()); + result = result.Replace (NameKey, projectName); + result = result.Replace (ReferencesKey, sb.ToString ()); + result = result.Replace (RegisterTypeKey, GetRegisterTypeNode (registerPath)); + result = result.Replace (PlistKey, infoPlistPath); + result = result.Replace (ContentKey, GenerateIncludeFilesNode (projectName, info, platform)); + switch (platform) { + case Platform.MacOSFull: + result = result.Replace (TargetFrameworkVersionKey, "v4.5.2"); + result = result.Replace (TargetExtraInfoKey, + "true"); + result = result.Replace (DefineConstantsKey, "ADD_BCL_EXCLUSIONS;XAMMAC_4_5"); + break; + case Platform.MacOSModern: + result = result.Replace (TargetFrameworkVersionKey, "v2.0"); + result = result.Replace (TargetExtraInfoKey, + "Xamarin.Mac"); + result = result.Replace (DefineConstantsKey, "ADD_BCL_EXCLUSIONS;MOBILE;XAMMAC"); + break; } + return result; } async Task GenerateMacTestProjectsAsync (IEnumerable<(string Name, string [] Assemblies, string ExtraArgs, double TimeoutMultiplier)> projects, Platform platform) @@ -687,8 +687,7 @@ async Task GenerateMacTestProjectsAsync (IEnumerable<(string var registerTypePath = Path.Combine (generatedCodeDir, "RegisterType-mac.cs"); var typesPerAssembly = projectDefinition.GetTypeForAssemblies (AssemblyLocator.GetAssembliesRootLocation (platform), platform); - var registerCode = await RegisterTypeGenerator.GenerateCodeAsync (typesPerAssembly, - projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); + var registerCode = RegisterTypeGenerator.GenerateCode (typesPerAssembly, projectDefinition.IsXUnit, GetRegisterTypeTemplate ()); using (var file = new StreamWriter (registerTypePath, false)) { // false is do not append await file.WriteAsync (registerCode); @@ -696,15 +695,14 @@ async Task GenerateMacTestProjectsAsync (IEnumerable<(string var projectPath = GetProjectPath (projectDefinition.Name, platform); string failure = null; try { - var plist = await InfoPlistGenerator.GenerateCodeAsync (GetPlistTemplate (platform), projectDefinition.Name); + var plist = InfoPlistGenerator.GenerateCode (GetPlistTemplate (platform), projectDefinition.Name); var infoPlistPath = GetPListPath (generatedCodeDir, platform); using (var file = new StreamWriter (infoPlistPath, false)) { // false is do not append await file.WriteAsync (plist); } var info = projectDefinition.GetAssemblyInclusionInformation (platform); - var generatedProject = await GenerateMacAsync (projectDefinition.Name, registerTypePath, - info, GetProjectTemplate (platform), infoPlistPath, platform); + var generatedProject = GenerateMac (projectDefinition.Name, registerTypePath, info, GetProjectTemplate (platform), infoPlistPath, platform); using (var file = new StreamWriter (projectPath, false)) { // false is do not append await file.WriteAsync (generatedProject); }