Exclude certain files ItemGroup - msbuild

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...

Related

How do I use the exact msbuild item "unexpanded wildcard expression" value

I have the following MSBuild .proj file content:
<ItemGroup>
<Exclude Include="*2.*"></Exclude>
</ItemGroup>
<ItemGroup>
<!-- I have 3 files in the current directory: File1.cpp, File2.cpp and File3.cpp -->
<ModuleInclude Include="*.cpp" Exclude="#(Exclude)"></ModuleInclude>
<!-- I have 3 files in the Subfolder directory: eFile1.h, eFile2.h and eFile3.h -->
<ModuleInclude Include="Subfolder\*.h" Exclude="#(Exclude->'Subfolder\%(identity)')"></ModuleInclude>
</ItemGroup>
<Target Name="Default">
<Message Text="ModuleIncludes: %(ModuleInclude.identity)" />
<Message Text="Excluded Items: #(Exclude)" />
<Message Text="Excluded Subfolder Items: #(Exclude->'Subfolder\%(identity)')" />
</Target>
I see the following output:
ModuleIncludes: File1.cpp
ModuleIncludes: File3.cpp
ModuleIncludes: Subfolder\eFile1.h
ModuleIncludes: Subfolder\eFile2.h
ModuleIncludes: Subfolder\eFile3.h
Excluded Items: File2.cpp
Excluded Subfolder Items: Subfolder\File2.cpp
What I really need is to have the following Subfolder files included
ModuleIncludes: Subfolder\eFile1.h
ModuleIncludes: Subfolder\eFile3.h
The excluded subfolder items therefore should be:
Excluded Subfolder Items: Subfolder\eFile2.h
To be able to get such an output I would need the expression
Subfolder\*2.*
The syntax that I am using
#(Exclude->'Subfolder\%(identity)')
does not give me what I need.
What would the correct syntax be? Or is this not possible?
If you want to deal with wildcards as text, use properties instead of items:
<PropertyGroup>
<FileExcludes>*2.*</FileExcludes>
</PropertyGroup>
<ItemGroup>
<ModuleInclude Include="*.cpp" Exclude="$(FileExcludes)"></ModuleInclude>
<ModuleInclude Include="Subfolder\*.h" Exclude="Subfolder\$(FileExcludes)"></ModuleInclude>
</ItemGroup>
You can even use an exclude pattern here that will match regardless of the subfolder:
<PropertyGroup>
<FileExcludes>**\*2.*</FileExcludes>
</PropertyGroup>
<ItemGroup>
<ModuleInclude Include="*.cpp" Exclude="$(FileExcludes)"></ModuleInclude>
<ModuleInclude Include="Subfolder\*.h" Exclude="$(FileExcludes)"></ModuleInclude>
</ItemGroup>
If you really need it as a list to prepend non-local folders, use metadata items:
<PropertyGroup>
<FileExclude Include="2">
<Pattern>**\*2.*</Pattern>
</FileExclude>
</PropertyGroup>
<ItemGroup>
<ModuleInclude Include="*.cpp" Exclude="#(FileExclude)"></ModuleInclude>
<ModuleInclude Include="..\shared-folder\*.h" Exclude="#(FileExclude->'shared-folder\%(Pattern)')"></ModuleInclude>
</ItemGroup>

How to get list of files matching wildcard from a list of directories in msbuild

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>

Copy subfolders and files in msbuild

I'm unable to copy subfolders and files with this code:
<ItemGroup>
<Compile Include="C:\Test\Folder1\text.txt"/>
<Compile Include="C:\Test\text1.txt"/>
</ItemGroup>
<Copy SourceFiles="#(Compile)" DestinationFiles="#(Compile->'C:\Destination\%(RecursiveDir)%(Filename)%(Extension)')" />
I get this error: Could not find a part of the path.
How to copy C:\Test\ files and subfolders to C:\Destination\ with msbuild ?
Thanks in advance for your help.
In order for the RecursiveDir metadata to be populated, you must specify a recursive wildcard (double asterisks) in your items' paths. The ** wildcard will mark the relative point at which the RecursiveDir should be applied. In your example, it sounds like you'd want to add the ** wildcard after C:\Test, so your code would need to look like the following example:
<ItemGroup>
<Compile Include="C:\Test\**\Folder1\text.txt"/>
<Compile Include="C:\Test\**\text1.txt"/>
</ItemGroup>
<Copy SourceFiles="#(Compile)" DestinationFiles="#(Compile->'C:\Destination\%(RecursiveDir)%(Filename)%(Extension)')" />
Adding the wildcard as shown above will copy the files to the following locations:
C:\Destination\text1.txt
C:\Destination\Folder1\text.txt
The same as in response above, but without additional list transformation:
<ItemGroup>
<Compile Include="C:\Test\**\Folder1\text.txt"/>
<Compile Include="C:\Test\**\text1.txt"/>
</ItemGroup>
<Copy SourceFiles="#(Compile)" DestinationFolder="C:\Destination\%(RecursiveDir)" />

Add Custom Metadata to Already-defined ItemGroup from Another 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>

How to overwrite ItemGroup (like what we do with PropertyGroup)

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)">