How to turn off caching of build definitions in Visual studio - visual-studio

In project file I import my own target file
<Import Project="Build\CopyDependencies.target" />
and later I call target from that target file
<CallTarget Targets="CopyDependencies" UseResultsCache="false" />
If I edit CopyDependencies.target file I have to reload whole solution and only then changes to CopyDependencies.target take effect. I believe it is some sort of build definitions caching in Visual Studio? If it is, maybe it can be turned off?

Thanks #KazR
Here is a smaller Solution that you can insert into your .csproj file
<Target Name="AfterBuild">
<PropertyGroup>
<TempProjectFile>Build.$([System.Guid]::NewGuid()).proj</TempProjectFile>
</PropertyGroup>
<Copy SourceFiles="Build.proj" DestinationFiles="$(TempProjectFile)" />
<MSBuild Projects="$(TempProjectFile)" />
<ItemGroup>
<TempProjectFiles Include="Build.????????-????-????-????-????????????.proj"/>
</ItemGroup>
<Delete Files="#(TempProjectFiles)" />
</Target>
Problem solved

I don't know how you would disable the VS cache, however I may have a workaround that would allow you to edit the build target without having to reload the solution.
You could use the MSBuild task in your proj file to call a wrapper target that copies your CopyDependencies.target file to CopyDependencies.[RandomNumber].target, then invokes your CopyDependencies target in the newly created file, and finally deletes it.
This would force VS to reload the target on each invocation as the filename is different.
Here's an example:
myProject.proj
Add this to the AfterBuild target:
<MSBuild Projects="Wrapper.target" Targets="MyWrappedTarget" UnloadProjectsOnCompletion="true"/>
Wrapper.target
Here we have the target that will - at build time - copy the real target file and invoke the desired build target within it (I've used an inline c# task which is only available in MSBuild 4.0):
<UsingTask TaskName="RandomNumber" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<Number ParameterType="System.Int32" Output="true"/>
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<!-- CDATA -->
Random rndGenerator = new Random();
Number = rndGenerator.Next(Int32.MaxValue);
<!-- CDATA -->
</Code>
</Task>
</UsingTask>
<Target Name="MyWrappedTarget">
<Message Text="MyWrappedTarget target called"/>
<RandomNumber>
<Output TaskParameter="Number" PropertyName="FileNumber"/>
</RandomNumber>
<PropertyGroup>
<CopiedTarget>inner.test.$(FileNumber).target</CopiedTarget>
</PropertyGroup>
<Copy SourceFiles="inner.test.target" DestinationFiles="$(CopiedTarget)"/>
<MSBuild Projects="$(CopiedTarget)" Targets="_innerTestTarget"/>
<Delete Files="$(CopiedTarget)"/>
</Target>
inner.test.target
This contains the real build target you want to execute, in this example it's a simple file copy.
<Target Name="_innerTestTarget">
<Message Text="This is a inner test text message"/>
<Copy SourceFiles="x.txt" DestinationFiles="x1.txt"/>
</Target>
This isn't production ready, but hopefully illustrates my point.
With this (slightly convoluted) process in place, you can change the inner.test.target file without having to reload the solution in VS.

Here's a solution that doesn't require any MSBuild scripting at all.
I noticed that unloading and reloading a project doesn't get around the cache, but closing and reopening the solution does. In addition, Visual Studio will prompt you to reload the solution if it notices the .sln file has changed. And finally, this superuser question explains how to touch a file in Windows.
Putting these together, I added a Visual Studio external tool to touch the current solution file. Here's how:
Select TOOLS > External Tools ...
Click the Add button to add a new tool.
Set properties as follows:
Title: Reload Solution
Command: cmd.exe
Arguments: /c copy "$(SolutionFileName)"+>nul
Initial directory: $(SolutionDir)
and turn on Use Output window
Click OK to close the External Tools window
Now if you have made changes to your MSBuild files, just select TOOLS > Reload Solution and all your build files will be reloaded.
I'm using Windows 7 64-bit and Visual Studio 2012 Express for Windows Desktop.

I have a different solution, not involving temporary files:
Include.targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foobar">
<Copy SourceFiles="test.source" DestinationFiles="testFoobar.dest" />
</Target>
</Project>
Project file:
....
<Target Name="BeforeBuild">
<Exec Command="$(MSBuildToolsPath)\MSBuild.exe Include.targets /t:Foobar" ContinueOnError="false" />
</Target>
....
in this case VS does not recognize the MSBuild command, and does not cache the file.
happy coding!

Before running MSBuild I run this to clear the download cache:
call "%VS120COMNTOOLS%vsvars32.bat"
echo Clear download cache
gacutil -cdl

Related

How to copy and rename file to output folder as part of build

I believe this is more of an msbuild-related question.
Have a .net core app and I need to conditionally publish a file and based on the build config selected in Visual Studio 2019, the file should be renamed before publishing to the target.
So Im looking at modifying the csproj file (which is nothing but an msbuild file itself)
I dont see a condition option on the copy task
https://learn.microsoft.com/en-us/visualstudio/msbuild/copy-task?view=vs-2019
The goal Im after, is if I have 3 different files
tester-notes.dev.json
tester-notes.debug.json
tester-notes.prod.json
If prod is selected as a build config, I want the file published to be tester-notes.prod.json, but renamed to tester-notes.json
Assuming you have three files(build action = None) in Solution Explorer when developing:
You can use something similar to this script to rename and copy to publish folder if you're using FileSystem publish mode:
<ItemGroup Condition="$(Configuration)=='Dev'">
<FileToRename Include="$(ProjectDir)\tester-notes.dev.json" />
</ItemGroup>
<ItemGroup Condition="$(Configuration)=='Debug'">
<FileToRename Include="$(ProjectDir)\tester-notes.debug.json" />
</ItemGroup>
<ItemGroup Condition="$(Configuration)=='Prof'">
<FileToRename Include="$(ProjectDir)\tester-notes.prof.json" />
</ItemGroup>
<Target Name="DoSthAfterPublish1" AfterTargets="Publish" Condition="$(Configuration)=='Dev'">
<Copy SourceFiles="#(FileToRename)" DestinationFiles="#(FileToRename->Replace('.dev.json','.json'))"/>
<Move SourceFiles="$(ProjectDir)\tester-notes.json" DestinationFolder="$(PublishDir)" OverwriteReadOnlyFiles="true"/>
</Target>
<Target Name="DoSthAfterPublish2" AfterTargets="Publish" Condition="$(Configuration)=='Debug'">
<Copy SourceFiles="#(FileToRename)" DestinationFiles="#(FileToRename->Replace('.debug.json','.json'))"/>
<Move SourceFiles="$(ProjectDir)\tester-notes.json" DestinationFolder="$(PublishDir)" OverwriteReadOnlyFiles="true"/>
</Target>
<Target Name="DoSthAfterPublish3" AfterTargets="Publish" Condition="$(Configuration)=='Prof'">
<Copy SourceFiles="#(FileToRename)" DestinationFiles="#(FileToRename->Replace('.prof.json','.json'))"/>
<Move SourceFiles="$(ProjectDir)\tester-notes.json" DestinationFolder="$(PublishDir)" OverwriteReadOnlyFiles="true"/>
</Target>
And if you can reset tester-notes.debug.json to tester-notes.Debug.json,, then we may combine the three targets into one by using DestinationFiles="#(FileToRename->Replace('.$(Configuration).json','.json'))". Hope it makes some help :)
In addition:
According to the Intellisense we can find the Copy task supports Condition:

