For my team city configuration, I have a build parameter with a potential preprocessor macro definition. It's a checkbox with this value: /p:DefineConstants=IncludeFleetSimulation . It has that value so that I can easily add that into my MSBuild parameters, obviously. However, I also want to change the output filename. I don't want /p:... in the filename. Instead, I want "IFS". Is there some way to insert something conditionally with the TeamCity parameter parsing? I'm picturing something like this: %IFS% != "" ? "_IFS" : ""% . How do I achieve that in TeamCity?
I ended up writing a Nant script step to make this happen:
<project name="RenameFilesBasedOnParamters" default="Rename">
<target name="Rename">
<property name="IncludeFleetSimulation" value="%IncludeFleetSimulation%" />
<property name="currentDirectory" value="%system.teamcity.build.workingDir%" />
<if test="${IncludeFleetSimulation != ''}">
<foreach item="File" property="file">
<in>
<items basedir="${currentDirectory}">
<include name="*%build.number%_%build.vcs.number%*" />
</items>
</in>
<do>
<move file="${file}"
tofile="${currentDirectory + '\' +
path::get-file-name-without-extension(file) +
'_FS' + path::get-extension(file)}" />
</do>
</foreach>
</if>
...
Related
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:
Calculate git and date info
If available, read in the existing gitinfo.txt
Compare the calculated values to those in the txt file
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)?
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>
So ... I have build.xml that loads property file from basedir.
Then, as the target I perform the following:
<var name="Var1" value="<property_from_**first**_loaded_property_file>" />
<var name="<property_from_**first**_loaded_property_file>" unset="true"/>
<property file="../<other directory>/<**second**_property_file>.properties" />
<var name="Var2" value="<property_from_**second**_loaded_property_file>"/>
The ceavat here is that both has same property name. It cannot be changed.
So, in the end, I should get the property like:
Var1=<property_from_**first**_loaded_property_file>
Var2=<property_from_**second**_loaded_property_file>
But instead -
I am getting signs that property (Var1) from first properties file is not unset and then filled with new value from second properties file. The thing that ant-contribs unset should deal with :/ ... something like:
Var1 = Var2
Why I am not getting the expected result?
I think the issue is that even though you're loading the variable into an antcontrib var, it's still an ant property first, thus immutable.
I know you can't change the property files, but what kind of freedom do you have with the script itself? You can try to leverage the scoping rules and the antcallback task to scope where the variables get loaded.
For example, the following achieves - albeit somewhat messily - what I think you're after:
<?xml version="1.0" encoding="utf-8"?>
<project name="Test" basedir=".">
<path id="ant.classpath">
<fileset dir="${basedir}">
<include name="ant-contrib_AP.jar"/>
</fileset>
</path>
<taskdef resource="net/sf/antcontrib/antcontrib.properties" classpathref="ant.classpath"/>
<target name="test">
<antcallback target="load-more-prop" return="Var2"/>
<loadproperties>
<file file="prop1.properties"/>
</loadproperties>
<property name="Var1" value="${var}" />
<echo>${Var1}</echo>
<echo>${Var2}</echo>
</target>
<target name="load-more-prop">
<loadproperties>
<file file="prop2.properties"/>
</loadproperties>
<property name="Var2" value="${var}" />
</target>
</project>
In my console I see:
Buildfile: C:\Users\mfelzani\workspace-junk\junk\build.xml
test:
load-more-prop:
[echo] 7
[echo] 1
BUILD SUCCESSFUL
Total time: 905 milliseconds
Which matches the values i set in prop1.properties and prop2.properties, respectively, for the var property.
You can't unset the value.
WRONG: <var name="<property_from_**first**_loaded_property_file>" unset="true"/>
You have to unset the variable
CORRECT: <var name="Var1" unset="true"/>
If you need to overwrite some existing property or userproperty (those properties defined via ant commandline parameter -Dkey=value), you might use Ant Plugin Flaka as alternative for antcontrib.
With Flaka's let task you either create a new property or overwrite any existing property straightforward :
<project xmlns:fl="antlib:it.haefelinger.flaka">
<property name="foo" value="bar"/>
<!-- create new property -->
<fl:let>foo := 'baar'</fl:let>
<echo>$${foo} => ${foo}</echo>
<!--
overwrite existing property
notice the double '::' in foo ::= 'baz'
-->
<fl:let>foo ::= 'baz'</fl:let>
<echo>$${foo} => ${foo}</echo>
</project>
I'm trying to copy a bunch of files whose names begin with the prefix DR__, but the copies must have that prefix removed. That is, DR__foo must be copied as foo. I'm trying this, which is based in the example provided in the documentation (the .chm):
<Target Name="CopyAuxiliaryFiles">
<MakeDir Directories="$(TargetDir)Parameters" Condition="!Exists('$(TargetDir)Parameters')" />
<ItemGroup>
<ContextVisionParameterFiles Include="$(SolutionDir)CVParameters\DR__*" />
</ItemGroup>
<Message Text="Files to copy and rename: #(ContextVisionParameterFiles)"/>
<RegexReplace Input="#(ContextVisionParametersFiles)" Expression="DR__" Replacement="">
<Output ItemName ="DestinationFullPath" TaskParameter="Output" />
</RegexReplace>
<Message Text="Renamed Files: #(DestinationFullPath)"/>
<Copy SourceFiles="#(ContextVisionParameterFiles)" DestinationFiles="#(DestinationFullPath)" />
</Target>
DestinationFullPath comes out empty (or that's what I see when I display it with Message). Thus, Copy fails because no DestinationFiles are specified. What's wrong here?
Edit: ContextVisionParameterFiles is not empty, it contains this:
D:\SVN.DRA.WorkingCopy\CVParameters\DR__big_bone.alut;D:\SVN.DRA.WorkingCopy\CVParameters\DR__big_medium.gop
They're actually 40 files, but I trimmed it for the sake of clarity
Got it! It seems to have been the combination of a stupid error and a seemingly compulsory parameter. As for the first one, there were two Targets called CopyAuxiliaryFiles. As for the second one, it seems the Count parameter is needed.
The final, working version:
<Target Name="CopyCvParameters">
<ItemGroup>
<CvParamFiles Include="$(SolutionDir)CVParameters\DR__*" />
</ItemGroup>
<Message Text="Input:
#(CvParamFiles, '
')"/>
<!-- Replaces first occurance of "foo." with empty string-->
<RegexReplace Input="#(CvParamFiles)" Expression="^.*DR__" Replacement="$(TargetDir)Parameters\" Count="1">
<Output ItemName ="RenamedCvParamFiles" TaskParameter="Output" />
</RegexReplace>
<Message Text="
Output RenamedCvParamFiles:
#(RenamedCvParamFiles, '
')" />
<Copy SourceFiles="#(CvParamFiles)" DestinationFiles="#(RenamedCvParamFiles)" SkipUnchangedFiles="True" />
</Target>
Notice that:
I renamed the Target to solve the name collision (Why doesn't Visual Studio detect this as an error?)
I pretty-printed the ItemGroups with the #(CvParamFiles, '
') syntax, which seems to replace ; with line breaks
My regex replaces the absolute path and the prefix
Count="1" is now passed to RegexReplace
I'm trying to load a list of filenames from a text file and then run an Exec task for each entry retrieved from the text file.
So I have a file, let's call it SomeFile.txt containing the following:
FileA.file
FileB.file
FileC.file
The MsBuild code I have for this looks like this (which doesn't work:)
<Target Name="runScripts">
<ItemGroup>
<scriptsFile Include="SomeFile.txt" />
</ItemGroup>
<ReadLinesFromFile File="#(scriptsFile)">
<Output TaskParameter="Lines" ItemName="scriptItems" />
</ReadLinesFromFile>
<Message Text="Running Exec for each entry..." />
<Exec Command="$(someCommand) %(scriptItems)" />
</Target>
This gives me an error saying I need to specify an item name, but if I use anything like %(scriptItems.item) or %(itemname.scriptItems) MsBuild simply puts a blank instead of %(scriptItems).
Simply need to use %(scriptItems.Identity) to get an item name from the metadata. This is well documented at MSDN.
In reworking our deployment process I moved over to using an MSBuild project in place of our existing batch files. All of the major elements are in place, and I was looking to cut out a step or two but ran into a snag.
I'm creating a property called OutputPath using the CombinePath task, and, while I can access it with no issues after it has been created I'm at a loss as for how to use it to my advantage. Consider:
<CombinePath BasePath ="$(DeployFolderRoot)" Paths ="$(DeployReleaseFolder)$(ReleaseFolderFormatted)" >
<Output TaskParameter ="CombinedPaths" ItemName ="OutputFolder"/>
</CombinePath>
<MakeDir Directories="#(OutputFolder)" />
<MakeDir Directories="#(OutputFolder)\Foo" />
<MakeDir Directories="#(OutputFolder)\Bar" />
Commands 2 and 3 fail because I'm referencing an array and attempting to concatenate with a string. Creating a property and assigning it #(OutputFolder) simply results in another item group, not a property I can reference with the $ accessor. I do have an ugly workaround but I'd love to clear this up somewhat.
Thanks,
-Jose
I'm not sure of the answer exactly but here is an idea:
<CombinePath BasePath ="$(DeployFolderRoot)" Paths ="$(DeployReleaseFolder)$(ReleaseFolderFormatted)" >
<Output TaskParameter ="CombinedPaths" ItemName ="OutputFolder"/>
</CombinePath>
<OutputFolder Include="$(DeployFolderRoot)$(DeployReleaseFolder)$(ReleaseFolderFormatted)\Foo" />
<OutputFolder Include="$(DeployFolderRoot)$(DeployReleaseFolder)$(ReleaseFolderFormatted)\Bar" />
<MakeDir Directories="#(OutputFolder)" />
Essentially, if you create OutputFolder items with the path they will just be appended to the list. This would have to be in an element btw, and you have to use Include="".
dOh! Definitely ignorance, used the wrong attribute on the Output element.
<CombinePath BasePath ="$(DeployFolderRoot)" Paths ="$(DeployReleaseFolder)$(ReleaseFolderFormatted)" >
<Output TaskParameter ="CombinedPaths" PropertyName="OutputFolder"/>
</CombinePath>
<MakeDir Directories="$(OutputFolder)" />
<MakeDir Directories="$(OutputFolder)\Foo" />
<MakeDir Directories="$(OutputFolder)\Bar" />