MSBuild - Calling target multiple times with different parameters - msbuild

I'm trying to transform web.configs as part of a build process through MSBuild, which is fine; but dealing with multiple web.configs in the same solution is causing problems.
The code we are using at the moment extracts web.config specific information and passes it to a transform target, both of these operations are bundled in a DependsOnTargets target:
<Target Name="ExtractWebConfigParams_1">
<!-- Get webConfig1 info -->
</Target>
<Target Name="TransformWebConfig_1">
<TransformXml Source="%(webConfig1).Web.config"
Transform="%(webConfig1).Web.Stage.config"
Destination="%(webConfig1).Web.config"
StackTrace="$(StackTraceEnabled)" />
</Target>
<Target Name="ExtractWebConfigParams_2">
<!-- Get webConfig2 info -->
</Target>
<Target Name="TransformWebConfig_2">
<TransformXml Source="%(webConfig2).Web.config"
Transform="%(webConfig2).Web.Stage.config"
Destination="(webConfig2).Web.config"
StackTrace="$(StackTraceEnabled)" />
</Target>
<Target
Name="Transform_1"
DependsOnTargets="ExtractWebConfigParams_1;
TransformWebConfig_1;">
</Target>
<Target
Name="Transform_2"
DependsOnTargets="ExtractWebConfigParams_2;
TransformWebConfig_2;">
</Target>
Our solution may contain up to 5 different web.configs, so there would have to be an extract, transform and DependsOnTargets target for every one of them.
I can't see a way of getting around using multiple extract targets but does anyone know if there's a way to call the transform target with different parameters instead of making an entirely new target everytime?

