diff --git a/src/Microsoft.DotNet.Tools.Compiler/Program.cs b/src/Microsoft.DotNet.Tools.Compiler/Program.cs index ba81143a2..bef586b3d 100644 --- a/src/Microsoft.DotNet.Tools.Compiler/Program.cs +++ b/src/Microsoft.DotNet.Tools.Compiler/Program.cs @@ -25,6 +25,7 @@ namespace Microsoft.DotNet.Tools.Compiler var output = app.Option("-o|--output ", "Directory in which to place outputs", CommandOptionType.SingleValue); var framework = app.Option("-f|--framework ", "Compile a specific framework", CommandOptionType.MultipleValue); var configuration = app.Option("-c|--configuration ", "Configuration under which to build", CommandOptionType.SingleValue); + var noProjectDependencies = app.Option("--no-project-dependencies", "Skips building project references.", CommandOptionType.NoValue); var project = app.Argument("", "The project to compile, defaults to the current directory. Can be a path to a project.json or a project directory"); app.OnExecute(() => @@ -36,20 +37,22 @@ namespace Microsoft.DotNet.Tools.Compiler path = Directory.GetCurrentDirectory(); } + var skipBuildingProjectReferences = noProjectDependencies.HasValue(); + // Load project contexts for each framework and compile them bool success = true; if (framework.HasValue()) { foreach (var context in framework.Values.Select(f => ProjectContext.Create(path, NuGetFramework.Parse(f)))) { - success &= Compile(context, configuration.Value() ?? Constants.DefaultConfiguration, output.Value()); + success &= Compile(context, configuration.Value() ?? Constants.DefaultConfiguration, output.Value(), skipBuildingProjectReferences); } } else { foreach (var context in ProjectContext.CreateContextForEachFramework(path)) { - success &= Compile(context, configuration.Value() ?? Constants.DefaultConfiguration, output.Value()); + success &= Compile(context, configuration.Value() ?? Constants.DefaultConfiguration, output.Value(), skipBuildingProjectReferences); } } return success ? 0 : 1; @@ -66,7 +69,7 @@ namespace Microsoft.DotNet.Tools.Compiler } } - private static bool Compile(ProjectContext context, string configuration, string outputPath) + private static bool Compile(ProjectContext context, string configuration, string outputPath, bool skipBuildingProjectReferences) { Reporter.Output.WriteLine($"Building {context.RootProject.Identity.Name.Yellow()} for {context.TargetFramework.DotNetFrameworkName.Yellow()}"); @@ -91,17 +94,30 @@ namespace Microsoft.DotNet.Tools.Compiler // Gather exports for the project var dependencies = exporter.GetCompilationDependencies().ToList(); - // Hackily trigger builds of the dependent projects - foreach (var dependency in dependencies.Where(d => d.CompilationAssemblies.Any())) + if (!skipBuildingProjectReferences) { - var projectDependency = dependency.Library as ProjectDescription; - if (projectDependency != null && !string.Equals(projectDependency.Identity.Name, context.RootProject.Identity.Name, StringComparison.Ordinal)) + var projects = new Dictionary(); + + // Build project references + foreach (var dependency in dependencies.Where(d => d.CompilationAssemblies.Any())) { - var compileResult = Command.Create("dotnet-compile", $"--framework {projectDependency.Framework} --configuration {configuration} {projectDependency.Project.ProjectDirectory}") - .ForwardStdOut() - .ForwardStdErr() - .RunAsync() - .Result; + var projectDependency = dependency.Library as ProjectDescription; + + if (projectDependency != null) + { + projects[projectDependency.Identity.Name] = projectDependency; + } + } + + foreach (var projectDependency in Sort(projects)) + { + // Skip compiling project dependencies since we've already figured out the build order + var compileResult = Command.Create("dotnet-compile", $"--framework {projectDependency.Framework} --configuration {configuration} --no-project-dependencies {projectDependency.Project.ProjectDirectory}") + .ForwardStdOut() + .ForwardStdErr() + .RunAsync() + .Result; + if (compileResult.ExitCode != 0) { Console.Error.WriteLine($"Failed to compile dependency: {projectDependency.Identity.Name}"); @@ -124,6 +140,7 @@ namespace Microsoft.DotNet.Tools.Compiler configuration, context.TargetFramework.GetTwoDigitShortFolderName()); } + if (!Directory.Exists(outputPath)) { Directory.CreateDirectory(outputPath); @@ -170,6 +187,33 @@ namespace Microsoft.DotNet.Tools.Compiler return result.ExitCode == 0; } + private static ISet Sort(Dictionary projects) + { + var outputs = new HashSet(); + + foreach (var pair in projects) + { + Sort(pair.Value, projects, outputs); + } + + return outputs; + } + + private static void Sort(ProjectDescription project, Dictionary projects, ISet outputs) + { + // Sorts projects in dependency order so that we only build them once per chain + foreach (var dependency in project.Dependencies) + { + ProjectDescription projectDependency; + if (projects.TryGetValue(dependency.Name, out projectDependency)) + { + Sort(projectDependency, projects, outputs); + } + } + + outputs.Add(project); + } + private static Command RunCsc(string cscArgs) { // Locate CoreRun diff --git a/src/Microsoft.Extensions.ProjectModel/Compilation/LibraryExporter.cs b/src/Microsoft.Extensions.ProjectModel/Compilation/LibraryExporter.cs index b9fa9f492..3195cfd67 100644 --- a/src/Microsoft.Extensions.ProjectModel/Compilation/LibraryExporter.cs +++ b/src/Microsoft.Extensions.ProjectModel/Compilation/LibraryExporter.cs @@ -41,7 +41,7 @@ namespace Microsoft.Extensions.ProjectModel.Compilation public IEnumerable GetCompilationDependencies() { // Export all but the main project - return ExportLibraries(l => !string.Equals(l.Identity.Name, _rootProject.Identity.Name, StringComparison.OrdinalIgnoreCase)); + return ExportLibraries(library => library != _rootProject); } ///