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
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 –
Ś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. –
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