13

您有一个大型 Visual Studio 解决方案,其中包含数十个项目文件。您将如何验证所有项目在其属性设置中是否遵循某些规则,并在添加新项目时强制执行这些规则。例如检查所有项目是否有:

TargetFrameworkVersion = "v4.5"
Platform = "AnyCPU"
WarningLevel = 4
TreatWarningsAsErrors = true
OutputPath = $(SolutionDir)bin
SignAssembly = true
AssemblyName = $(ProjectFolderName)

我自己知道两种方法,我将在下面的答案中添加,但我想知道人们如何进行这种类型的项目测试。我特别有兴趣了解可用的解决方案,例如库或为此构建任务,而不必发明新东西或从头开始编写。

4

10 回答 10

9

*.sln 文件是纯文本且易于解析,而 *.*proj 文件是 xml。

您可以添加一个具有预构建步骤的虚拟项目,该步骤解析 sln 以检索所有项目文件、验证其设置、打印报告并在必要时使构建失败。

此外,您应该检查这篇文章以确保始终执行预构建步骤。本质上,您在自定义构建步骤中指定一个空白输出以强制重新构建。

于 2015-10-14T08:39:28.150 回答
6

以下列表标识了在使用 Visual Studio .NET 集成开发环境 (IDE) 将解决方案添加到源代码管理时自动添加到 VSS 的关键文件类型:

解决方案文件 ( .sln)。这些文件中维护的关键项目包括组成项目列表、依赖关系信息、构建配置详细信息和源代码控制提供程序详细信息。项目文件( .csproj 或 *.vbproj)。这些文件中维护的关键项目包括程序集构建设置、引用的程序集(按名称和路径)和文件清单。应用程序配置文件。这些是基于可扩展标记语言 (XML) 的配置文件,用于控制项目运行时行为的各个方面。

尽可能使用单一解决方案模型

另请参阅:https ://msdn.microsoft.com/en-us/library/ee817677.aspx、https : //msdn.microsoft.com/en-us/library/ee817675.aspx