You can write a separate .msbuild (.proj) file as "reusable logic".
I have a "zip up website" common logic I'll post below.
My example was about zipping up an asp.net website, but encapsulating the rules about which files to ignore (.csproj for example).......and also have a few "hooks" for ignoring some files. Like the "images" directory, ours was HUGE, so I didn't want to zip that up every time.
My example is not directly related to your need. Its the idea that is important.
Encapsulate all your logic into one file, and pass parameters to it.
I include the .proj file in the Main.proj file. Then pass parameters to it.
ONE CAVEAT. Relative directories do NOT work in the sub .proj file if it is located anywhere besides the same directory as the Main.proj file.
Ala, you cannot set a directory property to something like ".\bin\", you have to figure out the full path BEFORE you call the sub-proj file and pass the full folder name. Is this example, "c:\myfolder\mysolution\myproject1\bin" ... aka, whatever f
Code to put in the "outside" Main.proj file:
<Target Name="ZipItUpUsingCommonLogic">
<Message Text=" " />
<Message Text=" About to Call External MSBUILD File " />
<Message Text=" " />
<MSBuild Projects="..\..\CommonLogicMsBuildStuff\WebSiteZippingCommonLogic.proj" Targets="WebSiteZippingAllTargetsWrapper" Properties="WebSiteFolderFullPath=c:\workstuff\mywebsolution;OutputFolderFullPath=c:\workstuff\buildoutputs;WebSiteZipFileNameNonConfig=MyNonConfigFiles$(Configuration).zip;WebSiteZipFileNameConfigFiles=MyWebSiteConfigFiles$(Configuration).zip;RevisionNumber=333;IgnoreFolder1=c:\workstuff\mywebsolution\images" />
</Target>
Code for a file named "WebSiteZippingCommonLogic.proj":
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="WebSiteZippingAllTargetsWrapper">
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />
<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\MSBuild.ExtensionPack.tasks" />
<!-- There was an issue with the xsl/document(path) function......this help address the issue. -->
<Target Name="WebSiteZippingAllTargetsWrapper">
<CallTarget Targets="ShowParameters" />
<CallTarget Targets="ValidateParameters" />
<CallTarget Targets="ZipTheWebSite" />
</Target>
<Target Name="ValidateParameters">
<Error Text="The WebSiteFolderFullPath property was not passed in correctly." Condition="'$(WebSiteFolderFullPath)' == ''" />
<Error Text="The OutputFolderFullPath property was not passed in correctly." Condition="'$(OutputFolderFullPath)' == ''" />
<Error Text="The WebSiteZipFileNameNonConfig property was not passed in correctly." Condition="'$(WebSiteZipFileNameNonConfig)' == ''" />
<Error Text="The WebSiteZipFileNameConfigFiles property was not passed in correctly." Condition="'$(WebSiteZipFileNameConfigFiles)' == ''" />
<!--<Error Text="The RevisionNumber property was not passed in correctly." Condition="'$(RevisionNumber)' == ''" />-->
</Target>
<Target Name="ShowParameters">
<Message Text=" WebSiteFolderFullPath = $(WebSiteFolderFullPath)" />
<Message Text=" OutputFolderFullPath = $(OutputFolderFullPath)" />
<Message Text=" WebSiteZipFileNameNonConfig = $(WebSiteZipFileNameNonConfig)" />
<Message Text=" WebSiteZipFileNameConfigFiles = $(WebSiteZipFileNameConfigFiles)" />
<Message Text=" IgnoreFolder1 = $(IgnoreFolder1)" />
<Message Text=" IgnoreFolder2 = $(IgnoreFolder2)" />
<Message Text=" IgnoreFolder3 = $(IgnoreFolder3)" />
<Message Text=" " />
<Message Text=" " />
</Target>
<Target Name="ZipTheWebSite" DependsOnTargets="ValidateParameters">
<ItemGroup>
<WebSiteExcludeFiles Include="$(WebSiteFolderFullPath)\**\*.sln" />
<WebSiteExcludeFiles Include="$(WebSiteFolderFullPath)\**\*.vbproj" />
<WebSiteExcludeFiles Include="$(WebSiteFolderFullPath)\**\*.csproj" />
<WebSiteExcludeFiles Include="$(WebSiteFolderFullPath)\**\*.config" />
<WebSiteExcludeFiles Include="$(WebSiteFolderFullPath)\.svn\**\*.*" />
<WebSiteExcludeFiles Include="$(WebSiteFolderFullPath)\obj\**\*.*" />
<WebSiteExcludeFiles Include="$(WebSiteFolderFullPath)\**\.svn\**" />
<WebSiteExcludeFiles Include="$(WebSiteFolderFullPath)**\.svn\**\*.*" />
<WebSiteExcludeFiles Include="$(IgnoreFolder1)\**\*.*" Condition="'$(IgnoreFolder1)' != ''" />
<WebSiteExcludeFiles Include="$(IgnoreFolder2)\**\*.*" Condition="'$(IgnoreFolder2)' != ''" />
<WebSiteExcludeFiles Include="$(IgnoreFolder3)\**\*.*" Condition="'$(IgnoreFolder3)' != ''" />
</ItemGroup>
<ItemGroup>
<WebSiteNonConfigIncludeFiles Include="$(WebSiteFolderFullPath)\**\*.*" Exclude="#(WebSiteExcludeFiles)">
</WebSiteNonConfigIncludeFiles>
</ItemGroup>
<MSBuild.Community.Tasks.Zip Files="#(WebSiteNonConfigIncludeFiles)" ZipFileName="$(OutputFolderFullPath)\$(WebSiteZipFileNameNonConfig)" WorkingDirectory="$(WebSiteFolderFullPath)\" />
<ItemGroup>
<WebSiteConfigIncludeFiles Include="$(WebSiteFolderFullPath)\**\*.config">
</WebSiteConfigIncludeFiles>
</ItemGroup>
<MSBuild.Community.Tasks.Zip Files="#(WebSiteConfigIncludeFiles)" ZipFileName="$(OutputFolderFullPath)\$(WebSiteZipFileNameConfigFiles)" WorkingDirectory="$(WebSiteFolderFullPath)\" />
<Message Text=" " />
<Message Text=" " />
</Target>
</Project>
If you don't want to encapsulate rules into a separate file, then you may be looking for this:
http://sstjean.blogspot.com/2006/09/how-to-get-msbuild-to-run-complete.html
However, I find the constant "condition-checks" annoying, which is why I went to the "by file" method, which I describe above.
I'm going to copy/paste his example here, just in case his blog ever goes down.
Remember "gotdotnet.com" ??
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Test" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Package Include="CommonWebSetup.ism">
<PackagerType>IS</PackagerType>
<SetupProjFolder>CommonWebSetup</SetupProjFolder>
<ISProductConfig>Server</ISProductConfig>
<ISReleaseConfig>Release</ISReleaseConfig>
</Package>
<Package Include="CommonClientSetup.vdproj">
<PackagerType>VS</PackagerType>
<SetupProjFolder>CommonClientSetup</SetupProjFolder>
<ISProductConfig>Client</ISProductConfig>
<ISReleaseConfig>Release</ISReleaseConfig>
</Package>
</ItemGroup>
<Target Name="Test" Outputs="%(Package.Identity)" >
<Message Text="Removing read-only flag for %(Package.Identity)" Importance="High" />
<Message Text="Setting Environment variable for %(Package.Identity)" Importance="High" />
<Message Condition=" '%(Package.PackagerType)' == 'IS' "
Text="Running InstallShield for %(Package.Identity)" Importance="High" />
<Message Condition=" '%(Package.PackagerType)' == 'VS' "
Text="Running DevEnv.exe for %(Package.Identity)" Importance="High" />
</Target>

