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

Embed NuGet files in binlog after restore #5042

Merged
merged 3 commits into from
Feb 23, 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 @@ -23,7 +23,7 @@ namespace NuGet.Build.Tasks.Console
/// This class implements <see cref="IBuildEngine" /> so that an instance of TaskLoggingHelper can be created.
///
/// This class implements <see cref="ILogger" /> so that it can be passed to MSBuild APIs that require an ILogger to log messages.</remarks>
internal class ConsoleLoggingQueue : LoggingQueue<ConsoleOutLogMessage>, IBuildEngine, ILogger
internal class ConsoleLoggingQueue : LoggingQueue<ConsoleOutLogItem>, IBuildEngine, ILogger
{
private readonly Lazy<TaskLoggingHelper> _taskLoggingHelperLazy;

Expand Down Expand Up @@ -144,7 +144,7 @@ void ILogger.Shutdown()
/// Processes a logging message by serializing it as JSON and writing it to <see cref="System.Console.Out" />.
/// </summary>
/// <param name="message">The <see cref="ConsoleOutLogMessage" /> to log.</param>
protected override void Process(ConsoleOutLogMessage message)
protected override void Process(ConsoleOutLogItem message)
{
System.Console.Out.WriteLine(message.ToJson());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using Microsoft.Build.Framework;
using Microsoft.Build.Graph;
using Microsoft.Build.Logging;
using Microsoft.Build.Utilities;
using NuGet.Commands;
using NuGet.Common;
using NuGet.Configuration;
Expand Down Expand Up @@ -114,7 +115,7 @@ public async Task<bool> RestoreAsync(string entryProjectFilePath, IDictionary<st

try
{
return await BuildTasksUtility.RestoreAsync(
bool result = await BuildTasksUtility.RestoreAsync(
dependencyGraphSpec: dependencyGraphSpec,
interactive: IsOptionTrue(nameof(RestoreTaskEx.Interactive), options),
recursive: IsOptionTrue(nameof(RestoreTaskEx.Recursive), options),
Expand All @@ -127,7 +128,11 @@ public async Task<bool> RestoreAsync(string entryProjectFilePath, IDictionary<st
restorePC: IsOptionTrue(nameof(RestoreTaskEx.RestorePackagesConfig), options),
cleanupAssetsForUnsupportedProjects: IsOptionTrue(nameof(RestoreTaskEx.CleanupAssetsForUnsupportedProjects), options),
log: MSBuildLogger,
cancellationToken: CancellationToken.None);
cancellationToken: CancellationToken.None);

LogFilesToEmbedInBinlog(dependencyGraphSpec);

return result;
}
catch (Exception e)
{
Expand Down Expand Up @@ -1065,5 +1070,40 @@ private void LogErrorFromException(Exception exception)
break;
}
}

/// <summary>
/// Logs the list of files to embed in the MSBuild binary log.
/// </summary>
/// <param name="dependencyGraphSpec"></param>
private void LogFilesToEmbedInBinlog(DependencyGraphSpec dependencyGraphSpec)
{
// If the MSBuildBinaryLoggerEnabled environment variable is not set, don't log the paths to the files.
if (!string.Equals(Environment.GetEnvironmentVariable("MSBUILDBINARYLOGGERENABLED"), bool.TrueString, StringComparison.OrdinalIgnoreCase))
{
return;
}

IReadOnlyList<PackageSpec> projects = dependencyGraphSpec.Projects;

foreach (PackageSpec project in projects)
{
if (project.RestoreMetadata.ProjectStyle == ProjectStyle.PackageReference)
{
LoggingQueue.Enqueue(new ConsoleOutLogEmbedInBinlog(Path.Combine(project.RestoreMetadata.OutputPath, LockFileFormat.AssetsFileName)));
LoggingQueue.Enqueue(new ConsoleOutLogEmbedInBinlog(Path.Combine(project.RestoreMetadata.OutputPath, DependencyGraphSpec.GetDGSpecFileName(Path.GetFileName(project.RestoreMetadata.ProjectPath)))));
LoggingQueue.Enqueue(new ConsoleOutLogEmbedInBinlog(BuildAssetsUtils.GetMSBuildFilePathForPackageReferenceStyleProject(project, BuildAssetsUtils.PropsExtension)));
LoggingQueue.Enqueue(new ConsoleOutLogEmbedInBinlog(BuildAssetsUtils.GetMSBuildFilePathForPackageReferenceStyleProject(project, BuildAssetsUtils.TargetsExtension)));
}
else if (project.RestoreMetadata.ProjectStyle == ProjectStyle.PackagesConfig)
{
string packagesConfigPath = BuildTasksUtility.GetPackagesConfigFilePath(project.RestoreMetadata.ProjectPath);

if (packagesConfigPath != null)
{
LoggingQueue.Enqueue(new ConsoleOutLogEmbedInBinlog(packagesConfigPath));
}
}
}
}
}
}
74 changes: 56 additions & 18 deletions src/NuGet.Core/NuGet.Build.Tasks/BuildTasksUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ public static async Task<bool> RestoreAsync(
var message = string.Format(
CultureInfo.CurrentCulture,
Strings.InstallCommandNothingToInstall,
"packages.config"
NuGetConstants.PackageReferenceFile
);

log.LogMinimal(message);
Expand Down Expand Up @@ -425,23 +425,9 @@ private static bool ProjectHasPackagesConfigFile(string projectDirectory, string
throw new ArgumentException(Strings.Argument_Cannot_Be_Null_Or_Empty, nameof(projectName));
}

packagesConfigPath = Path.Combine(projectDirectory, NuGetConstants.PackageReferenceFile);
packagesConfigPath = GetPackagesConfigFilePath(projectDirectory, projectName);

if (File.Exists(packagesConfigPath))
{
return true;
}

packagesConfigPath = Path.Combine(projectDirectory, $"packages.{projectName}.config");

if (File.Exists(packagesConfigPath))
{
return true;
}

packagesConfigPath = null;

return false;
return packagesConfigPath != null;
}

#if IS_DESKTOP
Expand Down Expand Up @@ -478,7 +464,7 @@ private static async Task<RestoreSummary> PerformNuGetV2RestoreAsync(Common.ILog

settings = settings ?? Settings.LoadSettingsGivenConfigPaths(pcRestoreMetadata.ConfigFilePaths);

var packagesConfigPath = Path.Combine(Path.GetDirectoryName(pcRestoreMetadata.ProjectPath), NuGetConstants.PackageReferenceFile);
string packagesConfigPath = GetPackagesConfigFilePath(pcRestoreMetadata.ProjectPath);

firstPackagesConfigPath = firstPackagesConfigPath ?? packagesConfigPath;

Expand Down Expand Up @@ -734,5 +720,57 @@ private static string[] AppendItems(string projectDirectory, string[] current, I

return current.Concat(additionalAbsolute).ToArray();
}

/// <summary>
/// Gets the path to a packages.config for the specified project if one exists.
/// </summary>
/// <param name="projectFullPath">The full path to the project.</param>
/// <returns>The path to the packages.config file if one exists, otherwise <c>null</c>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="projectFullPath" /> is <c>null</c>.</exception>
public static string GetPackagesConfigFilePath(string projectFullPath)
{
if (string.IsNullOrWhiteSpace(projectFullPath))
{
throw new ArgumentException(Strings.Argument_Cannot_Be_Null_Or_Empty, nameof(projectFullPath));
}

return GetPackagesConfigFilePath(Path.GetDirectoryName(projectFullPath), Path.GetFileNameWithoutExtension(projectFullPath));
}

/// <summary>
/// Gets the path to a packages.config for the specified project if one exists.
/// </summary>
/// <param name="projectFullPath">The full path to the project directory.</param>
/// <param name="projectName">The name of the project file.</param>
/// <returns>The path to the packages.config file if one exists, otherwise <c>null</c>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="projectDirectory" /> -or- <paramref name="projectName" /> is <c>null</c>.</exception>
public static string GetPackagesConfigFilePath(string projectDirectory, string projectName)
{
if (string.IsNullOrWhiteSpace(projectDirectory))
{
throw new ArgumentException(Strings.Argument_Cannot_Be_Null_Or_Empty, nameof(projectDirectory));
}

if (string.IsNullOrWhiteSpace(projectName))
{
throw new ArgumentException(Strings.Argument_Cannot_Be_Null_Or_Empty, nameof(projectName));
}

string packagesConfigPath = Path.Combine(projectDirectory, NuGetConstants.PackageReferenceFile);

if (File.Exists(packagesConfigPath))
{
return packagesConfigPath;
}

packagesConfigPath = Path.Combine(projectDirectory, "packages." + projectName + ".config");

if (File.Exists(packagesConfigPath))
{
return packagesConfigPath;
}

return null;
}
}
}
39 changes: 39 additions & 0 deletions src/NuGet.Core/NuGet.Build.Tasks/ConsoleOutLogEmbedInBinlog.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Newtonsoft.Json;

