I have a script snippet looks like below:
<ItemGroup>
<files Include="*.txt"></files>
</ItemGroup>
<Message Text="#(files)">
<ItemGroup>
<files Include="*.xml"></files>
</ItemGroup>
<Message Text="#(files)">
I want that in the second Message output, only *.xml is printed. Currently both of *.txt and *.xml are printed which is what I don't want to.
So, my question is how can we overwrite the item files in the second print script? Please help!
I find out a way to do it but I don't like it very much:
<ItemGroup>
<files Include="*.txt"></files>
</ItemGroup>
<Message Text="#(files)">
<ItemGroup>
<files Remove="#(files)"></files>
<files Include="*.xml"></files>
</ItemGroup>
<Message Text="#(files)">
Related
I want to include in an item group query files that match:
Web.*.config but be able to exclude one of them.
I've tried this but doesn't seem to work:
<ItemGroup>
<Files Include="Web.*.config"/>
<FilesToDelete Include="#(Files)" Condition="%(FileName) != 'Web.Base.config'" />
</ItemGroup>
<ItemGroup>
<Files Include="Web.*.config"/>
<FilesToDelete Include="#(Files)" Condition="%(Filename) != 'Web.Base'"/>
</ItemGroup>
Filename doesn't exclude the extension...
Give an item list of directories, how can I create an item list of files from those directories that are matching a wildcard?
The following doesn't expand the wildcard:
<ItemGroup>
<Files Include="#(Dirs -> '%(Identity)\*.c')" />
</ItemGroup>
and the following isn't allowed at all:
<ItemGroup>
<Files Include="#(Dirs)\*.c" />
</ItemGroup>
As usual found the answer after posting the questions:
<ItemGroup>
<Files Include="%(Dirs.Identity)\*.c" />
</ItemGroup>
I have the following:
<ItemGroup>
<Files Include="C:\Versioning\**\file.version" />
<ItemGroup>
<ReadLinesFromFile File="%(Files.Identity)">
<Output TaskParameter="Lines" ItemName="_Version"/>
</ReadLinesFromFile>
where each file.version file contains simply one line which is - you guessed it - a version of the form Major.Minor.Build.Revision.
I want to be able to associate each item in the Files ItemGroup with its _Version by adding the latter as metadata, so that I can do something like:
<Message Text="%(Files.Identity): %(Files.Version)" />
and have MSBuild print out a nice list of file-version associations.
Is this possible?
This can be achieved by using target batching to add your Version member to the metadata. This involves moving your ReadLinesFromFile operation to its own target, using the #(Files) ItemGroup as an input.
This causes the target to be executed for each item in your ItemGroup, allowing you to read the contents (i.e. version number) from each individual file and subsequently update that item to add the Version metadata:
<Project DefaultTargets="OutputFilesAndVersions" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Files Include="C:\Versioning\**\file.version" />
</ItemGroup>
<Target Name="OutputFilesAndVersions" DependsOnTargets="RetrieveVersions">
<Message Text="#(Files->'%(Identity): %(Version)')" />
</Target>
<Target Name="RetrieveVersions" Inputs="#(Files)" Outputs="%(Files.Identity)">
<ReadLinesFromFile File="%(Files.Identity)">
<Output TaskParameter="Lines" PropertyName="_Version"/>
</ReadLinesFromFile>
<PropertyGroup>
<MyFileName>%(Files.Identity)</MyFileName>
</PropertyGroup>
<ItemGroup>
<Files Condition="'%(Files.Identity)'=='$(MyFileName)'">
<Version>$(_Version)</Version>
</Files>
</ItemGroup>
</Target>
</Project>
<ItemGroup>
<!-- Unit Test Projects-->
<MyGroup Include="Hello.xml" />
<MyGroup Include="GoodBye.xml" />
</ItemGroup>
How do I make a task that iterates through this list and does something?
<XmlPeek XmlInputPath="%(MyGroup.Identity)"
Query="/results">
<Output TaskParameter="Result"
ItemName="myResult" />
</XmlPeek>
I want to thow an error message if myresult has a certain text inside of it. However for the life of me I can't figure out how to iterate through arrays in Msbuild... anyone know how to accomplish this?
You need to use batching for this. Batching will iterate over a set of items based on a metadata key. I've put together a bunch of material on this at http://sedotech.com/Resources#batching. For example take a look at this simple MSBuild file.
<Project DefaultTargets="Demo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Files Include="one.txt"/>
<Files Include="two.txt"/>
<Files Include="three.txt"/>
<Files Include="four.txt"/>
</ItemGroup>
<Target Name="Demo">
<Message Text="Not batched: #(Files->'%(Identity)')"/>
<Message Text="========================================"/>
<Message Text="Batched: %(Files.Identity)"/>
</Target>
</Project>
When you build the Demo target the results are
Not batched: one.txt;two.txt;three.txt;four.txt
========================================
Batched: one.txt
Batched: two.txt
Batched: three.txt
Batched: four.txt
Batching always uses the syntax %(Xyz.Abc). Take a look thorough those links for more info about batching then you ever wanted to know.
You could use batching on an inner target, like that :
<ItemGroup>
<!-- Unit Test Projects-->
<MyGroup Include="Hello.xml" />
<MyGroup Include="GoodBye.xml" />
</ItemGroup>
<Target Name="CheckAllXmlFile">
<!-- Call CheckOneXmlFile foreach file in MyGroup -->
<MSBuild Projects="$(MSBuildProjectFile)"
Properties="CurrentXmlFile=%(MyGroup.Identity)"
Targets="CheckOneXmlFile">
</MSBuild>
</Target>
<!-- This target checks the current analyzed file $(CurrentXmlFile) -->
<Target Name="CheckOneXmlFile">
<XmlPeek XmlInputPath="$(CurrentXmlFile)"
Query="/results/text()">
<Output TaskParameter="Result" ItemName="myResult" />
</XmlPeek>
<!-- Throw an error message if Result has a certain text : ERROR -->
<Error Condition="'$(Result)' == 'ERROR'"
Text="Error with results $(Result)"/>
</Target>
I’m trying to create a “Files” task item group with a metadata attribute called “TargetPath” populated with the relative path to a file.
Example:
For these paths:
D:\Test\Blah.exe
D:\Test\Config\fun.config
D:\Test\en-US\my.resources.dll
The output should be:
File Target = Blah.exe
File Target = Config\fun.config
File Target = en-US\my.resources.dll
Here is my best attempt... hopefully this makes my question clearer:
<ItemGroup>
<Files Include="d:\test\**\*" >
<TargetPath>%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</Files>
</ItemGroup>
<Message Text="File Target = #(Files->'%(TargetPath)')"/>
I'd like "TargetPath" populated correctly... currently it appears to be null or empty. Anyone know what I'm missing?
Edit:
Yes, I realize I can do this:
<Message Text="File Target = #(Files->'%(RecursiveDir)%(Filename)%(Extension)')"/>
However, I'm building up this ItemGroup to use the ResolveManifestFiles MSBuild task, which requires that I build up a TaskItem with the TargetPath metadata attribute to be able to customize that value.
You're trying to assign dynamic metadata to an itemgroup before it's created. In your example there's no need to create custom metadata since this information is already part of the well-known metadata, so you can just do:
<ItemGroup>
<Files Include="d:\test\**\*" ></Files>
</ItemGroup>
<Message Text="File Target = #(Files->'%(RecursiveDir)%(Filename)%(Extension)')"/>
Or:
<Message Text="File Target = %(Files.RecursiveDir)%(Files.Filename)%(Files.Extension)"/>
EDIT:
This example uses CreateItem task to dynamically update the itemgroup:
<ItemGroup>
<Files Include="d:\test\**\*" ></Files>
</ItemGroup>
<CreateItem
Include="#(Files)"
AdditionalMetadata="TargetPath=%(RecursiveDir)%(Filename)%(Extension)">
<Output TaskParameter="Include" ItemName="Files"/>
</CreateItem>
Modern MSBuild doesn't require CreateTask (since .NET 3.5).
You can do it like this:
<ItemGroup>
<Files Include="d:\test\**\*" />
<FilesWithMetadata Include="%(Files.Identity)" >
<TargetPath>%(RecursiveDir)%(Filename)%(Extension)</TargetPath>
</FilesWithMetadata>
</ItemGroup>
I am liking the CreateItem method to use like this:
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
...
</ItemGroup>
<CreateItem Include="#(Reference)" Condition="'%(Reference.Private)' == 'True'" AdditionalMetadata="TargetPath=$([System.IO.Path]::GetFileName(%(Reference.HintPath)))">
<Output TaskParameter="Include" ItemName="DLLFiles"/>
</CreateItem>
<Message Text="HintPaths: "#(DLLFiles->'$(OutputPath)%(TargetPath)')"" Importance="high" />
I am using Transforms to just get only the file name.
Output:
HintPaths:
"bin\Release\log4net.dll;bin\Release\Newtonsoft.Json.dll;bin\Release\RabbitMQ.Client.dll;bin\Release\ReflectSoftware.Insight.dll"