Related

How do I envoke a MSBuild target in the middle of another target?

What I'm looking for is something like:
<Target Name="DoStuff" >
<Message Text="Doing stuff..." />
//run target DoOtherThing
<Message Text="Doing more stuff..." />
</Target>
There's CallTarget which you'd use like
<Target Name="DoStuff" >
<Message Text="Doing stuff..." />
<CallTarget Targets="DoOtherThing" />
<Message Text="Doing more stuff..." />
</Target>
and there's the more idiomatic, albeit a bit over the top for this case, way:
<ItemGroup>
<MyTargets Include="Message1" />
<MyTargets Include="DoOtherThing" />
<MyTargets Include="Message2" />
</ItemGroup>
<Target Name="Message1" />
<Message Text="Doing stuff..." />
</Target>
<Target Name="DoOtherThing" />
<CallTarget Targets="DoOtherThing" />
</Target>
<Target Name="Message2" />
<Message Text="Doing more stuff..." />
</Target>
<Target Name="DoStuff" DependsOnTargets="#(MyTargets)">
</Target>

Transforming multiple config files for multiple projects via MsBuildProj file

I am trying to run multiple commands on a list of files based on a pattern (all files of form *.config under sub directories of a given directory), like so:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="BuildSolution" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<UsingTask TaskName="TransformXml"
AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v12.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Release</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<TransformConfiguration>Release</TransformConfiguration>
<PublishFolder>$(OutDir)_PublishedWebsites</PublishFolder>
</PropertyGroup>
<Target Name="BuildSolution">
<MSBuild Projects="$(MSBuildProjectDirectory)\SolutionName.sln"
ContinueOnError="false"
Properties="PublishProfile=$(TransformConfiguration);DeployOnBuild=true" />
</Target>
<ItemGroup>
<ConfigFiles Include="$(PublishFolder)\**\*.config" />
</ItemGroup>
<Target Name="TransformWebConfig" AfterTargets="BuildSolution"
Condition="'#(ConfigFiles)'!=''"
Outputs="%(ConfigFiles.Identity)">
<PropertyGroup>
<ConfigFile>%(ConfigFiles.Identity)</ConfigFile>
<BackupFile>$([System.IO.Path]::ChangeExtension($(ConfigFile),".Source.config"))</BackupFile>
<TransformFile>$([System.IO.Path]::ChangeExtension($(ConfigFile),$(TransformConfiguration) + ".Source.config"))</TransformFile>
</PropertyGroup>
<Message Text="$(ConfigFile)" />
<Message Text="$(BackupFile)" />
<Message Text="$(TransformFile)" />
<Copy SourceFiles="$(ConfigFile)"
DestinationFiles="$(BackupFile)" />
<Exec Command="attrib -r $(ConfigFile)" />
<TransformXml Source="$(BackupFile)"
Transform="$(TransformFile)"
Destination="$(ConfigFile)"
StackTrace="false" />
</Target>
</Project>
However, the batch processing of the matching files is not performed.
From outputs I have added I see that the property $(PublishFolder) points to the correct directory, however, the item #(ConfigFiles) is left empty.
I also tried manually listing the directory names and configuration file names like so:
<ItemGroup>
<Sites Include="Site1" />
<Sites Include="Site2" />
</ItemGroup>
<ItemGroup>
<ConfigFiles Include="Web" />
<ConfigFiles Include="NLog" />
</ItemGroup>
<Target Name="TransformWebConfig" AfterTargets="BuildSolution">
<PropertyGroup>
<SiteConfigFile>$(PublishFolder)\%(Sites.Identity)\%(ConfigFiles.Identity)</SiteConfigFile>
</PropertyGroup>
<Message Text="$(SiteConfigFile)" />
<Copy SourceFiles="$(SiteConfigFile).config"
DestinationFiles="$(SiteConfigFile).Source.config"/>
<Exec Command="attrib -r $(SiteConfigFile).config" />
<TransformXml Source="$(SiteConfigFile).Source.config"
Transform="$(SiteConfigFile).$(TransformConfiguration).config"
Destination="$(SiteConfigFile).config"
StackTrace="false" />
</Target>
However, in this case, the transform is only applied on one file in one site.
Any idea what to do to get this working?
Similar questions and MSDN references I have gone through:
MSBUild: Copy files with a name based on the original (following a pattern)
MSBuild multiple outputpath
How to invoke the same msbuild target twice with different parameters from within msbuild project file itself
msbuild array iteration
http://msdn.microsoft.com/en-us/library/ms171454.aspx
Edit:
Moving the ItemGroup under the task enabled reading the file list after the files were created, however, now only the first file from the list is transformed:
<ItemGroup>
<ConfigFiles Include="$(PublishFolder)\**\Web.config;$(PublishFolder)\**\NLog.config"
Exclude="$(PublishFolder)\**\Packages.config;$(PublishFolder)\**\*.*.config;$(PublishFolder)\**\bin\*.config" />
</ItemGroup>
<PropertyGroup>
<ConfigFile>%(ConfigFiles.Identity)</ConfigFile>
<BackupFile>$([System.IO.Path]::ChangeExtension($(ConfigFile),".Source.config"))</BackupFile>
<TransformFilePrefix>$([System.String]::Concat($(TransformConfiguration), ".config"))</TransformFilePrefix>
<TransformFile>$([System.IO.Path]::ChangeExtension($(ConfigFile), $(TransformFilePrefix)))</TransformFile>
</PropertyGroup>
<Message Text="$(PublishFolder)" />
<Message Text="#(ConfigFiles)" />
<Message Text="$(ConfigFile)" />
<Message Text="$(BackupFile)" />
<Message Text="$(TransformFile)" />
<Copy SourceFiles="$(ConfigFile)"
DestinationFiles="$(BackupFile)" />
<Exec Command="attrib -r $(ConfigFile)" />
<TransformXml Source="$(BackupFile)"
Transform="$(TransformFile)"
Destination="$(ConfigFile)"
StackTrace="false" />
Found the solution:
Create one target for creating the item group:
<Target Name="ListWebConfigs" AfterTargets="BuildSolution">
<ItemGroup>
<ConfigFiles Include="$(PublishFolder)\**\Web.config;$(PublishFolder)\**\NLog.config"
Exclude="$(PublishFolder)\**\Packages.config;$(PublishFolder)\**\*.*.config;$(PublishFolder)\**\bin\*.config" />
</ItemGroup>
<Message Text="$(PublishFolder)" />
<Message Text="#(ConfigFiles)" />
</Target>
Then another for the actual transforms:
<!-- \x to prevent MSBuild from skipping "because all output files are up-to-date" -->
<Target Name="TransformWebConfig" AfterTargets="ListWebConfigs" Inputs="#(ConfigFiles)" Outputs="%(ConfigFiles.Identity)\x">
<PropertyGroup>
<ConfigFile>%(ConfigFiles.Identity)</ConfigFile>
<BackupFile>$([System.IO.Path]::ChangeExtension($(ConfigFile),".Source.config"))</BackupFile>
<TransformFilePrefix>$([System.String]::Concat($(TransformConfiguration), ".config"))</TransformFilePrefix>
<TransformFile>$([System.IO.Path]::ChangeExtension($(ConfigFile), $(TransformFilePrefix)))</TransformFile>
</PropertyGroup>
<Message Text="$(PublishFolder)" />
<Message Text="#(ConfigFiles)" />
<Message Text="$(ConfigFile)" />
<Message Text="$(BackupFile)" />
<Message Text="$(TransformFile)" />
<Copy SourceFiles="$(ConfigFile)" DestinationFiles="$(BackupFile)" />
<Exec Command="attrib -r $(ConfigFile)" />
<TransformXml Source="$(BackupFile)"
Transform="$(TransformFile)"
Destination="$(ConfigFile)"
StackTrace="false" />
</Target>

