MsBuild Getting propertyvalue inside $([System.IO.Path]::GetFullPath - msbuild

i am creating a folder in the script executing directory with currentdatetime and pull doown source from source control.
<PropertyGroup>
<CurrentDate>$([System.DateTime]::Now.ToString(yyyy-MM-dd-mmss))
</CurrentDate>
</PropertyGroup>
But when i use the below inside a propertyGroup i do not get the value of $(CurrentDate) it gets escaped.
<FullPath>$([System.IO.Path]::GetFullPath('.\$(CurrentDate)\Build\Service\'))</FullPath>`
can any one suggest me what is wrong in the above. Is there any option use the property group value inside $([System.IO.Path])

Related

Does MSBuild has an OnImport callback or something similar to detect importing?

The intention in the title sounds more complex as it is
I have a script which looks like that:
<Import Project="MSBuild/Backend.msbuild.xml" />
<PropertyGroup>
<Data>SomeData</Data>
</PropertyGroup>
and I simply want to detect whether the Import statement is before or after the PropertyGroup because the Backend script looks like
<PropertyGroup>
<BasedOnData>$(Data)/magic.exe</BasedOnData>
</PropertyGroup>
If the Import statement comes first, BasedOnData just looks like /magic.exe which is wrong. If the Import comes last, everything is fine.
I tried to check the Data Property in a Target in Backend but at target-calling-time Data is already defined.
Resulting in
Data=SomeData
but
BasedOnData=/magic.exe
I could create an Error condition based whether $(BasedOnData)== '/magic.exe' but this is error prone as it relies on Data and BasedOnDatas value.
Having an Error condition in PropertyGroup does not work.
Any smarter solution to this known?
Should I fallback to CreateProperty in the first target so I am agnostic of the position of the Import statement? (This gets much more verbose and is not that easy to read than a plain nice PropertyGroup.)
Execution of MSBuild targets happens in separate pass, after all properties in property groups have been evaluated. So what you are observing is designed behavior.
To make import order checking in your case, the following should do the trick. Inside your Backend.msbuild.xml file add this:
<PropertyGroup>
<Data_Copy>$(Data)</Data_Copy>
</PropertyGroup>
This just makes a copy of whatever $(Data) value was at the time. Then in either same imported file or in any other target, add this <Error> task:
<Error Text="Import has to be specified after `Data` is defined" Condition="'$(Data_Copy)' == ''" />
This will break the build if Backend.msbuild.xml was imported before $(Data) is defined.

Condition statement to be used with "AfterCompileSolution" in tfsbuild

i need your help. I am running into a situation. I am trying to copy certain binaries into a particular folder. I am adding those task into "AfterCompileSolution" . I know it is incorrect, bcos it's gonna execute this step after every solution is compiled.
Here is my situation, i tried adding a condition like a SolutionFileName, but i get empty result. The target doesn't get executed because the SolutionFileName parameter is empty.
So do you know of any parameter that i can use between solutiontobuild i.e i want to copy certain binaries only after solution "A" is completed and i want these parameters to be part of "AfterCompileSolution" or maybe "BeforeCompileSolution"
Please suggest
Thanks
Satesh
It's been a while since I've done this but I believe you reference the file name with a syntax such as:
<Target Name="AfterCompileSolution" DependsOnTargets="RandomPreReqTarget">
<SomeTask Condition="'%(SolutionToBuildItem.Identity)' == 'ConditionValue'" />
</Target>
Another cool thing you can do is product extra properties in your SolutionToBuild item and reference them as metadata also like:
<SolutionToBuild Include="$(SolutionRoot)\$(SourceBranch)\RandomDirectory\Project.csproj">
<Targets>Build</Targets>
<Properties>OutDir=$(RandomDirectory);Configuration=$(Configuration);Platform=AnyCPU</Properties>
<GAC>True</GAC>
</SolutionToBuild>
You would then be able to access the metadata like this:
<Target Name="AfterCompileSolution" DependsOnTargets="RandomPreReqTarget">
<SomeTask Condition="'%(SolutionToBuildItem.GAC)' == 'True'" />
</Target>

MSBuild 4.0 property functions cannot access properties inside of them

Is it a limitation of MSBuild 4.0 property functions that I cannot access a property from inside of one?
Here is an example that works just fine:
<PropertyGroup>
<PartialConnection>$(TargetConnectionString.Substring( 0 + 12))</PartialConnection>
</PropertyGroup>
Here is another example that doe snot work. (I replace the 0 with another property)
<PropertyGroup>
<LocationOfDataSource>$(TargetConnectionString.IndexOf("Data Source="))</LocationOfDataSource>
</PropertyGroup>
<Message Importance="high" Text="Location is = $(LocationOfDataSource)"/>
<PropertyGroup>
<PartialConnection>$(TargetConnectionString.Substring( $(LocationOfDataSource) + 12))</PartialConnection>
</PropertyGroup>
this outputs
Location is = 0
Error MSB4184: The expression ""Data Source=MySQLServer;Integrated Security=True;Pooling=False".Substring(0 + 12)" cannot be evaluated. Input string was not in a correct format.
I took the output and plugged into a console app and it works just fine. I have tried several variations and I they always fail when I put a property inside a property function. (I even tried access the same property twice in a my property function and that failed too.)
Do property functions not support accessing properties?
I think my issue was assuming that math came for free.
I needed to do this kind of thing:
<PropertyGroup>
<LocationOfDataSource>$(TargetConnectionString.IndexOf("Data Source="))</LocationOfDataSource>
<LenthOfDataSourceString>12</LenthOfDataSourceString>
<LocationOfEndOfDataSourceString>$([MSBuild]::Add($(LocationOfDataSource), $(LenthOfDataSourceString)))</LocationOfEndOfDataSourceString>
<PartialConnectionString>$(TargetConnectionString.Substring($(LocationOfEndOfDataSourceString)))</PartialConnectionString>
</PropertyGroup>
Note that I am adding using Add($(Property), $(Property)) in this version. Add is one of the built-in MSBuild Property Functions (since MSBuild 4).
It seems to be working now.

Dynamic Metadata Assignment in an ItemGroup

I have an ItemGroup defined as:
<ItemGroup>
<ProtoFiles Include="Protos\*.proto"/>
</ItemGroup>
It yields a list of all .proto files in a directory of my project. I want each item in the group to include a piece of metadata that specifies the name of the file that will be generated based on the .proto file. I know I can do this:
<ItemGroup>
<ProtoFiles Include="Protos\*.proto">
<OutputFile>%(ProtoFiles.Filename).cs</OutputFile>
</ProtoFiles>
</ItemGroup>
But my problem is that it is not a simple mapping from .proto filename to output filename. There is some tricky logic involved that I need to encapsulate somewhere and call that when assigning metadata. I need something like:
<ItemGroup>
<ProtoFiles Include="Protos\*.proto">
<OutputFile><GetOutputFilename ProtoFilename="%(ProtoFiles.Filename)"/></OutputFile>
</ProtoFiles>
</ItemGroup>
The idea being that my custom GetOutputFilename task would be called in order to get the metadata value.
Is this possible? Am I barking up the wrong tree?
I think it's not, try instead passing the ItemGroup to a task to generate this metadata. Property Functions can operate on metadata values, but unfortunately cannot be used to define metadata.
It's hard to know if the logic is too tricky for MSBuild without knowing exactly what it is. Do you have a custom task that operates on #(ProtoFiles) to generate the output files? If so, why not alter your task (or refactor to a new one) that just calculates the output files without creating them, something like this,
<ProtoTask
Files="#(ProtoFiles)"
... other params
DryRun="true">
<Output
TaskParameter="OutputFiles"
ItemName="ProtoFiles" />
</ProtoFiles>
The task can clone the item array, calculate the metadata value, and assign it to the output item array, which in the example here overwrites the original item array passed into the task.

How can task parameters be defaulted in MSBuild

In mytask.targets, I have something like:
<UsingTask TaskName="DoStuff" AssemblyFile="....etc....."/>
<PropertyGroup>
<RequiredParamDefault>hello</RequiredParamDefault>
</PropertyGroup>
This task currently has a required parameter (which could be changed from required if necessary).
When the task is used:
<DoStuff RequiredParam="$(RequiredParamDefault)" OtherParam="wobble"/>
Currently, RequiredParam has to be specified everytime. Is there anyway that when UsingTask is defined, the default can be set up so it doesn't have to be specified on every use of DoStuff?
I know the default could be hardcoded in the assembly, but I'd like to be able to define different defaults with different UsingTask statements.
Thanks.
You can't do this at the UsingTask or Task but instead you can using properties that you pass into the task. For example.
<Target>
<PropertyGroup>
<ReqParam Condition=" '$(ReqParam)'=='' ">Param-Default-Value</ReqParam>
</PropertyGroup>
<DoStuff RequiredParam="$(ReqParam)" OtherParam="wobble"/>
</Target>
In this case I define the property ReqParam to be Param-Default-Value only if the property doesn't already have a value. This is not exactly what you are looking for, but it may be your best option unless you can change the task itself.