MSBuild: Compare ItemGroups or access by index

ⅰ亾dé卋堺 提交于 2021-01-28 19:22:09

问题


For a C++ project, I want to autogenerate a defs.h file with project definitions, such as the date, git commit, ... to automate the versioning process of my application.

Therefore I am trying to create a MSBuild Target that will extract the latest git tag, git commit, and the current date and save it to a temporary gitinfo.txt file. Another build target will depend on that file and generate a .h file. In order to avoid unnecessary recompiles of my project, the .h file and for that reason the gitinfo.txt file shall only be rewritten, if any of the information has changes.

So my idea is the following:

  1. Calculate git and date info
  2. If available, read in the existing gitinfo.txt
  3. Compare the calculated values to those in the txt file
  4. If anything has changed, rewrite the gitinfo.txt

I've mastered steps 1. and 2., however I am not sure how to process the values after reading them.

<!-- The purpose of this target is to update gitinfo.txt if git information (commit...) has changed -->
<Target
  Name="GetHeaderInfos"
  BeforeTargets="ClCompile"
  Outputs="$(IntDir)\gitinfo.txt"
>

  <!-- Get information about the state of this repo-->    
  <GitDescribe>
    <Output TaskParameter="Tag" PropertyName="NewGitTag" />
    <Output TaskParameter="CommitHash" PropertyName="NewGitCommitHash" />
    <Output TaskParameter="CommitCount" PropertyName="NewGitCommitCount" />
  </GitDescribe>

  <!-- Get the current date -->
  <Time Format="dd.MM.yyyy">
    <Output TaskParameter="FormattedTime" PropertyName="NewBuildDate" />
  </Time>

  <ReadLinesFromFile File="$(IntDir)\gitinfo.txt" Condition="Exists('$(IntDir)\gitinfo.txt')">
    <Output TaskParameter="Lines" ItemName="Version" />
  </ReadLinesFromFile> 

  <!-- Comparison here! HOW TO DO IT PROPERLY -->
  <PropertyGroup>
     <TagChanged> <!-- `$(NewGitTag)` == `$(Version)[0]` --> </TagChanged>
     <!-- Other comparisons -->
  </PropertyGroup>
</Target>

And this could be the content of gitinfo.txt

v4.1.4
04fe34ab
1
31.07.2016

I am not quite sure how to compare the values now. I need to compare $(NewGitTag) to the first value in the $(Version) version variable, and so on.

I haven't found an example, that actually accesses the variables after reading them from a file. The official documentation provides no help, nor have I found anything on stackoverflow or the likes.

I only know that the $(Version) variable holds a list, and I can batch process it. How can I compare its content to the defined variables $(NewGitTag), $(NewGitCommitHash), $(NewGitCommitCount) and $(NewBuildDate)?


回答1:


Suppose we start with this data:

<ItemGroup>
  <Version Include="v4.1.4;04fe34ab;1;31.07.2016"/>
</ItemGroup>

<PropertyGroup>
  <GitTag>v4.1.4</GitTag>
  <GitSHA>04fe34ab</GitSHA>
  <Count>1</Count>
  <Date>31.07.2016</Date>
</PropertyGroup>

Then here are at least 3 ways to achieve comparision (apart from the one mentioned in the comment) and there are probably other ways as well (I'll post them if I can come up with something else):

Just compare the items

I'm not sure why you want to compare everything seperately when this works just as well: compare the whole ItemGroup at once.

<Target Name="Compare1">
  <PropertyGroup>
    <VersionChanged>True</VersionChanged>
    <VersionChanged Condition="'@(Version)' == '$(GitTag);$(GitSHA);$(Count);$(Date)'">False</VersionChanged>
  </PropertyGroup>
  <Message Text="VersionChanged = $(VersionChanged)" />
</Target>

Batch and check if there's one difference

Each item of Version is compared with e.g. GitTag via batching. The result will be False;False;False;False if there's a difference, else it will be True;False;False;False. Count the distinct elements and if it's 2 it means we got the latter so GitTag did not change. Note this obviousle only works if each of your source items can never have the same value as one of the other items.

<Target Name="Compare2">
  <PropertyGroup>
    <TagChanged>True</TagChanged>
    <TagChanged Condition="'@(Version->Contains($(GitTag))->Distinct()->Count())' == '2'">False</TagChanged>
  </PropertyGroup>
  <Message Text="TagChanged = $(TagChanged)" />
</Target>

you can then compare the other items as well and combine the result.

Use an inline task to access items by index

This comes closest to what's in your question, but it does need a bit of inline code.

<UsingTask TaskName="IndexItemGroup" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
  <ParameterGroup>
    <Items Required="true" ParameterType="Microsoft.Build.Framework.ITaskItem[]"/>
    <Index Required="true" ParameterType="System.Int32"/>
    <Item Output="true" ParameterType="Microsoft.Build.Framework.ITaskItem"/>
  </ParameterGroup>
  <Task>
    <Code Type="Fragment" Language="cs">
      <![CDATA[Item = Items[ Index ];]]>
    </Code>
  </Task>
</UsingTask>

<Target Name="Compare3">
  <IndexItemGroup Items="@(Version)" Index="1">
    <Output PropertyName="OldGitSHA" TaskParameter="Item"/>
  </IndexItemGroup>

  <PropertyGroup>
    <SHAChanged>True</SHAChanged>
    <SHAChanged Condition="'$(GitSHA)' == '$(OldGitSHA)'">False</SHAChanged>
  </PropertyGroup>

  <Message Text="OldGitSHA = $(OldGitSHA), changed = $(SHAChanged)" />
</Target>


来源:https://stackoverflow.com/questions/38700241/msbuild-compare-itemgroups-or-access-by-index

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!