julealgon
julealgon

Reputation: 8182

How to list all target frameworks of all projects for a given solution inside Visual Studio?

Given an opened solution in Visual Studio, how do I quickly check which target frameworks the various projects in the solution have? Is there a solution-wide view somewhere that shows which target framework each project targets, or an aggregate view of how many projects target each framework version?

I'm aware I can check each project individually (either on properties window or on the csproj file itself), however in a solution with 100+ projects this is not feasible.

Additionally, I know I could probably do some sort of regex search inside csproj files in the root folder, but I was wondering if there was something built-in in Visual Studio to provide this data.

Upvotes: 15

Views: 2029

Answers (4)

Daniel Powell
Daniel Powell

Reputation: 8293

You can use the dotnet list package command to output this data.

By default it will output something like

Project 'MyProject' has the following package references
   [net462]:
   Top-level Package                                    Requested   Resolved
   > Microsoft.NETFramework.ReferenceAssemblies   (A)   [1.0.3, )   1.0.3
   > Newtonsoft.Json                                    12.0.3      12.0.3
   > Polly  

and you can also output and parse json as required with dotnet list package --format json

Which outputs

{
  "version": 1,
  "parameters": "",
  "problems": [
    {
      "level": "warning",
      "text": "(A) : Auto-referenced package."
    }
  ],
  "projects": [
    {
      "path": "C:/MyProject.csproj",
      "frameworks": [
        {
          "framework": "net462",
          "topLevelPackages": [
            {
              "id": "Microsoft.NETFramework.ReferenceAssemblies",
              "requestedVersion": "[1.0.3, )",
              "resolvedVersion": "1.0.3",
              "autoReferenced": "true"
            },
            {
              "id": "Newtonsoft.Json",
              "requestedVersion": "12.0.3",
              "resolvedVersion": "12.0.3"
            },
            {
              "id": "Polly",
              "requestedVersion": "7.2.2",
              "resolvedVersion": "7.2.2"
            }
          ]
        }
      ]
    }
  ]
}

Upvotes: 2

bretddog
bretddog

Reputation: 5519

Not built-in, and not sure if robust. But it works for me and is available at a right-click. Suggest improvements if you have.

Save this powershell script in a file like 'Get_TargetFrameworks.ps1':

param (
    [string]$solutionFolder
)
$indentColumn = 30  

Get-ChildItem $solutionFolder -Include *.csproj -Recurse -Force | ForEach-Object {
    [xml]$projectXml = (Get-Content ($_))
    $namespace=New-Object System.Xml.XmlNamespaceManager($projectXml.NameTable)
    $namespace.AddNamespace("nsp", $projectXml.DocumentElement.NamespaceURI)

    $dotnetVersionNode = $projectXml.SelectSingleNode("//nsp:TargetFrameworkVersion", $namespace)
    if ($dotnetVersionNode -eq $null) {
        $dotnetVersionNode = $projectXml.SelectSingleNode("//nsp:TargetFramework", $namespace)
    }

    $indent = $_.Name.Replace(".csproj", "") + " "
    $spacesNeeded = [Math]::Max(0, $indentColumn - $indent.Length)
    $spaces = ' ' * $spacesNeeded
    $output = "{0}{1}{2}" -f $indent, $spaces, $dotnetVersionNode.InnerXml
    Write-Host $output
}
 
Pause

Add to Explorer right-click menu:

Swap <File path> with actual, save as 'Get_TargetFrameworks.reg' file, and run to install in registry:

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\Directory\Background\shell\GetTargetFrameworks]
@="Get Target Frameworks"

[HKEY_CLASSES_ROOT\Directory\Background\shell\GetTargetFrameworks\command]
@="PowerShell.exe -ExecutionPolicy Bypass -File \"<File path>""

Upvotes: 0

Drew Noakes
Drew Noakes

Reputation: 310997

You could get MSBuild to print this out for you.

Add a Directory.Build.targets file at the top level of your code that prints out the TargetFramework value.

This does the trick for me:

<Project>

  <Target Name="LogTargetFramework" AfterTargets="CoreCompile">
    <Message
      Importance="high"
      Text="Project $(MSBuildProjectName): $(TargetFramework)"/>
  </Target>

</Project>

Adding that to, for example, the MetadataExtractor solution and rebuilding it produces:

1>Project MetadataExtractor: net35
1>Project MetadataExtractor: net45
1>Project MetadataExtractor: netstandard2.0
1>Project MetadataExtractor: netstandard1.3
3>Project MetadataExtractor.PowerShell: net40
2>Project MetadataExtractor.Samples: net48
5>Project MetadataExtractor.Tools.JpegSegmentExtractor: net6.0
4>Project MetadataExtractor.Benchmarks: net461
7>Project MetadataExtractor.Tests: net472
6>Project MetadataExtractor.Tools.FileProcessor: net6.0
7>Project MetadataExtractor.Tests: net6.0

Using MSBuild to get this data means you'll get correct results. Parsing XML is no substitute for actually running the build, as property values can be overridden in any number of ways.

Upvotes: 4

Brett Postin
Brett Postin

Reputation: 11375

I couldn't find anything so decided to write a script instead:

# Set root folder to current script location
$rootFolder = $PSScriptRoot
$solutionName = '[YOUR_SOLUTION_PATH]'

# Define the path to the solution file
$solutionFile = Join-Path $rootFolder $solutionName

# Read the contents of the solution file
$solutionText = Get-Content $solutionFile

# Use a regular expression to extract the names of the project files
$projectFiles = [regex]::Matches($solutionText, 'Project\("{([A-Za-z0-9-]+)}"\) = "([^"]+)", "([^"]+.csproj)"') | ForEach-Object { $_.Groups[3].Value } | Sort-Object

# Define project collection
$projects = @()

# Iterate over each project file
foreach ($projectFile in $projectFiles) {

    # Read the contents of the project file
    $projectText = Get-Content (Join-Path $rootFolder $projectFile)

    # Determine whether it is a SDK style project
    $isSdkProject = [regex]::IsMatch($projectText, '<Project Sdk="Microsoft.NET.Sdk">')

    # Use a regular expression to extract the target framework
    $targetFramework = [regex]::Match($projectText, '<TargetFramework>(.+)</TargetFramework>')
    
    # Get the target framework
    $foundFramework = if ($targetFramework.Success) { $($targetFramework.Groups[1].Value) } else { 'None' }

    # Add to projects collection
    $projects += [pscustomobject]@{ Project=$projectFile; SdkFormat=$isSdkProject; TargetFramework=$foundFramework; }
}

# Output projects as table
$projects | Format-Table

# Display summary
Write-Host $projects.Count "projects found"

It lists all projects, their target framework, and whether they are SDK style.

Upvotes: 2

Related Questions