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 "$(PsScript)" "$(MSBuildProjectDirectory)\version.txt" "$(LastVersionFile)"" 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. Может, когда-нибудь и удастся дорасти до более вменяемых вариантов. Пока что живу так, кайфую. Хотя и начинаю уже подозревать, что есть более вменяемые и простые механизмы решения подобных задач ![]()
> Для "сопутствующих" сборок
например собрал ты в конце года сборку
пересобрал после нового года, не поменял мажор минор.... лисп посчитает, что она старая???
предлагаю так....
как ты всегда учил надо минимизировать дублирование кода (относится и к свойствам проектов), для этого в нашем случае отлично подходит Directory.Build.props
кидаем его в корень каталога решения и прописываем в нем общие параметры для всех сборок
эти строчки отвечают за номер сборки
будет вида 0.1.9692.15284, для всех сборок проекта
если для какой то группы сборок нужен другой мажор минор, в каталоге этих сборок создаем Directory.Build.props и пишем там нужные цифры
если нужны индивидуальные свойства, то прописываем их в проекте сборки, они переопределят заданные в корневом Directory.Build.props
---
по поводу сборок NET Framework
я перевел их почти все на стиль SDK, типа так
учитывая, что у тебя есть Directory.Build.props если хочешь другой номер версии, то просто добавь
---
по поводу текстового файла с номером версии для обновлятора
в корень решения кидаем Directory.Build.targets
там прописываем такие строчки
формат любой, данные любые, тут только фантазией ограничено
у меня все сборки идут в один каталог, поэтому по каждой идет дозапись в файл.
единственное но... перед сборкой файл надо удалять руками или батником, как автоматизировать удаление перед общей сборкой мы с ботом пока не придумали
Возможно, я чего-то не понял (если честно, башка сейчас забита вопросом - то ли тех.долг закрывать, то ли дальше своего франкенштейна сшивать), но (как мне кажется), у тебя не сильно-то номера сборок назначаются. Использование маски типа 10.21.*, насколько я понял, для билда назначает количество дней с начала года, а для ревизии - количество минут с начала суток. Да и NET-сборки, которые не Framework, мне при компиляции ругались нехорошими словами. Возможно, я просто чего-то недоучитываю
> Использование маски типа 10.21.*, насколько я понял, для билда назначает количество дней с начала года
Build = количество дней с 1 января 2000 года (по локальному времени машины, где идёт сборка).
Revision = количество секунд от полуночи, делённое на 2 (то есть фактически кодирует время сборки в течение дня).
норм студия все собирает)
механизм полностью идентичный
false
AssemblyVersion=0.1.*