并且对于持续集成:有许多可用的工具,例如 MSBuild、Jenkins、Apache 的 Continuum、Cruise Control (CC) 和 Hudson(插件可以扩展为 c#)

于 2015-10-15T05:52:27.567 回答
4

这就是我自己的:

一种方法是创建一个带有错误条件的 MSBuild 目标:

<Error Condition="'$(TreatWarningsAsErrors)'!='true'" Text="Invalid project setting" />

我喜欢这种方法,因为它与 MSBuild 集成并为您提供早期错误,但是,您必须修改每个项目以将其导入其中,或者让您的所有团队成员使用带有环境变量的特殊命令提示符,该环境变量将注入自定义 pre-在构建过程中将步骤构建到您的项目中,这很痛苦。

我知道的第二种方法是使用像VSUnitTest这样的库,它为您可以测试的项目属性提供API 。VSUnitTest 目前不是开源的,并且未从 NuGet 服务中列出。

于 2015-10-03T23:53:05.343 回答
4

您可以编写一些代码以将解决方案作为文本文件打开,以识别所有引用的 csproj 文件,然后将每个文件作为 xml 文件打开,然后编写单元测试以确保项目的特定节点符合您的预期。

这是一个快速而肮脏的解决方案,但适用于 CI,让您可以灵活地忽略您不关心的节点。它实际上听起来有点用。我有一个包含 35 个项目的解决方案,我也想扫描。

于 2015-10-12T19:02:35.963 回答
4

让我们尝试一些完全不同的东西:您可以通过从模板生成它们或使用诸如CMake之类的构建生成工具来确保它们是一致。这可能比事后试图使它们保持一致更简单。

于 2015-10-16T15:22:12.143 回答
4

在我们的工作中,我们使用了一个 powershell 脚本来检查项目设置并在它们不正确时对其进行修改。例如,我们以这种方式删除 Debug 配置,禁用 C++ 优化和 SSE2 支持。我们手动运行它,但绝对可以自动运行它,例如作为 pre\post build 步骤。

下面的例子:

`function Prepare-Solution {  
param (  
    [string]$SolutionFolder
)  
$files = gci -Recurse -Path $SolutionFolder -file *.vcxproj | select -    ExpandProperty fullname  
$files | %{  
    $file = $_  
    [xml]$xml = get-content $file  

    #Deleting Debug configurations...
    $xml.Project.ItemGroup.ProjectConfiguration | ?{$_.Configuration -eq "Debug"} | %{$_.ParentNode.RemoveChild($_)} | Out-Null
    $xml.SelectNodes("//*[contains(@Condition,'Debug')]") |%{$_.ParentNode.RemoveChild($_)} | Out-Null

    if($xml.Project.ItemDefinitionGroup.ClCompile) {  
        $xml.Project.ItemDefinitionGroup.ClCompile | %{  
            #Disable SSE2
            if (-not($_.EnableEnhancedInstructionSet)){
                $_.AppendChild($xml.CreateElement("EnableEnhancedInstructionSet", $xml.DocumentElement.NamespaceURI)) | Out-Null  
            }   

            if($_.ParentNode.Condition.Contains("Win32")){  
                $_.EnableEnhancedInstructionSet = "StreamingSIMDExtensions"
            }
            elseif($_.ParentNode.Condition.Contains("x64")) {
                $_.EnableEnhancedInstructionSet = "NotSet"
            } else {
                Write-Host "Neither x86 nor x64 config. Very strange!!"
            }

            #Disable Optimization
            if (-not($_.Optimization)){  
                $_.AppendChild($xml.CreateElement("Optimization", $xml.DocumentElement.NamespaceURI)) | Out-Null  
            }   
            $_.Optimization = "Disabled" 
        } 
    } 
    $xml.Save($file);  
} }`
于 2015-10-16T23:44:09.187 回答
3

一个文件是一个程序集当且仅当它是被管理的并且在其元数据中包含一个程序集条目。有关程序集和元数据的详细信息,请参阅主题程序集清单。

如何手动确定文件是否为程序集

  1. 启动Ildasm.exe(IL 反汇编程序)。
  2. 加载您要测试的文件。
  3. 如果 ILDASM 报告该文件不是可移植可执行 (PE) 文件,则它不是程序集。
    有关详细信息,请参阅主题如何:查看装配内容。

如何以编程方式确定文件是否为程序集

  1. 调用该GetAssemblyName方法,传递您正在测试的文件的完整文件路径和名称。
  2. 如果BadImageFormatException抛出异常,则该文件不是程序集。

此示例测试 DLL 以查看它是否为程序集。

class TestAssembly
{
static void Main()
   {

    try
    {
        System.Reflection.AssemblyName testAssembly = System.Reflection.AssemblyName.GetAssemblyName(@"C:\Windows\Microsoft.NET\Framework\v3.5\System.Net.dll");

        System.Console.WriteLine("Yes, the file is an assembly.");
    }

    catch (System.IO.FileNotFoundException)
    {
        System.Console.WriteLine("The file cannot be found.");
    }

    catch (System.BadImageFormatException)
    {
        System.Console.WriteLine("The file is not an assembly.");
    }

    catch (System.IO.FileLoadException)
    {
        System.Console.WriteLine("The assembly has already been loaded.");
    }
   }
}
  // Output (with .NET Framework 3.5 installed):
 // Yes, the file is an assembly.

框架是安装的最高版本,SP 是该版本的服务包。

  RegistryKey installed_versions =   Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP");
  string[] version_names = installed_versions.GetSubKeyNames();
  //version names start with 'v', eg, 'v3.5' which needs to be trimmed off    before conversion
  double Framework = Convert.ToDouble(version_names[version_names.Length - 1].Remove(0, 1), CultureInfo.InvariantCulture);
  int SP =  Convert.ToInt32(installed_versions.OpenSubKey(version_names[version_names.Length     - 1]).GetValue("SP", 0));

 For .Net 4.5


 using System;
 using Microsoft.Win32;


 ...


 private static void Get45or451FromRegistry()
{
using (RegistryKey ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine,    RegistryView.Registry32).OpenSubKey("SOFTWARE\\Microsoft\\NET Framework  Setup\\NDP\\v4\\Full\\")) {
    int releaseKey = Convert.ToInt32(ndpKey.GetValue("Release"));
    if (true) {
        Console.WriteLine("Version: " + CheckFor45DotVersion(releaseKey));
     }
   }
 }


 ...