MSBUILD Batching (Looping) Over Files and String Replacing the FIleName(s)

I am trying to "loop" (batching) over a list of files, and string.replace the root directory name with a different directory name.
This project is using 3.5, thus cannot take full advantage of some new 4.0 features.
I am using the
MSBuild.ExtensionPack.Framework.TextString
task (from
http://msbuildextensionpack.codeplex.com/releases/57599/download/229487
)
Bascially, I want to rewrite a filename
c:\SomeFolder\myfile.txt
to
c:\AnotherFolder\myfile.txt
I've written a mock to demonstrate the issue.
Here are the results I am getting:
"C:\Windows\twunk_16.exe" "c:\WindowsFake\write.exe"
"C:\Windows\twunk_32.exe" "c:\WindowsFake\write.exe"
"C:\Windows\winhlp32.exe" "c:\WindowsFake\write.exe"
"C:\Windows\write.exe" "c:\WindowsFake\write.exe"
You'll notice that "c:\WindowsFake\write.exe" (right side) is repeated for each item in the batch.
:<
What I am trying to get:
"C:\Windows\twunk_16.exe" "c:\WindowsFake\twunk_16.exe"
"C:\Windows\twunk_32.exe" "c:\WindowsFake\twunk_32.exe"
"C:\Windows\winhlp32.exe" "c:\WindowsFake\winhlp32.exe"
"C:\Windows\write.exe" "c:\WindowsFake\write.exe"
I (kinda) understand why I am getting the repeat value.
But (of course) cannot figure out how to address the issue.
Don't get hung up on the "windows" "fakewindows", these are just mock examples I created, because most people have a c:\windows directory on their computer, and I needed some "grab" some files.
Again, I am using 3.5 MSBuild, not 4.0.
set msBuildDir=%WINDIR%\Microsoft.NET\Framework\v3.5
call %msBuildDir%\msbuild.exe Master_MSBuild.xml /p:Configuration=Release /l:FileLogger,Microsoft.Build.Engine;logfile=Master_MSBuild_LOG.log
set msBuildDir=
Here is the complete .msbuild code. (Which I have in a file called 'Master_MSBuild.xml'.)
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AllTargetsWrapper">
<!-- -->
<!-- -->
<UsingTask AssemblyFile=".\MSBuild.ExtensionPack.dll" TaskName="MSBuild.ExtensionPack.Framework.TextString" />
<!-- -->
<!-- -->
<PropertyGroup>
<WindowsForRealDirectory>$(windir)</WindowsForRealDirectory>
<WindowsFakeDirectory>c:\WindowsFake</WindowsFakeDirectory>
</PropertyGroup>
<!-- -->
<!-- -->
<Target Name="DetermineAllFilesTarget">
<!-- -->
<ItemGroup>
<MyExcludedFiles Include="$(WindowsForRealDirectory)\**\notepad.exe" />
<MyExcludedFiles Include="$(WindowsForRealDirectory)\**\b*.exe" />
<MyExcludedFiles Include="$(WindowsForRealDirectory)\**\e*.exe" />
<MyExcludedFiles Include="$(WindowsForRealDirectory)\**\f*.exe" />
</ItemGroup>
<!-- -->
<ItemGroup>
<MyIncludedFiles Include="$(WindowsForRealDirectory)\Microsoft.NET\*.exe" Exclude="#(MyExcludedFiles)" />
<MyIncludedFiles Include="$(WindowsForRealDirectory)\*.exe" Exclude="#(MyExcludedFiles)" />
</ItemGroup>
<!-- -->
<!-- This create item may be redundant. -->
<CreateItem Include="#(MyIncludedFiles)">
<Output TaskParameter="Include" ItemName="FoundFolders" />
</CreateItem>
<!-- -->
<!-- -->
<Message Text="rootdir: #(FoundFolders->'%(rootdir)')" />
<Message Text="fullpath: #(FoundFolders->'%(fullpath)')" />
<Message Text="rootdir + directory + filename + extension: #(FoundFolders->'%(rootdir)%(directory)%(filename)%(extension)')" />
<Message Text="identity: #(FoundFolders->'%(identity)')" />
<Message Text="filename: #(FoundFolders->'%(filename)')" />
<Message Text="directory: #(FoundFolders->'%(directory)')" />
<Message Text="relativedir: #(FoundFolders->'%(relativedir)')" />
<Message Text="extension: #(FoundFolders->'%(extension)')" />
<!-- -->
</Target>
<!-- -->
<!-- -->
<!-- -->
<Target Name="ReplaceAndShowAllFileNames" Inputs="#(FoundFolders)" Outputs="#(FoundFolders.Identity')">
<!-- -->
<Message Text=" " />
<Message Text="FoundFolders.FileName = %(FoundFolders.FileName)" />
<Message Text="FoundFolders.FullPath = %(FoundFolders.FullPath)" />
<Message Text=" rootdir + directory + filename + extension = '%(FoundFolders.rootdir) *** %(FoundFolders.directory) *** %(FoundFolders.filename) *** %(FoundFolders.extension)' " />
<Message Text="FoundFolders.rootdir = %(FoundFolders.rootdir)" />
<Message Text="FoundFolders.directory = %(FoundFolders.directory)" />
<Message Text="FoundFolders.relativedir = %(FoundFolders.relativedir)" />
<Message Text="FoundFolders.extension = %(FoundFolders.extension)" />
<Message Text="FoundFolders.recursivedir = %(FoundFolders.recursivedir)" />
<Message Text=" " />
<Message Text=" " />
<Message Text=" " />
<!-- -->
<Message Text=" (WindowsFakeDirectory)= $(WindowsFakeDirectory)" />
<!-- -->
<!-- Replace the contents of a string -->
<MSBuild.ExtensionPack.Framework.TextString TaskAction="Replace" OldString="%(FoundFolders.relativedir)%(FoundFolders.Filename)%(FoundFolders.Extension)" OldValue="$(WindowsForRealDirectory)" NewValue="$(WindowsFakeDirectory)">
<Output PropertyName="RewrittenFileName" TaskParameter="NewString" />
</MSBuild.ExtensionPack.Framework.TextString>
<!-- -->
<CreateProperty Value="$(CheckedOutFileName)">
<Output PropertyName="CheckedOutFileNameTwo" TaskParameter="Value" />
</CreateProperty>
<!-- -->
<Message Text=" " />
<Message Text="The Result of the String.Replace: $(RewrittenFileName)" />
<!-- -->
<Message Text=" " />
<Message Text="Command: "The original and the rewritten file name: " "%(FoundFolders.relativedir)%(FoundFolders.Filename)%(FoundFolders.Extension)" "$(RewrittenFileName)"" />
<!-- -->
</Target>
<!-- -->
<!-- -->
<!-- -->
<Target Name="AllTargetsWrapper">
<!-- -->
<CallTarget Targets="DetermineAllFilesTarget" />
<!-- -->
<CallTarget Targets="ReplaceAndShowAllFileNames" />
<!-- -->
</Target>
<!-- -->
</Project>
Is there any reason you aren't just doing simple item transforms? It would be far less code.
<ItemGroup>
<MyIncludedFiles
Include="$(WindowsForRealDirectory)\**\Microsoft.NET\*.exe"
Exclude="#(MyExcludedFiles)"
/>
<MyIncludedFiles
Include="$(WindowsForRealDirectory)\*.exe"
Exclude="#(MyExcludedFiles)"
/>
</ItemGroup>
...then
<ItemGroup>
<MyTransformedFiles
Include="#(MyIncludedFiles->'$(WindowsFakeDirectory)\%(RecursiveDir)%(Filename)%(Extension)')
/>
</ItemGroup>
Note the extra ** before Microsoft.NET, so that RecursiveDir has a value.