Visual Studio Express 2012 for Web - don't generate bin and obj folders

Can I turn off generating these folders on build? They contain some .dll, .pdb and other files I don't need. I'm just using Typescript compilation.
Yes, it is possible. I relied on the following nice overview on how to hijack a build process:
http://sedodream.com/2013/06/01/HijackingTheVisualStudioBuildProcess.aspx
Three main tricks:
1) project_name.csproj is the one you need to modify, it is XML MSBUILD file.
2) You cannot remove dependency from WebApplication.targets, you need to "deactivate" them as below.
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v11.0\WebApplications\Microsoft.WebApplication.targets" Condition="false" />
3) You have to implement general contract for clean/build/rebuild targets.
Mine looks like:
<Target Name="Build">
<Exec Command="call node_modules\.bin\tsc.cmd src/main.ts --out js/game.min.js > $(Temp)\tscout.txt" IgnoreExitCode="true" WorkingDirectory="$(SolutionDir)" />
<Exec Command="type $(Temp)\tscout.txt" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build">
</Target>
<Target Name="Clean">
<RemoveDir Directories="js">
</RemoveDir>
</Target>
You could create a New WebSite instead of New Project. That one should not create any binary output.

How do I use an MSBuild file from Visual Studio 2012?

I have a simple MSBuild file that I'm learning with.
Here it is:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Clean" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>{D5A16164-962E-4A6D-9382-240F31AB6C50}</ProjectGuid>
</PropertyGroup>
<Target Name="Clean">
<ItemGroup>
<BinFiles Include="bin\*.*" />
<fff Include="f\*.*" />
</ItemGroup>
<Delete Files="#(BinFiles)" />
<Delete Files="#(fff)" />
</Target>
</Project>
Now I want to include this in a Visual Studio solution and be able to run the "clean" target from Visual Studio 2012. I tried naming it testproject.msbuildproj like the internet seems to suggest "works", but it doesn't work. When I run the clean command I just get "unexpected error".
If I rename the project to testproject.csproj, it does some unintuitive things like creating compilation directories, but it does actually run my clean command properly. However, this is undesireable because it creates obj and bin/x86/debug type directories. It also looks goofy in Visual Studio because it still gives the References drop down.
How can I use just a plain vanilla MSBuild project from Visual Studio without random errors or false assumptions?
Note I only am having a problem with this from Visual Studio. Using msbuild from the command line it works perfectly
Visual Studio creates bin / obj folders when it opens csproj file. When you click Build / Rebuild / Clean it just uses appropriate targets from the project file.
You cannot stop VS from creating these folders, but you can ask it to create them in say temp folder by setting appropriate properties - refer this MSDN article for details.
So the steps are to rename your project to csproj, and add the following lines into project:
<PropertyGroup>
<OutputPath>$(Temp)\bin</OutputPath>
<IntermediateOutputPath>$(Temp)\obj</IntermediateOutputPath>
</PropertyGroup>
I usually use a bit different approach to work with MSBUILD files from VS:
I use regular csproj file with removed Import ... CSharp.targets part as pure container for my Build projects.
I add actual build files with targets and logic, and all properties, necessary artifacts like XSLT etc using "Include into project", so I can manage hierarchy and change any file from within VS.Net.
I redefine Build / Rebuild targets in csproj file for whatever I need, for example Build may contain minimum output, and while rebuild diagnostic one.
Like this:
<Target Name="Build">
<Exec Command="%windir%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe Builds\build.proj /t:Build /v:m" />
</Target>
<Target Name="Rebuild">
<Exec Command="%windir%\Microsoft.NET\Framework64\v4.0.30319\msbuild.exe Builds\build.proj /t:Build /v:d" />
</Target>

Visual Studio Project - MSBuild Target - AfterBuild - Condition - Only When Binary File Updated

I have a long afterbuild process on my Visual Studio project file's after build target, as show below.
The issue is that it always runs the AfterBuild target when I hit build even when the actual source code has not changed and the project is not compiled.
How can I have this only run when the project has been compiled and the physical binary is written or update on the disk?
<Target Name="AfterBuild">
<Exec Command=""$(ProgramFiles)\Microsoft\ILMerge\ILMerge.exe" /copyattrs /log /target:library /targetplatform:4,C:\Windows\Microsoft.NET\Framework64\v4.0.30319 /Lib:"$(TargetDir)\" /keyfile:"$(ProjectDir)\Plugin.snk" /out:"$(TargetDir)\$(AssemblyName).merged.dll" "$(AssemblyName).dll" "PluginCommandCommon.dll" "Common.dll"" />
<Copy SourceFiles="$(TargetDir)\$(AssemblyName).merged.dll" DestinationFolder="$(ProjectDir)..\PluginPackage\bin\$(Configuration)\" />
</Target>
Option 1:
Instead of AfterBuild use AfterRebuild (one of MSBuild's many undocumented features):
<Target Name="AfterRebuild" >...</Target>
Option 2:
Hook up one of the incremental build's conditions:
<Target Name="AfterBuild" Condition=" '#(_SourceItemsToCopyToOutputDirectory)' != '' " >
UPDATE:
Using MSBuild Extension Pack's ILMerge task will allow better control, I.E check for each file existence:
<Target Name="ILMergeItems">
<ItemGroup>
<Input Include="C:\b\MSBuild.ExtensionPack.dll"/>
<Input Include="C:\b\Ionic.Zip.dll"/>
</ItemGroup>
<MSBuild.ExtensionPack.Framework.ILMerge
Condition="Exists('%(Input.FullPath)')"
InputAssemblies="#(Input)"
OutputFile="C:\a\MyNewAssembly.dll"/>
</Target>
There is a ComboBox in Properties>>Build Events>>Run the post-build event...if this is what you mean.

MSBuild "Debug" configuration not working in VS 2010 Beta 2

I'm trying to set up my environment for developing, debugging and deploying Windows Desktop Gadgets. I've hit a bit of a roadblock in my project, where I can't run a build on my gadget when the configuration is set to "Debug". If the configuration is set to "Release", the build goes through the following custom tasks:
Copy gadget contents to a seperate folder.
Minify/obfuscate the javascript files, removing comments and whitespace.
Package the files into a CAB file.
Sign the CAB file with a digital certificate.
This runs just fine, my "Debug" configuration has the following tasks defined
Copy gadget folder to AppData\Local\Microsoft\Windows Sidebar\Gadgets\.
Start the gadget using the IDesktopGadget interface.
If I copy those two tasks to the "Release" configuration, they run just fine - no problems whatsoever. I've tried creating a seperate configuration called "Test", copied from the "Release" configuration.
If I try to build any configuration other than "Release", I get an instant message saying "Build succeeded" but no tasks have run at all.
EDIT: I've started a bounty because I still have the same problem with VS 2010 RC and it's very frustrating.
FURTHER EDIT:
Thanks to John I was able to debug the build process. It led me to realize that the <Target> element with condition for debugging was being completely ignored (not even processed). When I swapped the position of my <Target> elements, it worked:
<Target Name="Build" Condition="'$(Configuration)' == 'Release'">
<!--
<Obfuscate PathToJasob="C:\Program Files (x86)\Jasob.com\Jasob 3.5" Path="$(GadgetFolder)" Output="$(GadgetName)_obf" log="jasob_log.txt" />
-->
<BuildGadget BuildFormat="CAB" Path="$(GadgetFolder)" Target="$(GadgetName).gadget" />
<SignGadget CertName="Cert1" TimestampURL="http://timestamp.comodoca.com/authenticode" Target="$(GadgetName).gadget" />
</Target>
<Target Name="Build" Condition="'$(Configuration)' == 'Debug'">
<CopyToGadgets GadgetFolder="$(GadgetFolder)" GadgetName="$(GadgetName)" />
<RunGadget GadgetName="$(GadgetName)" />
</Target>
So it looks like the second <Target Name="Build"> element overrides the first, despite the Condition attribute being present. What can I do?
As Joe suggests:
Change your output path like this, and see if that fixes the issue:
<OutputPath>bin\Debug\</OutputPath>
Update
Have you tried running msbuild /verbosity:diagnostic ?
Can you try that and show the output?
Second Update
Make one target 'build', and then make two tasks in that target:
<Target Name="Build">
<CallTarget Targets="BuildRelease" Condition="'$(Configuration)' == 'Release'" />
<CallTarget Targets="BuildDebug" Condition="'$(Configuration)' == 'Debug'" />
</Target>
<Target Name="BuildRelease">
<!--
<Obfuscate PathToJasob="C:\Program Files (x86)\Jasob.com\Jasob 3.5" Path="$(GadgetFolder)" Output="$(GadgetName)_obf" log="jasob_log.txt" />
-->
<BuildGadget BuildFormat="CAB" Path="$(GadgetFolder)" Target="$(GadgetName).gadget" />
<SignGadget CertName="Cert1" TimestampURL="http://timestamp.comodoca.com/authenticode" Target="$(GadgetName).gadget" />
</Target>
<Target Name="BuildDebug">
<CopyToGadgets GadgetFolder="$(GadgetFolder)" GadgetName="$(GadgetName)" />
<RunGadget GadgetName="$(GadgetName)" />
</Target>
Just a guess:
Your Debug build has its output path set to bin\Release\.
The timestamps of the files in bin\Release\ are probably causing MSBuild to conclude that the debug build is already up to date. Try changing the the output path to bin\Debug\ for debug builds.

Resources