Skip to content

Commit

Permalink
Add capability to add a framework switch and also make sure to return…
Browse files Browse the repository at this point in the history
… referenced packages only for specified framework
  • Loading branch information
sensslen committed Sep 16, 2024
1 parent 6ac944f commit 24ca2c4
Show file tree
Hide file tree
Showing 18 changed files with 205 additions and 93 deletions.
21 changes: 21 additions & 0 deletions NuGetUtility.sln
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EmptyCppProject", "tests\ta
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VersionRangesProject", "tests\targets\VersionRangesProject\VersionRangesProject.csproj", "{6C96D10E-6CB6-4387-922F-C7C97C037495}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MultiTargetProjectWithDifferentDependencies", "tests\targets\MultiTargetProjectWithDifferentDependencies\MultiTargetProjectWithDifferentDependencies.csproj", "{A19BF89A-8B1F-4612-A923-08F52B53DC84}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -285,6 +287,24 @@ Global
{6C96D10E-6CB6-4387-922F-C7C97C037495}.TestWindows|x64.Build.0 = Debug|Any CPU
{6C96D10E-6CB6-4387-922F-C7C97C037495}.TestWindows|x86.ActiveCfg = Debug|Any CPU
{6C96D10E-6CB6-4387-922F-C7C97C037495}.TestWindows|x86.Build.0 = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Debug|x64.ActiveCfg = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Debug|x64.Build.0 = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Debug|x86.ActiveCfg = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Debug|x86.Build.0 = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Release|Any CPU.Build.0 = Release|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Release|x64.ActiveCfg = Release|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Release|x64.Build.0 = Release|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Release|x86.ActiveCfg = Release|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.Release|x86.Build.0 = Release|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.TestWindows|Any CPU.ActiveCfg = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.TestWindows|Any CPU.Build.0 = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.TestWindows|x64.ActiveCfg = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.TestWindows|x64.Build.0 = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.TestWindows|x86.ActiveCfg = Debug|Any CPU
{A19BF89A-8B1F-4612-A923-08F52B53DC84}.TestWindows|x86.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -304,6 +324,7 @@ Global
{01704839-219A-4CE2-93F4-DB3CB8773DCE} = {FFB2826C-17A3-4C18-8FFE-A34AB51592F7}
{9B107A91-87F9-420C-ADF8-732C77DAFB93} = {FA92392F-D895-4D1E-A5ED-E6DC3C08223E}
{6C96D10E-6CB6-4387-922F-C7C97C037495} = {FA92392F-D895-4D1E-A5ED-E6DC3C08223E}
{A19BF89A-8B1F-4612-A923-08F52B53DC84} = {FA92392F-D895-4D1E-A5ED-E6DC3C08223E}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {70887D40-0182-4C32-BFA1-B5A02E405F11}
Expand Down
5 changes: 5 additions & 0 deletions src/NuGetUtility/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ public class Program
Description = "This option allows to specify project name(s) to exclude from the analysis. This can be useful to exclude test projects from the analysis when supplying a solution file as input. Wildcard characters (*) are supported to specify ranges of ignored projects. The input can either be a file name containing a list of project names in json format or a plain string that is then used as a single entry.")]
public string? ExcludedProjects { get; } = null;

[Option(LongName = "target-framework",
ShortName = "f",
Description = "This option allows to select a Target framework mockier (https://learn.microsoft.com/en-us/dotnet/standard/frameworks) for which to analyze dependencies.")]
public string? TargetFramework { get; } = null;

private static string GetVersion()
=> typeof(Program).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? string.Empty;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ public ReferencedPackageReader(IMsBuildAbstraction msBuild,
_packagesConfigReader = packagesConfigReader;
}

