I have a .target file, which imports to my project:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="CreateInstaller">
<Import Project="$(PathToTargetFile)\My.target" />
<Project>
The .target is build by another target:
<Target Name="MyCustomTarget" DependsOnTargets="$(OtherTargets)">
<MSBuild Projects="$(PathToTargetFile)\My.target" />
</Target>
The target MyCustomTarget is called several time, each time the content of My.target would be changed. But My.target seems that not imported anymore rather than the first time. How to resolve this problem?
How to resolve this problem?
Visual Studio caches all imported .target files. That is the reason why you My.target seems that not imported anymore rather than the first time.
To resolve this issue, you can use a temp .target file with different name, this will force VS to reload .target file:
<Target Name="AfterBuild">
<PropertyGroup>
<TempProjectFile>My.$([System.Guid]::NewGuid()).target</TempProjectFile>
</PropertyGroup>
<Copy SourceFiles="My.target" DestinationFiles="$(TempProjectFile)" />
<MSBuild Projects="$(TempProjectFile)" />
<Delete Files="#(TempProjectFile)" />
</Target>
You can refer to the similar issue for detail info.
Hope this help you.
Related
I am looking for a way to run my defined Target only once per build process and not for every project that gets build.
I have defined the following in Directory.Build.props
<Target Name="MyTarget" AfterTargets="AfterBuild" >
<Message Text="Hello World!!!" Importance="High" />
</Target>
Should only run once no matter how many projects the msbuild process is building, currently it happens for each project.
It shouldn't matter if I hit (Re-)Build Soltution or (Re-)Build [ProjectName] or hit F5 in Visual Studio, as long any build happens I want to exectue MyTarget only once.
Just answer this situation:
If my guess is right, pure msbuild function is not enough and you have to use a external file to help it work.
create a file called test.txt on the solution folder and write 0 in the txt file.
Then, modify your Directory.Build.props like this:
<Project>
<PropertyGroup>
<Record></Record>
</PropertyGroup>
<ItemGroup>
<File Include="..\test.txt"></File>
</ItemGroup>
<Target Name="GetConditionValue" BeforeTargets="PrepareForBuild">
<ReadLinesFromFile File="#(File)">
<Output TaskParameter="Lines" PropertyName="Record"/>
</ReadLinesFromFile>
</Target>
<Target Name="MyTarget" AfterTargets="Build" Condition="'$(Record)'=='0'">
<WriteLinesToFile File="#(File)" Lines="2" Overwrite="true"></WriteLinesToFile>
<Message Text="Hello World!!!" Importance="High" />
</Target>
</Project>
When you start a new build process, you should clear the test.txt file to 0 to make a new start.
I wrote a C# template for creating of the .Net extensions for AutoCAD. Before, for each AutoCAD version it is was necessary to point the individual referenses set, the output directory, the target .Net Framework Platform, etc. Exist many versions of AutoCAD: AutoCAD 2009, 2010, ..., 2015. Now my template do it instead of me. My csproj-file has the CAD_Year property:
<PropertyGroup>
<CAD_Year>2013</CAD_Year>
<Min_Year>2009</Min_Year>
<Max_Year>2015</Max_Year>
</PropertyGroup>
When I change CAD_Year value (manually edit this option in the csproj-file) - all settings of my project do change too according target AutoCAD version. It works fine.
But I need to compile my code for all versions of AutoCAD always... It is inconvenient to change the CAD_Year every time for this... :(((
How can I create the cycle of compiling my project for the versions Min_Year, ..., Max_Year when I press the Rebuild Solution menu item?
Thank you, #stijn. I will mark your answer as a solution. Here I create an "answer" for the code highlighting. My current code works:
<!-- Redefine the CoreClean target, otherwise MSBuild will remove all results
of building except for the last. -->
<Target Name="CoreClean">
<ItemGroup>
<AllFiles Include="$(OutputPath)\*.*" />
</ItemGroup>
<Copy SourceFiles="#(AllFiles)" DestinationFolder="$(OutputPath)\temp" />
</Target>
<Target Name="BatchRebuild">
<ItemGroup>
<CADYearsItem Include="$(BuildFor)" />
</ItemGroup>
<Msbuild Projects="$(MsBuildThisFile)" Targets="Rebuild" Properties="CAD_Year_Platform=%(CADYearsItem.Identity)" />
<ItemGroup>
<AllFilesBack Include="$(OutputPath)\temp\*.*" />
</ItemGroup>
<Move SourceFiles="#(AllFilesBack)" DestinationFolder="$(OutputPath)" />
<!-- Doesn't work for Debug. The $(OutputPath)\temp\ will not removed.
But it work for Release.-->
<RemoveDir Directories="$(OutputPath)\temp\" />
</Target>
I see, the RemoveDir task doesn't work for the Debug for me, but it is not a big problem. Now my template is complete, and I will do refactoring of this. Thank you very much!
If you add this to your project file:
<ItemGroup>
<CADYears Include="2013;2014;2015"/>
</ItemGroup>
<Target Name="BatchRebuild">
<Msbuild Projects="$(MsBuildThisFile)" Targets="Rebuild" Properties="CAD_Year=%(CADYears.Identity)"/>
</Target>
and call
msbuild <path_to_projectfile> /t:BatchRebuild
on the commandline, it will build path_to_projectfile 3 times each with a different CAD_Year property.
To get this invoked by VS is trickier since you need to override the Rebuild target, but this for instance works for VS2013 (Actualrebuild target was copied from the Rebuild target in C:\Program Files (x86)\MSBuild\12.0\Bin\Microsoft.Common.CurrentVersion.targets):
<ItemGroup>
<CADYears Include="2013;2014;2015"/>
</ItemGroup>
<Target Name="ActualRebuild"
Condition=" '$(_InvalidConfigurationWarning)' != 'true' "
DependsOnTargets="$(RebuildDependsOn)"
Returns="$(TargetPath)"/>
<Target Name="BatchRebuild">
<Msbuild Projects="$(MsBuildThisFile)" Targets="ActualRebuild" Properties="CAD_Year=%(CADYears.Identity)"/>
</Target>
<Target Name="Rebuild">
<Msbuild Projects="$(MsBuildThisFile)" Targets="BatchRebuild"/>
</Target>
Edit
Since the template system in VS tries to copies ItemGroups it finds in the project root (which seems like a bug to me, or at the least a very annoying feature) you can work around that by using a property and converting it into an item when needed:
<PropertyGroup>
<CADYears>2013;2014;2015<CADYears/>
</PropertyGroup>
<Target Name="BatchRebuild">
<ItemGroup>
<CADYearsItem Include="$(CADYears)"/>
</ItemGroup>
<Msbuild Projects="$(MsBuildThisFile)" Targets="Rebuild" Properties="CAD_Year=%(CADYearsItem .Identity)"/>
</Target>
Note: in the project you posted in the link you are invoking the Rebuild target in the Afterbuild target. I didn't try it, but that will almost certainly lead to infinite recursion. So you should stick to a solution like posted above with a seperate target.
I have a solution with multiple projects.
However, this solution to compile correctly, I need to compile a sequence of projects that are in the solution folder.
As I have many projects, in each folder, need to dynamically obtain the relationship of project files, eg. Csproj, to compile.
I've tried creating tasks, creating xml editing csproj file, but I get no result.
<PropertyGroup>
<SrcFolder>$(MSBuildProjectDirectory)\..\..</SrcFolder>
</PropertyGroup>
<ItemGroup>
<PluginProjectsFiles Include="$(SrcFolder)\Folder\SubFolder.*\*.csproj" />
</ItemGroup>
<Target Name="BuildPlugins">
<Message Text="Building Plugins" />
<MSBuild Projects="#(PluginProjectsFiles)" Targets="Clean;Build" Properties="Configuration=Release" />
<Message Text="Plugins Built" />
</Target>
I am using YUICompressor.Net for minification. The .proj file executes from MSBuild and works fine.
The question is how do I attach the MSBuild action to the build of the main Project?
I know there are some "After Build" events, bud how do I point them to execute my additional MSBuild.
In case it's relevant this is how my MSBuild file looks like:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/MsBuild/2003">
<UsingTask TaskName="CssCompressorTask" AssemblyFile="..\bin\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
<UsingTask TaskName="JavaScriptCompressorTask" AssemblyFile="..\bin\Yahoo.Yui.Compressor.Build.MsBuild.dll" />
<Target Name="Minify">
<ItemGroup>
<CssFile_Common Include="../Styles/common.css"/>
<CssFile_Plugins_All Include="../Styles/plugins.all.css"/>
</ItemGroup>
<CssCompressorTask
SourceFiles="#(CssFile_Common)"
DeleteSourceFiles="false"
OutputFile="../Styles/common.min.css"
CompressionType="Standard"
LoggingType="Info"
PreserveComments="false"
LineBreakPosition="-1"
/>
<CssCompressorTask
SourceFiles="#(CssFile_Plugins_All)"
DeleteSourceFiles="false"
OutputFile="../Styles/plugins.all.min.css"
CompressionType="Standard"
LoggingType="Info"
PreserveComments="false"
LineBreakPosition="-1"
/>
</Target>
</Project>
Assuming your sample file is named Minify.proj you would simply need to put something like this at the bottom of your main project file:
<Import Project="Minify.proj" />
<Target Name="BeforeBuild" DependsOnTargets="Minify">
</Target>
I have next config in my Web.config file
<Target Name="UpdateWebConfigForProjectsBeforeRun">
<ItemGroup>
<FilesToTransofm Include="ProjectsDeployBin\Web.*.$(Configuration).config"/>
</ItemGroup>
<Message Text="Transform file: %(FilesToTransofm.Identity)" />
<TransformXml Source="web.config"
Transform="%(FilesToTransofm.Identity)"
Destination="web.config" />
</Target>
What i am trying to do its get all configs from ProjectsDeployBin directory and apply each file to main web.config.
After first transformation main web.config locked by msbuild.
So how can i fix this issue? Is there any other way to transform my web.config by collection of files?
Thanks.
As you've noticed, the TransformXml task shipped with Visual Studio 2010 has a bug that leaves the source file locked.
To work around that, you can make a temporary copy of your source file before each transformation.
Since you'll then be executing multiple tasks for each transform file (copy and transform), you'll need to switch to Target Batching instead of Task Batching.
Example:
<ItemGroup>
<FilesToTransofm Include="ProjectsDeployBin\Web.*.$(Configuration).config"/>
</ItemGroup>
<Target Name="UpdateWebConfigForProjectsBeforeRun"
Inputs="#(FilesToTransofm)"
Outputs="%(Identity).AlwaysRun">
<Message Text="Transform file: %(FilesToTransofm.Identity)" />
<Copy SourceFiles="web.config"
DestinationFiles="web.pre-%(FilesToTransofm.Filename).temp.config" />
<TransformXml Source="web.pre-%(FilesToTransofm.Filename).temp.config"
Transform="%(FilesToTransofm.Identity)"
Destination="web.config" />
</Target>
From a quick test, it looks like this bug is fixed in Visual Studio 2012, but I'm not able to find a reference / source that documents that, and the original Connect bug isn't viewable anymore.