2015-09-01 32 views
5

Pracuję nad skróceniem czasu kompilacji dla dużego rozwiązania C#/ASP.NET. Nasze rozwiązanie jest tłumaczone na kilkanaście języków obcych przy użyciu zwykłej metody plików resx. Parsowanie i kompilacja tych plików zasobów znacznie spowalnia nasz czas kompilacji i jest codzienną frustracją.Jak zapobiec generowaniu zasobów w języku obcym przez przesłonięcie celów MSBuild?

Mam świadomość, że można tworzyć niestandardowych dostawców zasobów i uciec od plików .resx. Na razie załóżmy, że musimy trzymać się plików .resx.

Wykluczając wszystkie pliki domyślne locale .resx poza plikami .csproj, jestem w stanie skrócić nasz czas kompilacji o połowę. Nasi programiści nie muszą kompilować kilkunastu innych języków podczas codziennego rozwoju.

Szukam sposobów, aby zapobiec kompilacji plików .resx obcego języka. Wymyśliłem dwie metody i szukam porady, czy ktoś jest lepszy, czy też istnieją inne lepsze metody.

Dwa Mam wymyślić:

  • pisać skrypty, które mogą Gazy i dodać z powrotem w innych niż domyślne plików .resx w różnych plikach .csproj. Być może zachowalibyśmy minimalne pliki .csproj w kontroli wersji i oddzielny proces kompilacji, aby ponownie rekursywnie dodawać pliki .resx w celu ponownego zintegrowania nowych tłumaczeń, wykonania testów i wykonania naszych wersji wdrożeniowych.
  • Rysowanie sposobu na zastąpienie wbudowanych obiektów docelowych MSBuild, które wykonują analizę składni i kompilację resx, skutecznie wyłączając je dla wszystkich oprócz domyślnego języka. Programiści mogliby włączyć/wyłączyć to zachowanie za pomocą prostej flagi kompilacji lub przełącznika kompilacji. Jeszcze nie zagłębiłem się w dostarczone pliki .target firmy Microsoft, aby zobaczyć, jak rozsądne lub możliwe do utrzymania jest to rozwiązanie.

Aktualizacja

napisałem poniższy skrypt PowerShell, aby przenieść wszystkie moje EmbeddedResources języków obcych i kompilację elementów do nowego ItemGroup który posiada atrybut warunkowy.

$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 

Odpowiedz

2

Można skorzystać z istniejących mechanizmów w MSBuild, które pozwalają obsługiwać ten scenariusz, jak elementu documentated here<Choose> i możliwość wprowadzenia własnych konfiguracji kompilacji. Jeśli twoi programiści budują tylko konfiguracje debugowania podczas codziennej pracy, możesz nie potrzebować nawet własnej konfiguracji kompilacji: możesz skonfigurować swój projekt tak, aby wszystkie zasoby języków obcych były uwzględniane tylko w kompilacjach wydań.

Ten projekt sekcji pliku by upewnić się, że polskie zasoby są zbudowane wyłącznie w konfiguracji Release (kody językowe są prawdopodobnie źle, ale masz pomysł):

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

W przypadku programiści muszą budować Zwolnij konfiguracje, możesz utworzyć własną konfigurację ReleaseResx i dołączyć pliki .resx obcego języka tylko w tej konfiguracji.

Obie twoje sugestie też by działały, po prostu myślę, że to podejście jest czystsze.

+0

Warunek warunku w ItemGroup wygląda czystsze.Ale generalnie zgadzam się - wolałbym włamać się do wewnątrz msbuild zamiast włączać-wyłączanie plików przez zewnętrzny skrypt –

+0

Świetnie! Zastanawiam się nad konfiguracją i konserwacją tej techniki. Podczas instalacji wyobrażam sobie, że będę musiał ręcznie edytować pliki csproj i wrzucić wszystkie zasoby domyślne do struktury Wybierz, prawda? Prawdopodobnie wystarczy skrypt. Po dodaniu nowych stron i formantów do rozwiązania, Visual Studio utworzy swoje zwykłe domyślne zasoby lokalizacji poza tą nową strukturą Choose, w której znajdują się pozostałe domyślne zasoby narodowe. Wtedy przypuszczam, że to wszystko jest kwestia, w jaki sposób chcemy zintegrować nowe tłumaczenia z plikami csproj. To jest obiecujące. –

+0

Instalacja jest najprawdopodobniej ręcznym krokiem, obawiam się. Zdobycie nowych zasobów w tłumaczeniach nie stanowi problemu, jeśli twoje biura tłumaczeń używają narzędzi takich jak SDL Passolo lub Alchemy Catalyst. – Jenszcz