namespace NuGet.Build.Tasks
{
/// <summary>
/// Represents a log message that contains a file path to embed in an MSBuild binary log.
/// </summary>
public sealed class ConsoleOutLogEmbedInBinlog
: ConsoleOutLogItem
{
/// <summary>
/// Initializes a new instance of the <see cref="ConsoleOutLogEmbedInBinlog" /> class.
/// </summary>
public ConsoleOutLogEmbedInBinlog()
{
MessageType = ConsoleOutLogMessageType.EmbedInBinlog;
}

/// <summary>
/// Initializes a new instance of the <see cref="ConsoleOutLogEmbedInBinlog" /> class with the specified full path to the file to embed in the MSBuild binary log.
/// </summary>
/// <param name="path">The full path to the file to embed in the MSBuild binary log.</param>
public ConsoleOutLogEmbedInBinlog(string path)
: this()
{
Path = path;
}

/// <summary>
/// Gets or sets the full path to the file to embed in the MSBuild binary log.
/// </summary>
public string Path { get; set; }

public override string ToJson() => JsonConvert.SerializeObject(this, SerializerSettings);
}
}
96 changes: 96 additions & 0 deletions src/NuGet.Core/NuGet.Build.Tasks/ConsoleOutLogItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// Copyright (c) .NET Foundation. All rights reserved.
// 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
{
/// <summary>
/// Represents a base class for defining log items sent over <see cref="Console.Out" />.
/// </summary>
public abstract class ConsoleOutLogItem
{
/// <summary>
/// Serialization settings for messages. These should be set to be as fast as possible and not for friendly
/// display since no user will ever see them.
/// </summary>
protected static readonly JsonSerializerSettings SerializerSettings = new JsonSerializerSettings
{
DefaultValueHandling = DefaultValueHandling.Ignore,
NullValueHandling = NullValueHandling.Ignore,
};

/// <summary>
/// Gets or sets the <see cref="ConsoleOutLogMessageType" /> of the message.
/// </summary>
public ConsoleOutLogMessageType MessageType { get; set; }

/// <summary>
/// Serializes the current object to JSON.
/// </summary>
/// <returns>The current object as a JSON string.</returns>
public abstract string ToJson();

/// <summary>
/// Implicitly converts a <see cref="BuildMessageEventArgs" /> object to a <see cref="ConsoleOutLogItem" /> object.
/// </summary>
/// <param name="buildMessageEventArgs">The <see cref="BuildMessageEventArgs" /> object to convert.</param>
public static implicit operator ConsoleOutLogItem(BuildMessageEventArgs buildMessageEventArgs)
{
return new ConsoleOutLogMessage
{
Importance = buildMessageEventArgs.Importance,
Message = buildMessageEventArgs.Message,
MessageType = ConsoleOutLogMessageType.Message,
};
}

/// <summary>
/// Implicitly converts a <see cref="BuildWarningEventArgs" /> object to a <see cref="ConsoleOutLogItem" /> object.
/// </summary>
/// <param name="buildWarningEventArgs">The <see cref="BuildWarningEventArgs" /> object to convert.</param>
public static implicit operator ConsoleOutLogItem(BuildWarningEventArgs buildWarningEventArgs)
{
return new ConsoleOutLogMessage
{
Code = buildWarningEventArgs.Code,
ColumnNumber = buildWarningEventArgs.ColumnNumber,
EndColumnNumber = buildWarningEventArgs.EndColumnNumber,
EndLineNumber = buildWarningEventArgs.EndLineNumber,
File = buildWarningEventArgs.File,
HelpKeyword = buildWarningEventArgs.HelpKeyword,
LineNumber = buildWarningEventArgs.LineNumber,
Message = buildWarningEventArgs.Message,
MessageType = ConsoleOutLogMessageType.Warning,
ProjectFile = buildWarningEventArgs.ProjectFile,
SenderName = buildWarningEventArgs.SenderName,
Subcategory = buildWarningEventArgs.Subcategory
};
}

/// <summary>
/// Implicitly converts a <see cref="BuildErrorEventArgs" /> object to a <see cref="ConsoleOutLogItem" /> object.
/// </summary>
/// <param name="buildErrorEventArgs">The <see cref="BuildErrorEventArgs" /> object to convert.</param>
public static implicit operator ConsoleOutLogItem(BuildErrorEventArgs buildErrorEventArgs)
{
return new ConsoleOutLogMessage
{
Code = buildErrorEventArgs.Code,
ColumnNumber = buildErrorEventArgs.ColumnNumber,
EndColumnNumber = buildErrorEventArgs.EndColumnNumber,
EndLineNumber = buildErrorEventArgs.EndLineNumber,
File = buildErrorEventArgs.File,
HelpKeyword = buildErrorEventArgs.HelpKeyword,
LineNumber = buildErrorEventArgs.LineNumber,
Message = buildErrorEventArgs.Message,
MessageType = ConsoleOutLogMessageType.Error,
ProjectFile = buildErrorEventArgs.ProjectFile,
SenderName = buildErrorEventArgs.SenderName,
Subcategory = buildErrorEventArgs.Subcategory,
};
}
}
}
Loading