public IEnumerable<PackageIdentity> GetInstalledPackages(string projectPath, bool includeTransitive)
public IEnumerable<PackageIdentity> GetInstalledPackages(string projectPath, bool includeTransitive, string? targetFramework = null)
{
IProject project = _msBuild.GetProject(projectPath);

if (TryGetInstalledPackagesFromAssetsFile(includeTransitive, project, out IEnumerable<PackageIdentity>? dependencies))
if (TryGetInstalledPackagesFromAssetsFile(includeTransitive, project, targetFramework, out IEnumerable<PackageIdentity>? dependencies))
{
return dependencies;
}
Expand All @@ -44,6 +44,7 @@ public IEnumerable<PackageIdentity> GetInstalledPackages(string projectPath, boo

private bool TryGetInstalledPackagesFromAssetsFile(bool includeTransitive,
IProject project,
string? targetFramework,
[NotNullWhen(true)] out IEnumerable<PackageIdentity>? installedPackages)
{
installedPackages = null;
Expand All @@ -54,42 +55,37 @@ private bool TryGetInstalledPackagesFromAssetsFile(bool includeTransitive,

var referencedLibraries = new HashSet<ILockFileLibrary>();

foreach (ILockFileTarget target in assetsFile.Targets!)
if (targetFramework is not null)
{
IEnumerable<ILockFileLibrary> referencedLibrariesForTarget =
GetReferencedLibrariesForTarget(project, includeTransitive, assetsFile, target);
referencedLibraries.AddRange(referencedLibrariesForTarget);
ILockFileTarget target = (assetsFile.Targets?.FirstOrDefault(t => t.TargetFramework.Equals(targetFramework))) ??
throw new ReferencedPackageReaderException($"Target framework {targetFramework} not found.");

referencedLibraries.AddRange(GetReferencedLibrariesForTarget(includeTransitive, assetsFile, target));
}
else
{
foreach (ILockFileTarget target in assetsFile.Targets!)
{
referencedLibraries.AddRange(GetReferencedLibrariesForTarget(includeTransitive, assetsFile, target));
}
}

installedPackages = referencedLibraries.Select(r => new PackageIdentity(r.Name, r.Version));
return true;
}

private IEnumerable<ILockFileLibrary> GetReferencedLibrariesForTarget(IProject project,
bool includeTransitive,
private static IEnumerable<ILockFileLibrary> GetReferencedLibrariesForTarget(bool includeTransitive,
ILockFile assetsFile,
ILockFileTarget target)
{
IEnumerable<ILockFileLibrary> referencedLibrariesForTarget = assetsFile.Libraries.Where(l => l.Type != ProjectReferenceIdentifier);

IEnumerable<ILockFileLibrary> dependencies = target.Libraries.Where(l => l.Type != ProjectReferenceIdentifier);
if (!includeTransitive)
{
ITargetFrameworkInformation targetFrameworkInformation = GetTargetFrameworkInformation(target, assetsFile);
IEnumerable<string> directlyReferencedPackages = _msBuild.GetPackageReferencesFromProjectForFramework(project,
targetFrameworkInformation.FrameworkName.ToString()!);

referencedLibrariesForTarget =
referencedLibrariesForTarget.Where(l => IsDirectlyReferenced(l, directlyReferencedPackages));
IEnumerable<ILibraryDependency> directDependencies = targetFrameworkInformation.Dependencies;
return dependencies.Where(d => directDependencies.Any(direct => direct.Name == d.Name));
}

return referencedLibrariesForTarget;
}

private bool IsDirectlyReferenced(ILockFileLibrary library,
IEnumerable<string> directlyReferencedPackages)
{
return directlyReferencedPackages.Any(p =>
library.Name.Equals(p, StringComparison.OrdinalIgnoreCase));
return dependencies;
}

private static ITargetFrameworkInformation GetTargetFrameworkInformation(ILockFileTarget target,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ namespace NuGetUtility.Wrapper.MsBuildWrapper
{
public interface IMsBuildAbstraction
{
IEnumerable<string> GetPackageReferencesFromProjectForFramework(IProject project, string framework);
IProject GetProject(string projectPath);
IEnumerable<string> GetProjectsFromSolution(string inputPath);
}
Expand Down
30 changes: 0 additions & 30 deletions src/NuGetUtility/Wrapper/MsBuildWrapper/MsBuildAbstraction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,12 @@

using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Exceptions;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Locator;

namespace NuGetUtility.Wrapper.MsBuildWrapper
{
public class MsBuildAbstraction : IMsBuildAbstraction
{
private const string CollectPackageReferences = "CollectPackageReferences";
private ProjectCollection? _projects;

private ProjectCollection Projects => _projects ??= InitializeProjectCollection();
Expand All @@ -22,20 +18,6 @@ public MsBuildAbstraction()
RegisterMsBuildLocatorIfNeeded();
}

public IEnumerable<string> GetPackageReferencesFromProjectForFramework(IProject project,
string framework)
{
var globalProperties = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
{
{ "TargetFramework", framework }
};
var newProject = new ProjectInstance(project.FullPath, globalProperties, null);
newProject.Build(new[] { CollectPackageReferences }, new List<ILogger>(), out IDictionary<string, TargetResult>? targetOutputs);

return targetOutputs.First(e => e.Key.Equals(CollectPackageReferences))
.Value.Items.Select(p => p.ItemSpec);
}

public IProject GetProject(string projectPath)
{
#if !NETFRAMEWORK
Expand Down Expand Up @@ -65,18 +47,6 @@ private static void RegisterMsBuildLocatorIfNeeded()
}
}

private static ProjectRootElement TryGetProjectRootElement(string projectPath)
{
try
{
return ProjectRootElement.Open(projectPath, ProjectCollection.GlobalProjectCollection)!;
}
catch (InvalidProjectFileException e)
{
throw new MsBuildAbstractionException($"Failed to open project: {projectPath}", e);
}
}

private static ProjectCollection InitializeProjectCollection()
{
ProjectCollection collection = ProjectCollection.GlobalProjectCollection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@ namespace NuGetUtility.Wrapper.NuGetWrapper.Frameworks
public interface INuGetFramework
{
string? ToString();
bool Equals(string targetFramework);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ public override bool Equals(object? obj)
return false;
}

public bool Equals(string targetFramework)
{
var other = NuGetFramework.Parse(targetFramework);
return _framework.Equals(other);
}

public override int GetHashCode()
{
return _framework.GetHashCode();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Licensed to the projects contributors.
// The license conditions are provided in the LICENSE file located in the project root

namespace NuGetUtility.Wrapper.NuGetWrapper.ProjectModel
{
public interface ILibraryDependency
{
string Name { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,5 @@ public interface ILockFile
{
IPackageSpec PackageSpec { get; }
IEnumerable<ILockFileTarget>? Targets { get; }

IEnumerable<ILockFileLibrary> Libraries { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ namespace NuGetUtility.Wrapper.NuGetWrapper.ProjectModel
public interface ILockFileTarget
{
INuGetFramework TargetFramework { get; }
IEnumerable<ILockFileLibrary> Libraries { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ namespace NuGetUtility.Wrapper.NuGetWrapper.ProjectModel
public interface ITargetFrameworkInformation
{
INuGetFramework FrameworkName { get; }
IEnumerable<ILibraryDependency> Dependencies { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the projects contributors.
// The license conditions are provided in the LICENSE file located in the project root

using NuGet.LibraryModel;

namespace NuGetUtility.Wrapper.NuGetWrapper.ProjectModel
{
internal class WrappedLibraryDependency : ILibraryDependency
{
private readonly LibraryDependency _dependency;

public WrappedLibraryDependency(LibraryDependency dependency)
{
_dependency = dependency;
}
public string Name => _dependency.Name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,7 @@ public WrappedLockFileTarget(LockFileTarget target)
}

public INuGetFramework TargetFramework => new WrappedNuGetFramework(_target.TargetFramework);

public IEnumerable<ILockFileLibrary> Libraries => _target.Libraries.Select(l => new WrappedLockFileTargetLibrary(l));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the projects contributors.
// The license conditions are provided in the LICENSE file located in the project root

using NuGet.ProjectModel;
using NuGetUtility.Wrapper.NuGetWrapper.Versioning;

namespace NuGetUtility.Wrapper.NuGetWrapper.ProjectModel
{
internal class WrappedLockFileTargetLibrary : ILockFileLibrary
{
public WrappedLockFileTargetLibrary(LockFileTargetLibrary library)
{
Type = library.Type ?? throw new ArgumentNullException(nameof(library), $"The field {nameof(library.Type)} on {nameof(library)} must not be null");
Name = library.Name ?? throw new ArgumentNullException(nameof(library), $"The field {nameof(library.Name)} on {nameof(library)} must not be null");
Version = new WrappedNuGetVersion(library.Version ?? throw new ArgumentNullException(nameof(library), $"The field {nameof(library.Version)} on {nameof(library)} must not be null"));
}

public string Type { get; }

public string Name { get; }

public INuGetVersion Version { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public WrappedTargetFrameworkInformation(TargetFrameworkInformation info)

public INuGetFramework FrameworkName => new WrappedNuGetFramework(_info.FrameworkName);

public IEnumerable<ILibraryDependency> Dependencies => _info.Dependencies.Select(library => new WrappedLibraryDependency(library));

public override string ToString()
{
return _info.ToString();
Expand Down
Loading

0 comments on commit 24ca2c4

Please sign in to comment.