// Checking the version using >= will enable forward compatibility,  
// however you should always compile your code on newer versions of 
// the framework to ensure your app works the same. 
private static string CheckFor45DotVersion(int releaseKey)
{
if (releaseKey >= 393273) {
   return "4.6 RC or later";
}
if ((releaseKey >= 379893)) {
    return "4.5.2 or later";
}
if ((releaseKey >= 378675)) {
    return "4.5.1 or later";
}
if ((releaseKey >= 378389)) {
    return "4.5 or later";
}
// This line should never execute. A non-null release key should mean 
// that 4.5 or later is installed. 
return "No 4.5 or later version detected";
}
于 2015-10-13T11:37:13.793 回答
1

出于类似目的,我们使用具有我们希望在项目之间共享的公共属性的自定义 MSBuild 片段,如下所示(build.common.props文件):

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>
    <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
    <PlatformToolset>v90</PlatformToolset>
    <OutputPath>$(SolutionDir)..\bin\$(PlatformPath)\$(Configuration)\</OutputPath>

   <!-- whatever you need here -->
  </PropertyGroup>

</Project>

然后我们只需将此片段包含到我们想要将这些属性应用于的真实 VS 项目中:

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <CommonProps>$(SolutionDir)..\Build\build.common.props</CommonProps>
  </PropertyGroup>

  <Import Project="$(CommonProps)" />

  <!-- the rest of the project -->

</Project>

我们使用这种方法处理了很多事情:

  • 常见的属性,正如你提到的
  • 静态分析(FxCop、StyleCop)
  • 组件的数字标志
  • 等等

唯一的缺点是您需要将这些 MSBuild 片段包含到每个项目文件中,但是一旦这样做,您将拥有易于管理和更新的模块化构建系统的所有好处。

于 2015-10-17T14:48:10.033 回答
1

您可以使用手写的 C#、脚本、powershell 或类似工具来搜索和替换正则表达式。但它存在以下问题:

  • 难以阅读(在三个月或更长时间内阅读你漂亮的正则表达式)
  • 难以增强(新的搜索/替换/检查功能的新正则表达式)
  • 容易破解(ms build 项目的新版本/格式或非预测标签可能不起作用)
  • 更难测试(您必须检查没有发生意外匹配)
  • 难以维护(由于上述原因)

以及以下优点:

  • 不做任何额外的验证(可能)让它适用于任何类型的项目(单声道或视觉)。
  • 不在乎 \r :)

最好的办法是使用Microsoft.Build.Evaluation 并构建一个 C# 工具来执行所有测试/检查/修复等。

我已经完成了一个命令行工具,它使用源文件列表(由 Mono 使用)并更新 csproj 的源以及另一个在控制台上转储 csproj 内容的源。这很容易做到,非常简单且易于测试。

但是,它可能会在由“非” Ms 工具(如 Mono Studio)修改的项目上或由于缺少\r ...而失败(正如我所经历的那样)。无论如何,您始终可以使用异常捕获和好消息。

这是使用 Microsoft.Build.dll 的示例(不要使用 Microsof.Build.Engine,因为它已过时):

using System;
using Microsoft.Build.Evaluation;

internal class Program
{
    private static void Main(string[] args)
    {
        var project = new Project("PathToYourProject.csproj");
        Console.WriteLine(project.GetProperty("TargetFrameworkVersion", true, string.Empty));
        Console.WriteLine(project.GetProperty("Platform", true, string.Empty));
        Console.WriteLine(project.GetProperty("WarningLevel", true, string.Empty));
        Console.WriteLine(project.GetProperty("TreatWarningsAsErrors", true, "false"));
        Console.WriteLine(project.GetProperty("OutputPath", false, string.Empty));
        Console.WriteLine(project.GetProperty("SignAssembly", true, "false"));
        Console.WriteLine(project.GetProperty("AssemblyName", false, string.Empty));
        Console.ReadLine();
    }
}

public static class ProjectExtensions
{
    public static string GetProperty(this Project project, string propertyName, bool afterEvaluation, string defaultValue)
    {
        var property = project.GetProperty(propertyName);
        if (property != null)
        {
            if (afterEvaluation)
                return property.EvaluatedValue;
            return property.UnevaluatedValue;
        }
        return defaultValue;
    }
}
于 2015-10-17T14:32:19.247 回答
0

我也遇到了这个问题,并创建了一个小型解决方案,该解决方案创建了一个包含详细信息的 csv 文件以识别不一致之处。您可以在此网址中查看

https://github.com/gdlmanuv/VSProjectConsistencyChecker

于 2022-02-18T17:45:09.863 回答