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>
Related
If I have
<PropertyGroup>
<Prop1>C:\asdfsa\abc;C:\sadf\def;C:\asfddsa\abc;</Prop1>
</PropertyGroup>
How do I remove all entries that contain \abc?
I want final value of $(Prop1) to be C:\sadf\def.
A property doesn't have 'entries', it's merely a string. You could fiddle with string splitting and/or regexes to erase some parts from it. On the other hand MSBuild also has Items which are more like proper lists. Going round via them is probably easier:
<Target Name="RemoveItemsFromProperty">
<PropertyGroup>
<Prop1>C:\asdfsa\abc;C:\sadf\def;C:\asfddsa\abc;</Prop1>
</PropertyGroup>
<ItemGroup>
<Items Include="$(Prop1)"/>
<FilteredItems Include="#(Items)" Condition="! $([System.String]::Copy('%(Identity)').Contains('\abc'))"/>
</ItemGroup>
<PropertyGroup>
<Prop1>#(FilteredItems)</Prop1>
</PropertyGroup>
<Message Text="$(Prop1)" />
</Target>
edit ok the regex way is easier though I'm not 100% sure my pattern covers all cases:
<Target Name="RemoveItemsFromProperty">
<PropertyGroup>
<Prop1>C:\asdfsa\abc;C:\sadf\def;C:\asfddsa\abc;</Prop1>
</PropertyGroup>
<Message Text="$([System.Text.RegularExpressions.Regex]::Replace('$(Prop1)', ';[.^;]\\abc', ''))" />
</Target>
If I have a property
<PropertyGroup>
<MyProp>abd;efg;hij;klm</MyProp>
</PropertyGroup>
How do I parse $(MyProp) to check the presence of klm?
You can use property functions to invoke the string Contains() method to check for string occurrences. While some other options using the Items (through an Include="$(MyProp)" and checking if an item with the expected identity exists) is also possible, conditions using property functions can be used on any msbuild element, both inside and outside of targets.
Example:
<Project>
<PropertyGroup>
<MyProp>abd;efg;hij;klm</MyProp>
</PropertyGroup>
<Target Name="Build">
<Message Importance="high" Text="klm is included!" Condition="$(MyProp.Contains('klm'))" />
<PropertyGroup>
<MyProp>;$(MyProp);</MyProp>
</PropertyGroup>
<Message Importance="high" Text="exactly klm is included!" Condition="$(MyProp.Contains(';klm;'))" />
</Target>
</Project>
The second approach - pre- and appending ; and checking for ;klm; - makes sure that the string is matched as a whole in the list can deal with ;aklm.
I have been attempting to use the zip task of msbuild in a project I am working on at the moment.
My project file looks something like this:
<PropertyGroup> <MSBuildCommunityTasksPath>$(SolutionDir)\.build</MSBuildCommunityTasksPath> </PropertyGroup>
<Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.Targets" />
<ItemGroup>
<FileToZip include="C:\FilePath"></FilesToZip>
<FileToZip include="C:\FilePath"></FilesToZip>
</ItemGroup>
<Target Name="BeforeBuild">
<PropertyGroup>
<ReleasePath>\releasepath</ReleasePath>
<Zip Files="#(FilesToZip)" WorkingDirectory="$(ReleasePath)" ZipFileName="HTMLeditor.html" ZipLevel="9" />
</Target>
However, the zip file updates but does not contain the files specified in the item group FilesToZip. I cannot figure out why they aren't being recognised! I have double checked file paths and they are correct. Any ideas?
I think you want to do something like this:
<ItemGroup>
<FileToZip include="C:\FilePath;C:\FilePath"/>
</ItemGroup>
As I mentioned in my comment, simply creating a variable (FileToZip) and repeating it twice with different values does not give you an array that contains both of the values. You end up with only the last value (and not an array at all). Your include attribute is a selector which is used to build the array and it can contain multiple values, wildcards and other patterns which are used to build out that array for you.
Here's a link to MSDN that gives you more information on how to use the Include attribute: http://msdn.microsoft.com/en-us/library/ms171454.aspx
I ditched the ItemGroup in the end, and went with another way of doing it.
<Target Name="Zip">
<CreateItem Include="FilesToInclude" >
<Output ItemName="ZipFiles" TaskParameter="Include"/>
<Zip ZipFileName="ZipFile.zip" WorkingDirectory="FolderToWriteZipTo" Files="#(ZipFiles)" />
</Target>
This method seemed to be easier and wasn't adding files to the root of the file.
Thanks for the help though guys.
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
I have list of items like this:
<ItemGroup>
<ToCompile Include="clojure\core.clj;clojure\set.clj;clojure\zip.clj;clojure\test\junit.clj;"/>
</ItemGroup>
And I want to transform that to a list of items like this:
clojure.core clojure.set clojure.zip clojure.test.junit
Is there a way to do this with MSBuild transforms? I tried but I can only get at the file name; the extension and the root path, and I can change the separator. But not the path separators.
If not, any other solution that avoids using custom tasks is appreciated.
We can do it easily with fewer cheese:
<Message
Text="$([System.String]::Copy('%(ToCompile.Identity)').Replace('.clj','').Replace('\','.'))"/>
This is a bit cheesy, but it works in MSBuild 4.0+.
<Target Name="Namespaces">
<PropertyGroup>
<Cheesy>#(ToCompile -> '%(relativedir)%(filename)', ' ')</Cheesy>
</PropertyGroup>
<Message Text="$(Cheesy.Replace(`\`, `.`))" />
</Target>