Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always log an error when static graph-based restore fails #5085

Merged
merged 2 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -651,10 +651,10 @@ private DependencyGraphSpec GetDependencyGraphSpec(string entryProjectPath, IDic
// Load the projects via MSBuild and create an array of them since Parallel.ForEach is optimized for arrays
var projects = LoadProjects(entryProjects)?.ToArray();

// If no projects were loaded, return null indicating that the projects could not be loaded.
// If no projects were loaded, return an empty DependencyGraphSpec
if (projects == null || projects.Length == 0)
{
return null;
return new DependencyGraphSpec();
jeffkl marked this conversation as resolved.
Show resolved Hide resolved
}

var sw = Stopwatch.StartNew();
Expand Down
108 changes: 62 additions & 46 deletions src/NuGet.Core/NuGet.Build.Tasks.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
Expand Down Expand Up @@ -33,64 +34,79 @@ internal static class Program
/// <returns><code>0</code> if the application ran successfully with no errors, otherwise <code>1</code>.</returns>
public static async Task<int> Main(string[] args)
{
var debug = IsDebug();

if (debug)
try
{
Debugger.Launch();
}
var debug = IsDebug();

NuGet.Common.Migrations.MigrationRunner.Run();
if (debug)
{
Debugger.Launch();
}

// Parse command-line arguments
if (!TryParseArguments(args, out (Dictionary<string, string> Options, FileInfo MSBuildExeFilePath, string EntryProjectFilePath, Dictionary<string, string> MSBuildGlobalProperties) arguments))
{
return 1;
}
NuGet.Common.Migrations.MigrationRunner.Run();

// Parse command-line arguments
if (!TryParseArguments(args, out (Dictionary<string, string> Options, FileInfo MSBuildExeFilePath, string EntryProjectFilePath, Dictionary<string, string> MSBuildGlobalProperties) arguments))
{
return 1;
}

// Enable MSBuild feature flags
MSBuildFeatureFlags.MSBuildExeFilePath = arguments.MSBuildExeFilePath.FullName;
MSBuildFeatureFlags.EnableCacheFileEnumerations = true;
MSBuildFeatureFlags.LoadAllFilesAsReadonly = true;
MSBuildFeatureFlags.SkipEagerWildcardEvaluations = true;
// Enable MSBuild feature flags
MSBuildFeatureFlags.MSBuildExeFilePath = arguments.MSBuildExeFilePath.FullName;
MSBuildFeatureFlags.EnableCacheFileEnumerations = true;
MSBuildFeatureFlags.LoadAllFilesAsReadonly = true;
MSBuildFeatureFlags.SkipEagerWildcardEvaluations = true;
#if NETFRAMEWORK
if (AppDomain.CurrentDomain.IsDefaultAppDomain())
{
// MSBuild.exe.config has binding redirects that change from time to time and its very hard to make sure that NuGet.Build.Tasks.Console.exe.config is correct.
// It also can be different per instance of Visual Studio so when running unit tests it always needs to match that instance of MSBuild
// The code below runs this EXE in an AppDomain as if its MSBuild.exe so the assembly search location is next to MSBuild.exe and all binding redirects are used
// allowing this process to evaluate MSBuild projects as if it is MSBuild.exe
var thisAssembly = Assembly.GetExecutingAssembly();

AppDomain appDomain = AppDomain.CreateDomain(
thisAssembly.FullName,
securityInfo: null,
info: new AppDomainSetup
{
ApplicationBase = arguments.MSBuildExeFilePath.DirectoryName,
ConfigurationFile = Path.Combine(arguments.MSBuildExeFilePath.DirectoryName, "MSBuild.exe.config")
});

return appDomain
.ExecuteAssembly(
thisAssembly.Location,
args);
}
if (AppDomain.CurrentDomain.IsDefaultAppDomain())
{
// MSBuild.exe.config has binding redirects that change from time to time and its very hard to make sure that NuGet.Build.Tasks.Console.exe.config is correct.
// It also can be different per instance of Visual Studio so when running unit tests it always needs to match that instance of MSBuild
// The code below runs this EXE in an AppDomain as if its MSBuild.exe so the assembly search location is next to MSBuild.exe and all binding redirects are used
// allowing this process to evaluate MSBuild projects as if it is MSBuild.exe
var thisAssembly = Assembly.GetExecutingAssembly();

AppDomain appDomain = AppDomain.CreateDomain(
thisAssembly.FullName,
securityInfo: null,
info: new AppDomainSetup
{
ApplicationBase = arguments.MSBuildExeFilePath.DirectoryName,
ConfigurationFile = Path.Combine(arguments.MSBuildExeFilePath.DirectoryName, "MSBuild.exe.config")
});

return appDomain
.ExecuteAssembly(
thisAssembly.Location,
args);
}
#endif

// Check whether the ask is to generate the restore graph file.
if (MSBuildStaticGraphRestore.IsOptionTrue("GenerateRestoreGraphFile", arguments.Options))
{
// Check whether the ask is to generate the restore graph file.
if (MSBuildStaticGraphRestore.IsOptionTrue("GenerateRestoreGraphFile", arguments.Options))
{
using (var dependencyGraphSpecGenerator = new MSBuildStaticGraphRestore(debug: debug))
{
return dependencyGraphSpecGenerator.WriteDependencyGraphSpec(arguments.EntryProjectFilePath, arguments.MSBuildGlobalProperties, arguments.Options) ? 0 : 1;
}
}

// Otherwise run restore!
using (var dependencyGraphSpecGenerator = new MSBuildStaticGraphRestore(debug: debug))
{
return dependencyGraphSpecGenerator.WriteDependencyGraphSpec(arguments.EntryProjectFilePath, arguments.MSBuildGlobalProperties, arguments.Options) ? 0 : 1;
return await dependencyGraphSpecGenerator.RestoreAsync(arguments.EntryProjectFilePath, arguments.MSBuildGlobalProperties, arguments.Options) ? 0 : 1;
}
}

// Otherwise run restore!
using (var dependencyGraphSpecGenerator = new MSBuildStaticGraphRestore(debug: debug))
catch (Exception e)
{
return await dependencyGraphSpecGenerator.RestoreAsync(arguments.EntryProjectFilePath, arguments.MSBuildGlobalProperties, arguments.Options) ? 0 : 1;
var consoleOutLogMessage = new ConsoleOutLogMessage
{
Message = string.Format(CultureInfo.CurrentCulture, Strings.Error_StaticGraphUnhandledException, e.ToString()),
MessageType = ConsoleOutLogMessageType.Error,
};

System.Console.Out.WriteLine(consoleOutLogMessage.ToJson());

return -1;
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/NuGet.Core/NuGet.Build.Tasks/ConsoleOutLogItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static implicit operator ConsoleOutLogItem(BuildMessageEventArgs buildMes
{
return new ConsoleOutLogMessage
{
Importance = buildMessageEventArgs.Importance,
Importance = (ConsoleOutLogMessage.MessageImportance)buildMessageEventArgs.Importance,
Message = buildMessageEventArgs.Message,
MessageType = ConsoleOutLogMessageType.Message,
};
Expand Down
11 changes: 10 additions & 1 deletion src/NuGet.Core/NuGet.Build.Tasks/ConsoleOutLogMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using Microsoft.Build.Framework;
using Newtonsoft.Json;

namespace NuGet.Build.Tasks
Expand Down Expand Up @@ -49,5 +48,15 @@ public sealed class ConsoleOutLogMessage : ConsoleOutLogItem
public string Subcategory { get; set; }

public override string ToJson() => JsonConvert.SerializeObject(this, SerializerSettings);

/// <summary>
/// Represents the message importance. This is a copy of <see cref="MessageImportance" /> because in some code paths we need to log a message before MSBuild assemblies have been loaded.
/// </summary>
public enum MessageImportance
{
High,
Normal,
Low
}
}
}
Loading