如何在Visual Studio中列出给定解决方案中所有项目的所有目标框架?

14

在Visual Studio中打开一个解决方案后,我如何快速检查解决方案中各个项目所针对的目标框架?是否有一种解决方案级别的视图能够显示每个项目所指定的目标框架,或者显示有多少个项目使用了每个框架版本?

我知道可以逐个检查每个项目(通过属性窗口或者csproj文件本身),但是在一个包含100多个项目的解决方案中这样做是不可行的。

此外,我知道可以在根文件夹中csproj文件内进行正则表达式搜索,但我想知道是否有内置于Visual Studio中提供此数据的功能。


你可以发起悬赏以获得答案。 - SidD
2个回答

2
你可以让MSBuild为你打印出来。
在代码的顶层添加一个名为Directory.Build.targets的文件,该文件会打印出TargetFramework值。
对我来说,这个方法很有效。
<Project>

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

</Project>

将其添加到例如MetadataExtractor解决方案中并重新构建会产生以下结果:

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

使用MSBuild来获取这些数据意味着您将会得到正确的结果。解析XML并不能代替实际运行构建项目,因为属性值可以以任意方式被覆盖。

这是一个相当简单的解决方案,所以我非常喜欢它!然而那些发出的消息...它们不会被项目中其他构建消息淹没吗?或者您建议只使用最小的日志进行构建,以便它们像报告一样突出显示? - julealgon
另一个问题:您确定每个项目最终都会执行此目标吗?这取决于执行CoreCompile。无论项目类型如何,这是否总是发生在增量构建中? - julealgon
你需要重新构建才能使其起作用。如果VS认为项目是最新的,它将完全跳过调用MSBuild的步骤。如果重新构建,则不会出现问题。是的,这些消息会与其他消息交织在一起。我假设这只是偶尔想要做的事情,因此您可以在消息前加上某个唯一字符串(例如 ****),以便轻松地筛选它们(手动或使用命令行工具)。 - Drew Noakes
并非所有项目都有<TargetFramework>元素。有些只有<TargetFrameworkVersion>,有些则两者都有。因此我使用了Text="...: $(TargetFramework) | $(TargetFrameworkVersion)"。虽然不是特别优雅,但它完成了工作。 - Rich Armstrong

1

我找不到任何东西,所以决定编写一个脚本:

# 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"

它列出了所有项目、它们的目标框架以及它们是否是 SDK 样式。


虽然这种方法可能有效,但我不建议采用这种方式。相反,如果你真的想使用脚本或应用程序来完成它,我建议使用适当的MSBuild项目读取包,并使用其本机类型解释值。在我看来,这比仅仅试图手动处理数据要更加健壮。话虽如此,这并不能解决我最初提出的要求,所以我不会将其标记为答案。 - julealgon
就像你所说的那样,它是有效的。我明白这可能并没有直接回答你的问题。在我的情况下,我需要一个快速概述一个300个项目解决方案,以帮助迁移到 .net core。这是一个简单的解决方案。 - Brett Postin
1
要明确一点,我并不是在批评你的回答(例如,我没有给它投反对票)。只是我在特别寻找一个更加永久和内置的解决方案。当我说我不建议你在这里采用这种方法时,我的意思是作为长期解决方案:例如,有人使用你的代码创建了一个Visual Studio扩展,或者即使你正在为一个真正的生产系统创建这个长期脚本,该系统将由多个开发人员使用等等。我的观点特别关注解决方案的整体稳健性和未来可靠性。 - julealgon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接