NET автоверсионность сборок

Напоминалка самому себе. Ибо постоянно искать подобную информацию ну так себе затея.

Есть задача - автоматически проставлять версию сборки, загружаемой в *CAD. При этом автоматом должен увеличиваться номер ревизии. Хоть до упора ;)

По ходу дела - для "сопутствующих" сборок надо повторить поведение, как бы это было в NET Framework, когда можно было назначить только мажор и минор, остальное каким-то магическим вариантом вычислялось самостоятельно.

Уточнение: все csproj-файлы выполнены в SDK-стиле. Для NET Standard / NET5+ это и так в норме, а вот для NET Framework придется поколдовать слегка. Ну или пересоздать проект ;)

Для "загружаемой" в CAD придется слегка поколдовать. Рядом с csproj создаю отдельный файл version.txt, в котором и прописываю, к примеру, версию "3.0.0.0". Как только будет выполнена компиляция в Release, версия загружаемой сборки должна стать 3.0.0.0, а в version.txt надо записать 3.0.0.1. Если понадобится менять мажор, минор или билд - добро пожаловать в редактирование verion.txt (понятно, что файлу надо установить свойство "не копировать в выходной каталог при компиляции"). А в csproj прописать примерно следующее:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<Target Name="IncrementVersion"
       BeforeTargets="BeforeBuild"
       Condition="'$(Configuration)' == 'Release'">

    <PropertyGroup>
        <LastVersionFile>$(IntermediateOutputPath)last_build_version.txt</LastVersionFile>
        <PsScript>$(IntermediateOutputPath)increment_version.ps1</PsScript>
    </PropertyGroup>
    <WriteLinesToFile
       File="$(PsScript)"
       Lines="$verFile = $args[0]
$lastFile = $args[1]
$desired = Get-Content $verFile -ErrorAction SilentlyContinue
if (-not $desired) { $desired = '1.0.0.0' }
$desired = $desired.Trim()
if (Test-Path $lastFile) {
   $last = (Get-Content $lastFile -ErrorAction Stop).Trim()
} else {
   $last = $null
}
if ($desired -eq $last) {
   $parts = $desired -split '\.'
   if ($parts.Count -eq 4) {
       $parts[3] = ([int]$parts[3]) + 1
       $new = $parts -join '.'
   } else {
       $new = $desired
   }
   Set-Content -Path $verFile -Value $new
   Set-Content -Path $lastFile -Value $new
   Write-Output $new
} else {
   Set-Content -Path $lastFile -Value $desired
   Write-Output $desired
}"

       Overwrite="true"
       Encoding="utf-8" />

    <Exec Command="powershell -NoProfile -ExecutionPolicy Bypass -File &quot;$(PsScript)&quot; &quot;$(MSBuildProjectDirectory)\version.txt&quot; &quot;$(LastVersionFile)&quot;"
         ConsoleToMSBuild="true">
        <Output TaskParameter="ConsoleOutput" PropertyName="IncrementedVersion" />
    </Exec>
</Target>

Плюс в PropertyGroup добавить:

1
2
3
        <Version>$(IncrementedVersion.Trim())</Version>
        <AssemblyVersion>$(Version)</AssemblyVersion>
        <FileVersion>$(Version)</FileVersion>

Выглядит как черная магия, согласен. Чисто по логике:

  • Самая первая попытка сборки Release увидит, что в каталоге \obj\Release (это если никакие дополнительные настройки не были изменены) файл last_build_version.txt отсутвует. То есть это типа изменение. Читается файл version.txt и устанавливается версия для сборки из файла version.txt
  • Следующая сборка видит, что файлы version.txt и last_build_version.txt одинаковы (если last_build.version.txt вообще существует ;) ). Оба файла перезаписать, увеличив номер ревизии. И увеличенный номер и назначить как версию.

По идее даже очистка каталогов \obj и \bin подход разрушить не должна. У меня по крайней мере пока все работает как часы ;)

Для "сопутствующих" сборок ситуация попроще. Достаточно в csproj-файл в разделе PropertyGroup добавить следующее:

1
2
3
4
5
6
<VersionPrefix>3.0</VersionPrefix>
    <Build>$([System.DateTime]::Now.DayOfYear)</Build>
    <Revision>$([System.DateTime]::Now.TimeOfDay.TotalMinutes.ToString("F0"))</Revision>
    <AssemblyVersion>$(VersionPrefix).$(Build).$(Revision)</AssemblyVersion>
    <Version>$(VersionPrefix).$(Build).$(Revision)</Version>
    <FileVersion>$(VersionPrefix).$(Build).$(Revision)</FileVersion>

Билд будет высчитываться как количество дней от начала года, а ревизия - как количество минут с полуночи текущей даты.

Если обновление dll выполняется лиспом (лично у меня достаточно частая ситуация, если честно), то чем сравнивать даты dll на локальной машине и серверной версии, значительно проще получать данные о версии сборки прямо из текстового файлика, который автоматом будет создаваться рядом с dll (прописывать текст надо тоже в csproj):

1
2
3
  <Target Name="CreateVersionFile" AfterTargets="Build" Condition="'$(Configuration)' == 'Release'">
    <WriteLinesToFile File="$(TargetDir)$(TargetName).txt" Lines="$(Version)" Overwrite="true" Encoding="utf-8" />
  </Target>

P.S. Может, когда-нибудь и удастся дорасти до более вменяемых вариантов. Пока что живу так, кайфую. Хотя и начинаю уже подозревать, что есть более вменяемые и простые механизмы решения подобных задач ;)

Размещено в .NET, Новости, Разное · Метки:



Поделитесь своим мнением


Я не робот.