diff --git a/src/NuGetUtility/Extensions/ProjectExtensions.cs b/src/NuGetUtility/Extensions/ProjectExtensions.cs index 5f9c466b..95a650bd 100644 --- a/src/NuGetUtility/Extensions/ProjectExtensions.cs +++ b/src/NuGetUtility/Extensions/ProjectExtensions.cs @@ -19,6 +19,11 @@ public static bool IsPackageReferenceProject(this IProject project) !project.HasPackagesConfigFile(); } + public static string GetPackagesConfigPath(this IProject project) + { + return Path.Join(Path.GetDirectoryName(project.FullPath), PackagesConfigFileName); + } + private static bool HasPackagesConfigFile(this IProject project) { return project.GetEvaluatedIncludes().Any(include => include?.Equals(PackagesConfigFileName) ?? false); diff --git a/src/NuGetUtility/NuGetUtility.csproj b/src/NuGetUtility/NuGetUtility.csproj index f369d1fd..50b20301 100644 --- a/src/NuGetUtility/NuGetUtility.csproj +++ b/src/NuGetUtility/NuGetUtility.csproj @@ -30,6 +30,7 @@ + diff --git a/src/NuGetUtility/ReferencedPackagesReader/ReferencedPackageReader.cs b/src/NuGetUtility/ReferencedPackagesReader/ReferencedPackageReader.cs index b438348e..6d02e7f3 100644 --- a/src/NuGetUtility/ReferencedPackagesReader/ReferencedPackageReader.cs +++ b/src/NuGetUtility/ReferencedPackagesReader/ReferencedPackageReader.cs @@ -1,7 +1,9 @@ +using System.Xml.Linq; using NuGetUtility.Extensions; using NuGetUtility.Wrapper.MsBuildWrapper; using NuGetUtility.Wrapper.NuGetWrapper.Packaging.Core; using NuGetUtility.Wrapper.NuGetWrapper.ProjectModel; +using NuGetUtility.Wrapper.NuGetWrapper.Versioning; namespace NuGetUtility.ReferencedPackagesReader { @@ -27,9 +29,26 @@ public IEnumerable GetInstalledPackages(string projectPath, boo return Enumerable.Empty(); } - return GetInstalledPackagesFromAssetsFile(includeTransitive, project); + if (project.IsPackageReferenceProject()) + return GetInstalledPackagesFromAssetsFile(includeTransitive, project); + + return GetInstalledPackagesFromPackagesConfig(project); + } + + private IEnumerable GetInstalledPackagesFromPackagesConfig(IProject project) + { + var xml = XElement.Load(project.GetPackagesConfigPath()); + + return xml.Descendants("package").Select(p => ToPackageIdentity(p)); } + private PackageIdentity ToPackageIdentity(XElement xlm) + { + var packageid = (string) xlm.Attribute("id"); + var version = (string) xlm.Attribute("version"); + + return new PackageIdentity(packageid, new WrappedNuGetVersion(version)); + } private IEnumerable GetInstalledPackagesFromAssetsFile(bool includeTransitive, IProject project) { diff --git a/src/NuGetUtility/Wrapper/MsBuildWrapper/MsBuildAbstraction.cs b/src/NuGetUtility/Wrapper/MsBuildWrapper/MsBuildAbstraction.cs index 40eeb00c..f5e51139 100644 --- a/src/NuGetUtility/Wrapper/MsBuildWrapper/MsBuildAbstraction.cs +++ b/src/NuGetUtility/Wrapper/MsBuildWrapper/MsBuildAbstraction.cs @@ -1,3 +1,4 @@ +using System.Management; using Microsoft.Build.Construction; using Microsoft.Build.Evaluation; using Microsoft.Build.Exceptions; @@ -12,10 +13,17 @@ namespace NuGetUtility.Wrapper.MsBuildWrapper public class MsBuildAbstraction : IMsBuildAbstraction { private const string CollectPackageReferences = "CollectPackageReferences"; + private readonly Dictionary _project_props = new (); public MsBuildAbstraction() { RegisterMsBuildLocatorIfNeeded(); + + // to support VC-projects we need to workaround : https://github.com/3F/MvsSln/issues/1 + // adding 'VCTargetsPath' to Project::GlobalProperties seem to be enough + + if (GetBestVCTargetsPath() is string path) + _project_props.Add("VCTargetsPath", $"{path}\\"); } public IEnumerable GetPackageReferencesFromProjectForFramework(IProject project, @@ -40,16 +48,9 @@ public IProject GetProject(string projectPath) { ProjectRootElement rootElement = TryGetProjectRootElement(projectPath); - var project = new Project(rootElement); - var projectWrapper = new ProjectWrapper(project); - - if (!projectWrapper.IsPackageReferenceProject()) - { - throw new MsBuildAbstractionException( - $"Invalid project structure detected. Currently only PackageReference projects are supported (Project: {project.FullPath})"); - } + var project = new Project(rootElement, _project_props, null); - return projectWrapper; + return new ProjectWrapper(project); } public IEnumerable GetProjectsFromSolution(string inputPath) @@ -59,6 +60,32 @@ public IEnumerable GetProjectsFromSolution(string inputPath) return sln.ProjectsInOrder.Select(p => p.AbsolutePath); } + private static string? GetBestVCTargetsPath() + { + var cpp_props = new List(); + + foreach (string path in GetVisualStudioInstallPaths()) + cpp_props.AddRange(new DirectoryInfo(path).GetFiles("Microsoft.Cpp.Default.props", SearchOption.AllDirectories)); + + // if multiple, assume most recent 'LastWriteTime' property is 'best' + return cpp_props.OrderBy(f => f.LastWriteTime).LastOrDefault()?.DirectoryName; + } + + private static IEnumerable GetVisualStudioInstallPaths() + { + // https://learn.microsoft.com/en-us/visualstudio/install/tools-for-managing-visual-studio-instances?view=vs-2022#using-windows-management-instrumentation-wmi + + var result = new List(); + + var mmc = new ManagementClass("root/cimv2/vs:MSFT_VSInstance"); + + foreach (var vs_instance in mmc.GetInstances()) + if (vs_instance["InstallLocation"] is string install_path) + result.Add(install_path); + + return result; + } + private static void RegisterMsBuildLocatorIfNeeded() { if (!MSBuildLocator.IsRegistered) diff --git a/tests/NuGetUtility.Test/ReferencedPackagesReader/ReferencedPackagesReaderIntegrationTest.cs b/tests/NuGetUtility.Test/ReferencedPackagesReader/ReferencedPackagesReaderIntegrationTest.cs index b4630c50..920eb0dd 100644 --- a/tests/NuGetUtility.Test/ReferencedPackagesReader/ReferencedPackagesReaderIntegrationTest.cs +++ b/tests/NuGetUtility.Test/ReferencedPackagesReader/ReferencedPackagesReaderIntegrationTest.cs @@ -65,14 +65,13 @@ public void GetInstalledPackagesShould_ReturnEmptyEnumerableForProjectsWithoutPa [Test] [Platform(Include = "Win")] - public void GetInstalledPackagesShould_ThrowMsBuildAbstractionException_If_ProjectUsesPackagesConfig() + public void GetInstalledPackagesShould_ReturnPackagesForPackagesConfigProject() { string path = Path.GetFullPath("../../../../targets/PackagesConfigProject/PackagesConfigProject.csproj"); - MsBuildAbstractionException? exception = Assert.Throws(() => _uut!.GetInstalledPackages(path, false)); - Assert.AreEqual( - $"Invalid project structure detected. Currently only PackageReference projects are supported (Project: {path})", - exception?.Message); + IEnumerable result = _uut!.GetInstalledPackages(path, false); + + Assert.That(result.Count, Is.EqualTo(1)); } } }