Why doesn't Content Remove work for MSBuild ItemGroup?

I have an AfterCompile target defined in my csproj which involves minifying and combining JS and CSS. I then add them to ItemGroup Content and remove the unnecessary files, however the Remove paramter does not seem to work.
<Target Name="AfterCompile">
<ItemGroup>
<JS_Combine Include="js\c??.*.min.js" />
<CSS_Combine Include="css\c??.*.min.css" />
</ItemGroup>
<!-- Combine JS -->
<ReadLinesFromFile File="%(JS_Combine.Identity)">
<Output TaskParameter="Lines" ItemName="JSLines" />
</ReadLinesFromFile>
<WriteLinesToFile File="js\combined.min.js" Lines="#(JSLines)" Overwrite="true" />
<!-- Combine CSS -->
<ReadLinesFromFile File="%(CSS_Combine.Identity)">
<Output TaskParameter="Lines" ItemName="CSSLines" />
</ReadLinesFromFile>
<WriteLinesToFile File="css\combined.min.css" Lines="#(CSSLines)" Overwrite="true" />
<!-- Tidy up -->
<ItemGroup>
<Content Include="js\combined.min.js" />
<Content Include="css\combined.min.css" />
<Content Remove="#(JS_Combine)" />
<Content Remove="#(CSS_Combine)" />
</ItemGroup>
<!-- DEBUG message -->
<Message Text="DEBUG: #(Content)" Importance="high" />
</Target>
The debug message still shows #(Content) as having the unnecessary js files. Can anyone tell me what's happening?
In order to recreate you situation I created this sample script
<Project DefaultTargets="Demo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Demo">
<ItemGroup>
<JS_Combine Include="js\c01.min.js;js\c02.min.js;js\c03.min.js;" />
<CSS_Combine Include="css\c01.min.css;css\c02.min.css;css\c03.min.css;" />
</ItemGroup>
<ItemGroup>
<Content Include="#(JS_Combine);#(CSS_Combine)"/>
</ItemGroup>
<Message Text="Content Before: #(Content)" Importance="high" />
<!-- Tidy up -->
<ItemGroup>
<Content Include="js\combined.min.js" />
<Content Include="css\combined.min.css" />
<Content Remove="#(JS_Combine)" />
<Content Remove="#(CSS_Combine)" />
</ItemGroup>
<Message Text="-------------------------"/>
<Message Text="Content After: #(Content)" Importance="high" />
</Target>
</Project>
It works for me here is the results:
Project "C:\Data\Development\My Code\Community\MSBuild\RemoveTest\Remove01.proj" on node
1 (default targets).
Demo:
Content Before: js\c01.min.js;js\c02.min.js;js\c03.min.js;css\c01.min.css;css\c02.min.c
ss;css\c03.min.css
-------------------------
Content After: js\combined.min.js;css\combined.min.css
Done Building Project "C:\Data\Development\My Code\Community\MSBuild\RemoveTest\Remove01.
proj" (default targets).
Are you still having issues with this?

