From 11ada3f1f715551adbb37b98c4f95bb5398ce3cf Mon Sep 17 00:00:00 2001 From: David Fowler Date: Fri, 24 Apr 2020 09:49:54 -0700 Subject: [PATCH] Support parallel building of projects in tye.yaml (#405) - Generate an msbuild project on the fly to build all projects described in tye.yaml Fixes #389 --- src/Microsoft.Tye.Hosting/ProcessRunner.cs | 90 +++++++++++++++++----- 1 file changed, 69 insertions(+), 21 deletions(-) diff --git a/src/Microsoft.Tye.Hosting/ProcessRunner.cs b/src/Microsoft.Tye.Hosting/ProcessRunner.cs index 776f51da..302fb433 100644 --- a/src/Microsoft.Tye.Hosting/ProcessRunner.cs +++ b/src/Microsoft.Tye.Hosting/ProcessRunner.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; @@ -45,20 +46,23 @@ namespace Microsoft.Tye.Hosting private async Task BuildAndRunProjects(Application application) { + var projectGroups = new Dictionary(); + var groupCount = 0; + foreach (var service in application.Services.Values) { var serviceDescription = service.Description; string path; string args; - var buildArgs = string.Empty; + var buildProperties = string.Empty; string workingDirectory; if (serviceDescription.RunInfo is ProjectRunInfo project) { path = project.RunCommand; workingDirectory = project.ProjectFile.Directory.FullName; args = project.Args == null ? project.RunArguments : project.RunArguments + " " + project.Args; - buildArgs = project.BuildProperties.Aggregate(string.Empty, (current, property) => current + $" /p:{property.Key}={property.Value}").TrimStart(); + buildProperties = project.BuildProperties.Aggregate(string.Empty, (current, property) => current + $";{property.Key}={property.Value}").TrimStart(';'); service.Status.ProjectFilePath = project.ProjectFile.FullName; } @@ -90,34 +94,68 @@ namespace Microsoft.Tye.Hosting project2.Build && _options.BuildProjects) { - // Sometimes building can fail because of file locking (like files being open in VS) - _logger.LogInformation("Building project {ProjectFile}", service.Status.ProjectFilePath); + if (!projectGroups.TryGetValue(buildProperties, out var projectGroup)) + { + projectGroup = new ProjectGroup("ProjectGroup" + groupCount); + projectGroups[buildProperties] = projectGroup; + groupCount++; + } - service.Logs.OnNext($"dotnet build \"{service.Status.ProjectFilePath}\" {buildArgs} /nologo"); + projectGroup.Services.Add(service); + } + } + + if (projectGroups.Count > 0) + { + using var directory = TempDirectory.Create(); - var buildResult = await ProcessUtil.RunAsync("dotnet", $"build \"{service.Status.ProjectFilePath}\" {buildArgs} /nologo", throwOnError: false, workingDirectory: workingDirectory); + var projectPath = Path.Combine(directory.DirectoryPath, Path.GetRandomFileName() + ".proj"); - service.Logs.OnNext(buildResult.StandardOutput); + var sb = new StringBuilder(); + sb.AppendLine(@" + "); - if (buildResult.ExitCode != 0) + foreach (var group in projectGroups) + { + foreach (var p in group.Value.Services) { - _logger.LogInformation("Building {ProjectFile} failed with exit code {ExitCode}: \r\n" + buildResult.StandardOutput, service.Status.ProjectFilePath, buildResult.ExitCode); - return; + sb.AppendLine($" <{group.Value.GroupName} Include=\"{p.Status.ProjectFilePath}\" />"); } + sb.AppendLine(@" "); } - } - foreach (var s in application.Services) - { - switch (s.Value.ServiceType) + sb.AppendLine($@" "); + foreach (var group in projectGroups) { - case ServiceType.Executable: - LaunchService(application, s.Value); - break; - case ServiceType.Project: - LaunchService(application, s.Value); - break; - }; + sb.AppendLine($@" "); + } + + sb.AppendLine(" "); + sb.AppendLine(""); + File.WriteAllText(projectPath, sb.ToString()); + + _logger.LogInformation("Building projects"); + + var buildResult = await ProcessUtil.RunAsync("dotnet", $"build \"{projectPath}\" /nologo", throwOnError: false, workingDirectory: application.ContextDirectory); + + if (buildResult.ExitCode != 0) + { + _logger.LogInformation("Building projects failed with exit code {ExitCode}: \r\n" + buildResult.StandardOutput, buildResult.ExitCode); + return; + } + + foreach (var s in application.Services) + { + switch (s.Value.ServiceType) + { + case ServiceType.Executable: + LaunchService(application, s.Value); + break; + case ServiceType.Project: + LaunchService(application, s.Value); + break; + }; + } } } @@ -398,5 +436,15 @@ namespace Microsoft.Tye.Hosting public CancellationTokenSource StoppedTokenSource { get; } = new CancellationTokenSource(); } + + private class ProjectGroup + { + public ProjectGroup(string groupName) + { + GroupName = groupName; + } + public List Services { get; } = new List(); + public string GroupName { get; } + } } }