I'm having problem in accessing a PropertyGroup which is declared inside a target.
Message inside Target is publishing the version number correctly. When i try to publish the VersionNumber and VersionInfo ,I’m able to see only correct value for VersionInfo as true but VersionNumber is displayed as empty string.I want the VersionNumber value also published here
Please help !
Below is my code file :
<PropertyGroup >
<FileLocation>C:\Dev\version.txt</FileLocation>
<VersionInfo>false</VersionInfo>
<VersionInfo Condition="Exists('C:\Dev\version.txt')">true</VersionInfo>
</PropertyGroup>
<Target Name="ReadFromFile">
<ReadLinesFromFile File="$(FileLocation)" >
<Output PropertyName="VersionNumber"
TaskParameter="Lines"/>
</ReadLinesFromFile>
<Message Text="Inside Target (Version Number) : $(VersionNumber)"/>
</Target>
<ItemDefinitionGroup>
<PreBuildEvent>
<Command>
echo VersionNumber: $(VersionNumber)
echo VersionInfo: $(VersionInfo)
</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
I found a solution for my problem.Even i could remove the entire Target-ReadfromFile and could read the text file content in property group itself.I used property function - System.IO.File::ReadAllText to achieve my functionality.
It turned out to be a simple solution
More details about property functions can be found here
My code looks like below now :
<PropertyGroup >
<FileLocation>C:\Dev\version.txt</FileLocation>
<VersionInfo>false</VersionInfo>
<VersionInfo Condition="Exists('C:\Dev\version.txt')">true</VersionInfo>
<VersionDetails>$([System.IO.File]::ReadAllText($(FileLocation)))</VersionDetails>
</PropertyGroup>
Now i can access VersionDetails property anywhere in the project
Related
I have a path for compilation :
C:\FolderA\Folder16-9\ForderC\FolderD
I want to extract the name of the second folder "Folder16-9" to put in a variable and use it after for compilation destination path.
How I can extract this folder name? is it possible with MSBuild scripting?
The following example code
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Test">
<PropertyGroup>
<ExamplePath>C:\FolderA\Folder16-9\ForderC\FolderD</ExamplePath>
<SecondPart>$(ExamplePath.Split('\')[2])</SecondPart>
</PropertyGroup>
<Message Text="$(ExamplePath)" />
<Message Text="$(SecondPart)" />
</Target>
</Project>
will output
C:\FolderA\Folder16-9\ForderC\FolderD
Folder16-9
String methods can be used on properties. The example code is splitting the string on '/' and from the resulting array is using the value of index 2.
In the example, I defined the test value as a property. If you need to work with Item metadata, e.g. %(FullPath), note that property functions can't be used on metadata and the metadata will need to pulled out into a property.
In building an MSBuild script, I need to define a series of properties that are default but can be overridden when running the script. According to the following article, you should use a Conditional to default the property:
http://msdn.microsoft.com/en-us/library/ee240983.aspx
How Microsoft recommends:
<PropertyGroup>
<MyProperty Condition="'$(MyProperty)' == '' ">Default Value</MyProperty>
</PropertyGroup>
However, this behaves exactly the same way:
<PropertyGroup>
<MyProperty>Default Value Without Conditional</MyProperty>
</PropertyGroup>
So, if I have this Target and invoke it with either of the above, it has the same behavior:
<Target Name="DefaultsTest">
<Message Text="$(MyProperty)"></Message>
</Target>
Invocation:
msbuild build.xml /t:DefaultsTest /p:MyProperty="Overridden value"
Please explain the benefits of using the Condition attribute if you are only defaulting the same property that can be overridden from the invocation?
Update:
Here is a full simple config file to demonstrate: defaults.xml
<?xml version="1.0" encoding="utf-8" ?>
<Project DefaultTargets="DefaultsTest" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MyProperty Condition=" '$(MyProperty)' == '' ">MyProperty with Conditional</MyProperty>
<MyOtherProperty>MyOtherProperty without Conditional</MyOtherProperty>
</PropertyGroup>
<Target Name="DefaultsTest">
<Message Text="$(MyProperty)"></Message>
<Message Text="$(MyOtherProperty)"></Message>
</Target>
</Project>
This can be run simply as msbuild defaults.xml
or
msbuild defaults.xml /p:MyProperty="Changed Value" /p:MyOtherProperty="Changed as well"
You correctly noticed, that you can achieve behavior you want with unconditional assignment of property. The following project when built without /p: override on command line will produce Default Value. And when built using command msbuild myproj.proj /t:DefaultsTest /p:MyProperty=NewValue will produce NewValue.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MyProperty>Default Value</MyProperty>
</PropertyGroup>
<Target Name="DefaultsTest">
<Message Text="MyProperty=$(MyProperty)"></Message>
</Target>
</Project>
This is because any properties specified on MSBuild command line or provided as a parameter of task will be treated as Global Properties. For global properties, any assignment or modifications, either conditional or unconditional, are simply ignored -- global properties will remain constant throughout lifetime of the project execution.
The only difference in behavior between conditional assignment and unconditional will be if you use TreatAsLocalProperty attribute.
For example, consider the following project:
<Project TreatAsLocalProperty="Prop1;Prop2" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Prop1>Prop1 Default Value</Prop1>
<Prop2 Condition="$(Prop2) == ''">Prop2 Default Value</Prop2>
</PropertyGroup>
<Target Name="DefaultsTest">
<Message Text="Prop1=$(Prop1)"></Message>
<Message Text="Prop2=$(Prop2)"></Message>
</Target>
</Project>
Two properties -- Prop1 and Prop2 are both declared as local in the Project element. Prop1 is assigned unconditionally, while Prop2 is assigned using non-empty condition. Executing build command:
msbuild b.proj /t:DefaultsTest /p:Prop1=NewValue1 /p:Prop2=NewValue2
will produce output:
Prop1=Prop1 Default Value
Prop2=NewValue2
This means that in general case (if you are not absoluterly sure if property will be global or local), it is safer to use conditional assignment of the default value, because it works always.
I want to write a number in a text file using WriteLinesToFile but the task is putting a line feed at the end which causes me trouble when i want to read or combine in other places
Example:
<WriteLinesToFile File="$(TextFile)" Lines="#(BuildNumber)" Overwrite="true"/>
UPDATE as the user comment below:
The problem that I had was that I was using a very simple command in Property to read the content of a file $([System.IO.File]::ReadAllText("$(TextFile)")) and I really want to use this one but it also included the line feed from WriteLinesToFiles. I ended up using similar solution like yours using ReadLinesFromFile.
There is a slight dissconnect between the title and the description. I would have liked to post this "answer" as an edit, but do not have enough reputation points :)
Do you have a problem with the newline at the end of a file, or do you have a problem ignoring that newline? Could you please clarify?
One way how I suppose you could ignore that newline follows.
This small snippet of code writes a build number to a file, then reads it out and then increments the number read by 1.
<Target Name="Messing_around">
<PropertyGroup>
<StartBuildNumber>1</StartBuildNumber>
</PropertyGroup>
<ItemGroup>
<AlsoStartBuildNumber Include="1"/>
</ItemGroup>
<!-- Use a property
<WriteLinesToFile File="$(ProjectDir)test.txt" Lines="$(StartBuildNumber)" Overwrite="true"/>
-->
<WriteLinesToFile File="$(ProjectDir)test.txt" Lines="#(AlsoStartBuildNumber)" Overwrite="true"/>
<ReadLinesFromFile File="$(ProjectDir)test.txt">
<Output
TaskParameter="Lines"
ItemName="BuildNumberInFile"/>
</ReadLinesFromFile>
<PropertyGroup>
<OldBuildNumber>#(BuildNumberInFile)</OldBuildNumber>
<NewBuildNumber>$([MSBuild]::Add($(OldBuildNumber), 1))</NewBuildNumber>
</PropertyGroup>
<Message Importance="high" Text="Stored build number: #(BuildNumberInFile)"/>
<Message Importance="high" Text="New build number: $(NewBuildNumber)"/>
</Target>
And this is what I see
Output:
1>Build started xx/xx/xxxx xx:xx:xx.
1>Messing_around:
1> Stored build number: 1
1> New build number: 2
1>
1>Build succeeded.
If you attempting to read, in an MSBuild Task, a single line containing only a number from a file with a trailing line feed, then you should not have a problem.
As a side note: With the little information at hand I'd assume that BuildNumber is an Item in an ItemGroup. If you have only one build number to deal with, perhaps Property may have been an option. But then, again, I haven't been tinkering with MSBuild for too long. So, I am open to feedback on the Item vs Property issue.
I am trying to modify property value depending on certain condition in another file.
For ex.
I have one file that calls target file.
<Import Project="sample.vcxproj"/>
<PropertyGroup>
<Gender>Boy</Gender>
<Search>UNIQUE_NAME</Search>
</PropertyGroup>
<Target Name="Build">
<callTarget Targets="SetName"/>
<Message Text="$(Person)"/>
</Target>
I have one file that includes item group to decide and target that modifies
<ItemGroup>
<Name Include="UNIQUE_NAME">
<Boy>DAVID</Boy>
<Girl>REBECCA</Girl>
</NAME>
</ItemGroup>
<Target Name="SetName">
<PropertyGroup Condition="'$(Search)'=='#(Name)'">
<Person>#(Name->'%($(Gender))')</Person>
</PropertyGroup>
</target>
But when I print 'Person' I get empty string. And I checked that 'SetName' is called and correct name is set.
What am I missing here?
This has to do with the accessibility of MSBuild properties, depending on whether you are using DependsOnTargets or CallTarget. When using DependsOnTargets you will have greater access to properties. That is why your example works when using that method.
There is an existing stackoverflow article that speaks to this issue.
It works fine using 'DependsOnTarget' attrib instead of callTarget task
In MSBuild I have a property which value is Name_Something. How can I get name part of this property.
With MSBuild 4
If you use MSBuild 4, you could use the new and shiny property functions.
<PropertyGroup>
<MyProperty>Name_Something</MyProperty>
</PropertyGroup>
<Target Name="SubString">
<PropertyGroup>
<PropertyName>$(MyProperty.Substring(0, $(MyProperty.IndexOf('_'))))</PropertyName>
</PropertyGroup>
<Message Text="PropertyName: $(PropertyName)"/>
</Target>
With MSBuild < 4
You could use the RegexReplace task of MSBuild Community Task
<PropertyGroup>
<MyProperty>Name_Something</MyProperty>
</PropertyGroup>
<Target Name="RegexReplace">
<RegexReplace Input="$(MyProperty)" Expression="_.*" Replacement="" Count="1">
<Output ItemName ="PropertyNameRegex" TaskParameter="Output" />
</RegexReplace>
<Message Text="PropertyNameRegex: #(PropertyNameRegex)"/>
</Target>
If I understand your question correctly you are trying to get the substring of a MSBuild property. There is no direct way to do string manipulation in MSBuild, like in NAnt. So you have two options:
1). Create separate variables for each part and combine them:
<PropertyGroup>
<Name>Name</Name>
<Something>Something</Something>
<Combined>$(Name)_$(Something)</Combined>
</PropertyGroup>
This works fine if the parts are known before hand, but not if you need to do this dynamically.
2). Write a customer MSBuild task that does the string manipulation. This would be your only option if it needed to done at runtime.
It looks like you could use Item MetaData instead of a Property:
<ItemGroup>
<Something Include="SomeValue">
<Name>YourName</Name>
<SecondName>Foo</SecondName>
</Something>
</ItemGroup>