2015-09-01 41 views
5

我正在減少大型C#/ ASP.NET解決方案的編譯時間。我們的解決方案使用通常的resx文件方法被翻譯成十幾種外語。這些資源文件的解析和編譯大大減慢了我們的編譯時間,並且每天都受挫。如何通過覆蓋MSBuild目標來防止外語資源的生成?

我知道有可能創建自定義資源提供程序並遠離.resx文件。現在,請假設我們必須堅持使用.resx文件。

通過從我們的.csproj文件中排除除默認語言環境.resx文件以外的所有文件,我可以將編譯時間減半。我們的開發人員在日常開發過程中不需要編譯其他十幾種語言。

我正在尋找方法來防止編譯外語.resx文件。我提出了兩種方法,並且正在尋求一種優越性的建議,還是有其他更好的方法。

兩個我已經想出了:

  • 寫腳本,可以去掉,並在各種.csproj的文件添加回在非缺省的.resx文件。也許我們會將最小化的.csproj文件保存在版本控制中,並且有一個單獨的構建過程用於遞歸重新添加.resx文件,以便重新整合新的翻譯,執行測試並執行我們的部署構建。
  • 想象一種方法來覆蓋執行resx解析和編譯的內置MSBuild目標,除了缺省語言之外,它們都可以有效地禁用它們。開發人員可以使用簡單的編譯標誌或編譯開關啓用/禁用此行爲。我還沒有深入研究微軟提供的.target文件,看看這個解決方案實際上有多合理或可維護。

更新

我寫了下面PowerShell腳本將我的所有外國語言EmbeddedResources和編譯元素融入其中有一個條件屬性的新的ItemGroup。

$root = "C:\Code\solution_root\" 

# Find all csproj files. 
$projects = Get-ChildItem -Path $root -Recurse -ErrorAction SilentlyContinue -Filter *.csproj | Where-Object { $_.Extension -eq '.csproj' } 

# Use a hashtable to gather a unique list of suffixes we moved for sanity checking - make sure we didn't 
# relocate anything we didn't intend to. This is how I caught I was moving javascript files I didn't intend to. 
$suffixes = @{} 

# Find foreign resources ending in .resx and .Designer.cs 
# Use a regex capture to so we can count uniques. 
$pattern = "\.(?<locale>\w{2}(-\w{2,3})?\.(Designer.cs|resx))$" 

foreach ($project in $projects) 
{ 
    "Processing {0}" -f $project.FullName 

    # Load the csproj file as XML 
    $xmlDoc = new-object XML 
    $xmlDoc.Load($project.FullName) 

    # Set namespace for XPath queries 
    $ns = New-Object System.Xml.XmlNamespaceManager($xmlDoc.NameTable) 
    $ns.AddNamespace("ns", $xmlDoc.DocumentElement.NamespaceURI) 

    $count = 0 

    $embeds = $xmlDoc.SelectNodes("//ns:EmbeddedResource",$ns) 
    $compiles = $xmlDoc.SelectNodes("//ns:Compile",$ns) 

    # Create new conditional ItemGroup node if it does not exist. 
    # Side-effect - every csproj will get this new element regardless of whether it 
    # contains foreign resources. That works for us, might not for you. 
    $moveToNode = $xmlDoc.SelectSingleNode("//ns:ItemGroup[@Condition=`" '`$(Configuration)'=='Release' `"]", $ns) 
    if ($moveToNode -eq $null) { 
     # When creating new elements, pass in the NamespaceURI from the parent node. 
     # If we don't do this, elements will get a blank namespace like xmlns="", and this will break compilation. 
     # Hat tip to https://stackoverflow.com/questions/135000/how-to-prevent-blank-xmlns-attributes-in-output-from-nets-xmldocument 

     $conditionAtt = $xmlDoc.CreateAttribute("Condition") 
     $conditionAtt.Value = " '`$(Configuration)'=='Release' " 
     $moveToNode = $xmlDoc.CreateElement("ItemGroup", $xmlDoc.Project.NamespaceURI) 
     $ignore = $moveToNode.Attributes.Append($conditionAtt) 
     $ignore = $xmlDoc.LastChild.AppendChild($moveToNode) 
    } 

    # Loop over the EmbeddedResource and Compile elements. 
    foreach ($resource in ($embeds += $compiles)) { 

     # Exclude javascript files which I found in our Web project. 
     # These look like *.js.resx or *.js.Designer.cs and were getting picked up by my regex. 
     # Yeah, I could make a better regex, but I'd like to see my kids today. 
     if ($resource.Include -notmatch "js\.(Designer.cs|resx)$" -and $resource.Include -match $pattern) { 

      # We have a foreign-language resource. 

      # Track unique suffixes for reporting later. 
      $suffix = $matches['locale'] 
      if (!$suffixes.ContainsKey($suffix)) { 
       $ignore = $suffixes.Add($suffix,"") 
      } 

      $ignore = $moveToNode.InsertBefore($resource, $null) 

      # Count how many we moved per project. 
      $count += 1 
     } 
    } 
    "Moved {0} resources in {1}.`n" -f $count, $project.Name 
    $xmlDoc.Save($project.FullName) 
} 
echo "The following unique suffixes were processed." 
$suffixes.Keys | sort 

回答

2

你可以利用的MSBuild的,讓你來處理這種情況下,像<Choose>元素documentated here並介紹自己的編譯配置選項現有機制。如果您的開發人員僅在日常工作中構建調試配置,則可能甚至不需要您自己的構建配置:您可以設置項目,以便所有外語資源僅包含在發佈構建中。

該項目的文件部分將確保波蘭的資源只建在Release配置(語言代碼很可能是錯誤的,但你的想法):

<Choose> 
    <When Condition=" '$(Configuration)'=='Release' "> 
    <ItemGroup> 
    <EmbeddedResource Include="Properties\Resources.pl.resx"> 
     <Generator>ResXFileCodeGenerator</Generator> 
     <LastGenOutput>Resources.pl.Designer.cs</LastGenOutput> 
     <SubType>Designer</SubType> 
    </EmbeddedResource> 
    <Compile Include="Properties\Resources.pl.Designer.cs"> 
     <DependentUpon>Resources.pl.resx</DependentUpon> 
     <AutoGen>True</AutoGen> 
    </Compile> 

    <!-- TODO: add other language resources as necessary--> 

    </ItemGroup> 
    </When> 
</Choose> 

如果你的開發人員需要建立發佈配置可以創建自己的配置ReleaseResx,並僅在該配置中包含外語.resx文件。

你的建議也可以發揮作用,我只是覺得這種方法更清潔。

+0

ItemGroup上的條件屬性看起來更乾淨。但總的來說,我同意 - 我寧願在msbuild內部進行黑客攻擊,而不是包括 - 通過外部腳本排除文件 –

+0

太棒了!我想知道這種技術的設置和維護。在設置上,我想我需要手動編輯csproj文件並將所有非默認區域設置資源推送到Choose結構中,對嗎?可以很容易地編寫腳本。當新的頁面和控件被添加到解決方案時,Visual Studio將在這個新的Choose結構之外創建其通常的默認語言環境資源,其餘的默認語言環境資源駐留在該結構中。那麼我想這就是我們如何將新翻譯整合回csproj文件的問題。這是有希望的。 –

+0

恐怕,安裝很可能是手動步驟。如果您的翻譯公司使用SDL Passolo或Alchemy Catalyst等工具,獲取新資源並不會造成問題。 – Jenszcz