Skip to content

Commit

Permalink
Allow passing assets file directly to dotnet nuget why (#5922)
Browse files Browse the repository at this point in the history
  • Loading branch information
zivkan committed Jul 17, 2024
1 parent a713935 commit 12a39e1
Show file tree
Hide file tree
Showing 6 changed files with 238 additions and 100 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ public static int ExecuteCommand(WhyCommandArgs whyCommandArgs)
}

string targetPackage = whyCommandArgs.Package;
IEnumerable<string> projectPaths;
IEnumerable<(string assetsFilePath, string? projectPath)> assetsFiles;
try
{
projectPaths = MSBuildAPIUtility.GetListOfProjectsFromPathArgument(whyCommandArgs.Path);
assetsFiles = FindAssetsFiles(whyCommandArgs.Path, whyCommandArgs.Logger);
}
catch (ArgumentException ex)
{
Expand All @@ -46,57 +46,103 @@ public static int ExecuteCommand(WhyCommandArgs whyCommandArgs)
return ExitCodes.InvalidArguments;
}

foreach (var projectPath in projectPaths)
bool anyErrors = false;
foreach ((string assetsFilePath, string? projectPath) in assetsFiles)
{
Project project = MSBuildAPIUtility.GetProject(projectPath);

string usingNetSdk = project.GetPropertyValue("UsingMicrosoftNETSdk");
LockFile? assetsFile = GetProjectAssetsFile(assetsFilePath, projectPath, whyCommandArgs.Logger);

if (!string.IsNullOrEmpty(usingNetSdk))
if (assetsFile != null)
{
LockFile? assetsFile = GetProjectAssetsFile(project, whyCommandArgs.Logger);
ValidateFrameworksOptionsExistInAssetsFile(assetsFile, whyCommandArgs.Frameworks, whyCommandArgs.Logger);

Dictionary<string, List<DependencyNode>?>? dependencyGraphPerFramework = DependencyGraphFinder.GetAllDependencyGraphsForTarget(
assetsFile,
whyCommandArgs.Package,
whyCommandArgs.Frameworks);

if (assetsFile != null)
if (dependencyGraphPerFramework != null)
{
ValidateFrameworksOptionsExistInAssetsFile(assetsFile, whyCommandArgs.Frameworks, whyCommandArgs.Logger);
whyCommandArgs.Logger.LogMinimal(
string.Format(
Strings.WhyCommand_Message_DependencyGraphsFoundInProject,
assetsFile.PackageSpec.Name,
targetPackage));

Dictionary<string, List<DependencyNode>?>? dependencyGraphPerFramework = DependencyGraphFinder.GetAllDependencyGraphsForTarget(
assetsFile,
whyCommandArgs.Package,
whyCommandArgs.Frameworks);
DependencyGraphPrinter.PrintAllDependencyGraphs(dependencyGraphPerFramework, targetPackage, whyCommandArgs.Logger);
}
else
{
whyCommandArgs.Logger.LogMinimal(
string.Format(
Strings.WhyCommand_Message_NoDependencyGraphsFoundInProject,
assetsFile.PackageSpec.Name,
targetPackage));
}
}
else
{
anyErrors = true;
}
}

if (dependencyGraphPerFramework != null)
return anyErrors ? ExitCodes.Error : ExitCodes.Success;
}

private static IEnumerable<(string assetsFilePath, string? projectPath)> FindAssetsFiles(string path, ILoggerWithColor logger)
{
if (XPlatUtility.IsJsonFile(path))
{
yield return (path, null);
yield break;
}

var projectPaths = MSBuildAPIUtility.GetListOfProjectsFromPathArgument(path);
foreach (string projectPath in projectPaths.NoAllocEnumerate())
{
Project project = MSBuildAPIUtility.GetProject(projectPath);
try
{
string usingNetSdk = project.GetPropertyValue("UsingMicrosoftNETSdk");
if (bool.TryParse(usingNetSdk, out bool b) && b)
{
if (!MSBuildAPIUtility.IsPackageReferenceProject(project))
{
whyCommandArgs.Logger.LogMinimal(
logger.LogError(
string.Format(
Strings.WhyCommand_Message_DependencyGraphsFoundInProject,
assetsFile.PackageSpec.Name,
targetPackage));
CultureInfo.CurrentCulture,
Strings.Error_NotPRProject,
project.FullPath));

DependencyGraphPrinter.PrintAllDependencyGraphs(dependencyGraphPerFramework, targetPackage, whyCommandArgs.Logger);
continue;
}
else

string? assetsFilePath = project.GetPropertyValue(ProjectAssetsFile);
if (string.IsNullOrEmpty(assetsFilePath) || !File.Exists(assetsFilePath))
{
whyCommandArgs.Logger.LogMinimal(
logger.LogError(
string.Format(
Strings.WhyCommand_Message_NoDependencyGraphsFoundInProject,
assetsFile.PackageSpec.Name,
targetPackage));
CultureInfo.CurrentCulture,
Strings.Error_AssetsFileNotFound,
project.FullPath));
continue;
}

yield return (assetsFilePath, projectPath);
}
else
{
logger.LogMinimal(
string.Format(
CultureInfo.CurrentCulture,
Strings.WhyCommand_Message_NonSDKStyleProjectsAreNotSupported,
project.GetPropertyValue("MSBuildProjectName")));
}
}
else
finally
{
whyCommandArgs.Logger.LogMinimal(
string.Format(
Strings.WhyCommand_Message_NonSDKStyleProjectsAreNotSupported,
project.GetPropertyValue("MSBuildProjectName")));
ProjectCollection.GlobalProjectCollection.UnloadProject(project);
}

ProjectCollection.GlobalProjectCollection.UnloadProject(project);
}

return ExitCodes.Success;
}

/// <summary>
Expand Down Expand Up @@ -133,7 +179,7 @@ private static bool ValidatePathArgument(string path, ILoggerWithColor logger)
// Check that the path is a directory, solution file or project file
if (Directory.Exists(fullPath)
|| (File.Exists(fullPath)
&& (XPlatUtility.IsSolutionFile(fullPath) || XPlatUtility.IsProjectFile(fullPath))))
&& (XPlatUtility.IsSolutionFile(fullPath) || XPlatUtility.IsProjectFile(fullPath) || XPlatUtility.IsJsonFile(fullPath))))
{
return true;
}
Expand Down Expand Up @@ -184,51 +230,60 @@ private static void ValidateFrameworksOptionsExistInAssetsFile(LockFile assetsFi
}

/// <summary>
/// Validates and returns the assets file for the given project.
/// Returns the path to an assets file for the given project.
/// </summary>
/// <param name="project">Evaluated MSBuild project</param>
/// <param name="logger">Logger for the 'why' command</param>
/// <returns>Assets file for the given project. Returns null if there was any issue finding or parsing the assets file.</returns>
private static LockFile? GetProjectAssetsFile(Project project, ILoggerWithColor logger)
private static LockFile? GetProjectAssetsFile(string assetsFilePath, string? projectPath, ILoggerWithColor logger)
{
if (!MSBuildAPIUtility.IsPackageReferenceProject(project))
if (!File.Exists(assetsFilePath))
{
logger.LogError(
string.Format(
CultureInfo.CurrentCulture,
Strings.Error_NotPRProject,
project.FullPath));

return null;
}

string assetsPath = project.GetPropertyValue(ProjectAssetsFile);

if (!File.Exists(assetsPath))
{
logger.LogError(
string.Format(
CultureInfo.CurrentCulture,
Strings.Error_AssetsFileNotFound,
project.FullPath));
if (!string.IsNullOrEmpty(projectPath))
{
logger.LogError(
string.Format(
CultureInfo.CurrentCulture,
Strings.Error_AssetsFileNotFound,
projectPath));
}
else
{
logger.LogError(
string.Format(
CultureInfo.CurrentCulture,
Strings.Error_PathIsMissingOrInvalid,
assetsFilePath));
}

return null;
}

var lockFileFormat = new LockFileFormat();
LockFile assetsFile = lockFileFormat.Read(assetsPath);
LockFile assetsFile = lockFileFormat.Read(assetsFilePath);

// assets file validation
if (assetsFile.PackageSpec == null
|| assetsFile.Targets == null
|| assetsFile.Targets.Count == 0)
{
logger.LogError(
string.Format(
CultureInfo.CurrentCulture,
Strings.WhyCommand_Error_InvalidAssetsFile,
assetsFile.Path,
project.FullPath));
if (string.IsNullOrEmpty(projectPath))
{
logger.LogError(
string.Format(
CultureInfo.CurrentCulture,
Strings.WhyCommand_Error_InvalidAssetsFile_WithoutProject,
assetsFilePath));
}
else
{
logger.LogError(
string.Format(
CultureInfo.CurrentCulture,
Strings.WhyCommand_Error_InvalidAssetsFile_WithProject,
assetsFilePath,
projectPath));
}

return null;
}
Expand Down
26 changes: 22 additions & 4 deletions src/NuGet.Core/NuGet.CommandLine.XPlat/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 12a39e1

Please sign in to comment.