How to add content from a target in a csproj file?

I would like to add content to my web application depending on the configuration. I have declared the target in the initials target and the target looks like this :
<Target Name="ApplicationNameDefinition" Outputs="$(MashupName)">
<MSBuild.ExtensionPack.Framework.TextString TaskAction="StartsWith" String1="$(ConfigurationName)" String2="Config1">
<Output TaskParameter="Result" PropertyName="isConfig1" />
</MSBuild.ExtensionPack.Framework.TextString>
<MSBuild.ExtensionPack.Framework.TextString TaskAction="StartsWith" String1="$(ConfigurationName)" String2="Config2">
<Output TaskParameter="Result" PropertyName="isConfig2" />
</MSBuild.ExtensionPack.Framework.TextString>
<MSBuild.ExtensionPack.Framework.TextString TaskAction="StartsWith" String1="$(ConfigurationName)" String2="Config3">
<Output TaskParameter="Result" PropertyName="isConfig3" />
</MSBuild.ExtensionPack.Framework.TextString>
<MSBuild.ExtensionPack.Framework.TextString TaskAction="StartsWith" String1="$(ConfigurationName)" String2="Config4">
<Output TaskParameter="Result" PropertyName="isConfig4" />
</MSBuild.ExtensionPack.Framework.TextString>
<Error Condition=" !$(isConfig1) And !$(isConfig2) And !$(isConfig3) And !$(isConfig4) " Text="Configuration $(ConfigurationName) Inconnue" />
<PropertyGroup>
<MashupName Condition="$(isConfig1)">App1</MashupName>
<MashupName Condition="$(isConfig2)">App2</MashupName>
<MashupName Condition="$(isConfig3)">App3</MashupName>
<MashupName Condition="$(isConfig4)">App4</MashupName>
</PropertyGroup>
<Error Condition="'$(MashupName)'==''" Text="Configuration $(MashupName) Inconnue" />
<ItemGroup >
<Content Condition=" '$(MashupName)'!='' " Include="App_Themes\$(MashupName)\**" />
</ItemGroup>
However, the content is not added to the project. Any Idea ?
Thanks.
Actually this works in msbuild, but not in Visual Studio which has some cache with csproj files...