簡體   English   中英

檢查Visual Studio項目的一致性

[英]Checking Visual Studio projects for consistency

您有一個很大的Visual Studio解決方案,其中包含許多項目文件。 您將如何驗證所有項目在其屬性設置中均遵循某些規則,並在添加新項目時執行這些規則。 例如,檢查所有項目是否具有:

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

我自己知道兩種方法,可以在下面的答案中添加,但我想知道人們如何進行這種類型的項目測試。 我特別想了解可用的解決方案(例如庫)或為此構建任務,而不必非要發明新的東西或從頭開始編寫它。

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

您可以使用預構建步驟添加一個虛擬項目,該步驟可以解析sln以檢索所有項目文件,驗證其設置,打印報告並在必要時使構建失敗。

另外,您應該檢查此帖子以確保始終執行預構建步驟。 本質上,您可以在定制構建步驟中指定空白輸出以強制重建。

下表列出了使用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#)

這就是我自己:

一種方法是創建具有錯誤條件的MSBuild目標:

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

我喜歡這種方法,因為它與MSBuild集成在一起,並且會給您帶來早期錯誤,但是,您必須修改每個項目以將其導入其中,或者讓所有團隊成員使用帶有特殊變量的命令提示符以及環境變量來注入自定義的在構建過程中將步驟構建到項目中,這很痛苦。

我知道的第二種方法是使用諸如VSUnitTest之類的 ,該庫提供了一個API,用於可以測試的項目屬性。 VSUnitTest當前不是開源的,並且未從NuGet服務中列出。

您可以編寫一些代碼以文本文件的形式打開解決方案,以標識所有引用的csproj文件,然后依次將每個文件作為xml文件打開,然后編寫單元測試以確保項目的特定節點符合您的期望。

這是一種快速而骯臟的解決方案,但適用於CI,並為您提供了忽略不需要的節點的靈活性。 實際上聽起來有點有用。 我也有一個我想掃描的35個項目的解決方案。

讓我們嘗試完全不同的東西:通過從模板生成它們或使用諸如CMake之類的生成工具,可以通過構造確保它們的一致性。 這可能比事實之后嘗試使它們保持一致要簡單。

在我們的工作中,我們使用一個Powershell腳本來檢查項目設置,並在不正確的情況下對其進行修改。 例如,我們以這種方式刪除調試配置,禁用C ++優化和SSE2支持。 我們手動運行它,但是絕對可以自動運行它,例如作為pre \\ post構建步驟。

在示例下面:

`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);  
} }`

當且僅當文件被管理時,它才是程序集,並且在其元數據中包含程序集條目。 有關程序集和元數據的更多信息,請參見主題程序集清單。

如何手動確定文件是否為程序集

  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.

Framework是安裝的最高版本,SP是該版本的Service Pack。

  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";
}

您可以搜索並用手寫C#,腳本,powershell或類似內容替換正則表達式。 但是它存在以下問題:

  • 難以閱讀 (三個或三個月以上即可閱讀漂亮的正則表達式)
  • 難以增強 (用於新的搜索/替換/檢查功能的新正則表達式)
  • 容易破解 (ms生成項目的新發行版/格式或未預測的標記可能不起作用)
  • 難以測試 (您必須檢查沒有意外的匹配發生)
  • 難以維護 (由於上述原因)

以及以下優點:

  • 不做任何額外的驗證 (可能會使它在任何類型的項目(單聲道或視覺項目)上均可使用)。
  • 不在乎\\ 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;
    }
}

出於類似的目的,我們使用具有想要在項目之間共享的公共屬性的自定義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片段包含到每個項目文件中的唯一缺點是,但是一旦這樣做,您將擁有易於管理和更新的模塊化構建系統的所有優點。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM