Номер на версията в елемента dependentAssembly/assemblyIdentity на файла на манифеста

Работя с приложение, което включва неуправляван клиентски DLL и управляван COM сървър DLL (което само по себе си беше предизвикателство: Managed Reg-Free COM сървър няма да се активира), и сега се чудя кой е най-добрият начин да поддържам номерата на версиите в синхрон. Тъй като изграждаме както клиента, така и сървъра и се опитваме да поддържаме номерата на версиите на всички наши файлове синхронизирани за всяка компилация, изглежда, че трябва да имам процес, който редактира всички мои манифестни файлове както на клиента, така и на сървъра края на всички мои изолирани COM препратки, преди да се случи пълно изграждане. Има ли по-лесен начин?

Пример (клиентски манифест):

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
   <assemblyIdentity type="win32" name="globals" version="1.0.0.0" />
   <dependency>
      <dependentAssembly>
         <assemblyIdentity type="win32" name="SoftBrands.FourthShift.FSCulture" version="8.0.0.999" publicKeyToken="541b4aff0f04b60a" />
      </dependentAssembly>
   </dependency>
</assembly>

Сървърен манифест:

<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <assemblyIdentity type="win32" name="SoftBrands.FourthShift.FSCulture" version="8.0.0.999" publicKeyToken="541b4aff0f04b60a" />
   <clrClass   clsid="{23D4FF3D-EEDF-4F68-AD65-749958EE3B2A}"
               name="SoftBrands.FourthShift.FSCulture.FSCulture"
               tlbid="{8D480B22-D603-309F-9A26-EA9E9B020207}">
   </clrClass>
</asmv1:assembly>

Бих могъл просто да направя глобално търсене и да заменя на version="8.0.0.999" с текущата версия за всяка компилация, но подозирам, че може да има по-добър начин.


person BlueMonkMN    schedule 07.12.2015    source източник
comment
Обмисляли ли сте персонализирана задача MSBuild, за да свършите работата?   -  person Randy    schedule 07.12.2015
comment
Не. Не съм запознат с персонализираните задачи на MSBuild. Може да е опция, ако приемем, че те няма да причинят счупване на системите на други разработчици (поради липса на инсталирана персонализация). На пръв поглед изглежда, че ще изисква инсталиране на друга сборка на системата на всеки разработчик. Мисля, че бих бил склонен да използвам решение за глобална замяна преди това.   -  person BlueMonkMN    schedule 07.12.2015
comment
Можете да включите сглобката за персонализираната задача като част от вашия проект, така че всеки да я има спрямо пътя на проекта. Обикновено проверяваме тези видове файлове в нашето хранилище за изходен код и използването им е прозрачно за всички разработчици.   -  person Randy    schedule 07.12.2015


Отговори (1)


Най-добрият ви залог е да използвате персонализирана задача MSBuild, за да манипулирате артефактите на проекта преди компилирането.

Задачата трябва да приема три свойства. Едно свойство за клиентския манифест, друго свойство за сървърния манифест и третото свойство за версията.

Ето как може да изглежда целевият файл в MSBuild.

Manifests.targets

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
  <!-- Import Tasks -->
  <!-- Be sure to sue the full namespace of the task to import -->
  <UsingTask TaskName="Full.Namespace.To.UpdateManifestsTask" AssemblyFile="$(MSBuildThisFileDirectory)\MyCustomTasks.dll" />
  <!-- Define the location of the client and server manifest files -->
  <PropertyGroup>
    <ClientManifest><!-- Location of the client manifest --></ClientManifest>
    <ServerManifest><!-- Location of the server manifest --></ServerManifest>
  </PropertyGroup>
  <!-- Define Generate App Config Target -->
  <Target Name="UpdateManifests">
    <UpdateManifests ClientManifest="$(ClientManifest)" ServerManifest="$(ServerManifest)" Version="$(Version)" />
  </Target>
  <!-- Define Before Build Target -->
  <!-- This will ensure the above target gets executed prior to compilation -->
  <Target Name="BeforeBuild">
    <CallTarget Targets="UpdateManifests;" />
  </Target>
</Project>

Ето как може да изглежда персонализираната задача -

UpdateManifestsTask.cs

public class UpdateManifestsTask : Task
{
    public ITaskItem ClientManifest { get; set; }
    public ITaskItem ServerManifest { get; set; }
    public ITaskItem Version { get; set; }

    public override bool Execute()
    {
        var newVersion = string.Format("name=\"SoftBrands.FourthShift.FSCulture\" version=\"{0}\"", this.Version.ItemSpec);
        var clientFile = File.ReadAllText(this.ClientManifest.ItemSpec);
        clientFile = Regex.Replace(clientFile, "name=\"SoftBrands.FourthShift.FSCulture\" version=\"\\d*\\.\\d*\\.\\d*\\.\\d*\"", newVersion);
        File.WriteAllText(this.ClientManifest.ItemSpec, clientFile);
        var serverFile = File.ReadAllText(this.ClientManifest.ItemSpec);
        serverFile = Regex.Replace(clientFile, "name=\"SoftBrands.FourthShift.FSCulture\" version=\"\\d*\\.\\d*\\.\\d*\\.\\d*\"", newVersion);
        File.WriteAllText(this.ServerManifest.ItemSpec, serverFile);

        return true;
    }
}

RegEx е малко небрежен, тъй като това беше бърз и мръсен пример, но това е ефективно начинът, по който бихте се захванали да правите подобни неща.

Не забравяйте да добавите препратки към библиотеките на MSBuild.

http://blogs.msdn.com/b/msbuild/archive/2006/01/21/515834.aspx

Ако не искате да създавате персонализирана задача, можете да напишете малко конзолно приложение, което прави същото, но според мен персонализираната задача е по-чиста.

Ако решите да отидете по пътя на конзолното приложение, можете да използвате събитията BeforeBuild и AfterBuild във вашия проектен файл.

  <Target Name="BeforeBuild">
      <!-- Invoke console app -->
  </Target>
  <Target Name="AfterBuild">
  </Target>
person Randy    schedule 07.12.2015
comment
Имате ли идея какво би направила персонализираната задача? Мога да се сетя за няколко възможности: задаване на макрос $(ProductVersion), който да съответства на този от споделен RC файл (след това използвайте този макрос в раздела Файл на манифеста на настройките на Linker на проекти); търсене в проекта за всички .manifest файлове и замяна на атрибутите на версията, съдържащи се в елементите assemblyIdentity; Стартирайте mt.exe, за да замените информацията за версията в манифестите след компилацията... - person BlueMonkMN; 07.12.2015
comment
Вижте актуализиран отговор, който предоставя повече подробности за това как можете да направите това. - person Randy; 07.12.2015
comment
Опитвам се да си представя как ще изглежда това, когато се използва, и подозирам, че просто ще изложи някои допълнителни свойства на проекта. Поради това не съм сигурен как това опростява процеса на синхронизиране на номерата на версиите във всички проекти в компилация. Има ли някакъв механизъм, чрез който цяло дърво от проекти, обхващащо много решения, може да споделя едни и същи стойности на свойства за определени свойства? - person BlueMonkMN; 08.12.2015
comment
Абсолютно. MSBuild ви позволява да направите това. Просто поставете свойствата си във файл *.properties и го включете във всеки друг MSBuild файл, който би се нуждаел от тези свойства. - person Randy; 08.12.2015
comment
Файлът със свойства не е нищо друго освен MSBuild файл, съдържащ свойства. Това би работило и за C# проекти. - person Randy; 11.12.2015
comment
Така че свойствата на разширението всъщност не означават нищо, освен да покажат на потребителите, че файлът вероятно съдържа PropertyGroup? - person BlueMonkMN; 11.12.2015
comment
Това е правилно – същото би било вярно за целевия файл. Това е един вид самодокументираща се конвенция за именуване. - person Randy; 11.12.2015
comment
Успях да получа изход за отстраняване на грешки от задачата с this.Log.LogError. Има ли по-добър начин? this.Log.LogMessage изглежда не извежда нищо. Изглежда проектът C++ дори не изпълнява задачата и не знам защо. Мога да накарам задачата да се изпълни само като включа <Import Project="..\..\..\FSBuildTasks\ManifestVersion.targets" /> в моя C# проект и същото не работи за C++ проекта. - person BlueMonkMN; 11.12.2015
comment
Можете да стартирате MSBuild на проекта VC++ от командния ред на Visual Studio. След това просто прикачете дебъгера на Visual Studio към процеса MSBuild и трябва да можете да разберете дали задачата ви се изпълнява. Също така се уверете, че сте импортирали задачата си във вашия проектен файл VC++. - person Randy; 11.12.2015
comment
Подробният изход на MSBuild както в C++, така и в C# проектите съобщава, че целта BeforeBuild се отменя от моя .targets файл, но няма повече препратки към него в C++ проекта, докато следващата препратка в C# компилацията отчита Target "BeforeBuild" in file "F:\FSBuildTasks\ManifestVersion.targets" from project "F:\Src\FSCulture\FSCulture\FSCulture.csproj" (target "Build" depends on it): Проверка в social.msdn.microsoft.com/Forums/vstudio/en-US/ - person BlueMonkMN; 11.12.2015
comment
Да, изглежда е проблем за VC++ проекти. Решението, предложено в статията, която споменахте, изглежда, че трябва да бъде жизнеспособно решение. - person Randy; 11.12.2015