MSBuild property being set to a value never defined (caching?) - visual-studio

I have a .NET solution file that contains many projects, most of which are using the newer .NET SDK style .csproj format, but there is a project or two targeting .Net Framework 4.X and uses the older .csproj format.
I am extending the build process and have created 2 files in the solution root Directory.Build.props and Directory.Build.targets.
Problem: I have a particular property that is being set despite me belieiving that it shouldn't be set given the Conditions I have specified on my PropertyGroup.
To simplify the troubleshoot, I assigned a dummy starting value in my Directory.Build.props and then assigned a totally separate value in my Directory.Build.targets to see if my target ever runs.
Directory.Build.props
<Project>
<PropertyGroup>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<MyProperty>Not set</MyProperty> <!-- property in question -->
</PropertyGroup>
</Project>
Directory.Build.targets
<Project>
<PropertyGroup Condition="
'$(tf_build)' != 'true' AND
'$(IsInnerBuild)' != 'true' AND
'$(Configuration)' == 'Debug' AND
'$(UsingMicrosoftNETSdk)' == 'true' AND
'$(BypassPublishLocalNugetPackages)' != 'true'">
<MyProperty>Set</MyProperty> <!-- property in question -->
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<!-- Redacted addl misc. properties ... -->
</PropertyGroup>
<Target Name="BeforeBuild">
<Message Importance="high" Text="----------- BEFORE BUILD ------------"></Message>
<Message Importance="high" Text="MyProperty: $(MyProperty)"></Message>
<Message Importance="high" Text="GeneratePackageOnBuild = $(GeneratePackageOnBuild), UsingMicrosoftNETSdk = $(UsingMicrosoftNETSdk)"></Message>
<Message Importance="high" Text="----------- BEFORE BUILD ------------"></Message>
</Target>
<Target Name="AfterBuild">
<Message Importance="high" Text="----------- AFTER BUILD ------------"></Message>
<Message Importance="high" Text="MyProperty: $(MyProperty)"></Message>
<Message Importance="high" Text="GeneratePackageOnBuild = $(GeneratePackageOnBuild), UsingMicrosoftNETSdk = $(UsingMicrosoftNETSdk)"></Message>
<Message Importance="high" Text="----------- AFTER BUILD ------------"></Message>
</Target>
</Project>
To my surprise, the results of the compilation from within Visual Studio are inconsistent. I can compile a handful of times and the value of <MyProperty> will show Not set, however, after additional compilations and doing something as simple as modifying the .targets file by adding a space or new line and recompiling, the results change.
Initial compilation results
Rebuild started...
1>------ Rebuild All started: Project: MyProject, Configuration: Debug Any CPU ------
1> ----------- BEFORE BUILD ------------
1> MyProperty: Not set
1> GeneratePackageOnBuild = false, UsingMicrosoftNETSdk = "
1> ----------- BEFORE BUILD ------------
1> MyProject -> C:\MyProject\MyProject.dll
1> ----------- AFTER BUILD ------------
1> MyProperty: Not set
1> GeneratePackageOnBuild = false, UsingMicrosoftNETSdk = "
1> ----------- AFTER BUILD ------------
Random subsequent results
Note: Once this result below have been attained, the results do not seem to change at that point during additional compilations.
Also Note: The value displayed for <MyProperty> below is NOT the value set in my .targets file. That said, the condition in the .targets file is also not met anyway, so I'm not sure that matters.
Rebuild started...
1>------ Rebuild All started: Project: MyProject, Configuration: Debug Any CPU ------
1> ----------- BEFORE BUILD ------------
1> MyProperty: true
1> GeneratePackageOnBuild = false, UsingMicrosoftNETSdk = "
1> ----------- BEFORE BUILD ------------
1> MyProject -> C:\MyProject\MyProject.dll
1> ----------- AFTER BUILD ------------
1> MyProperty: true
1> GeneratePackageOnBuild = false, UsingMicrosoftNETSdk = "
1> ----------- AFTER BUILD ------------
I've done a fair amount of playing with these files to troubleshoot. Is it possible that the value is cached somewhere? Perhaps using a value that I had set at some point in the past?
I used SysInternals' ProcMon to look to see what files are being accessed to as a clue, but nothing stood out. Restarting Visual Studio does not help.
Any ideas why I'd have some ghost value appearing in my property?

Related

How to prevent Visual Studio from changing the solution file in the scenario of using a utility project?

This question is related to How to code a utility msbuild project so that it depends on a "real" C# project?
So I have a working sample code where there is a utility project depending on a "real" C# project. It works fine when building on the console. Here are the files:
C:\work\a [master]> tree /F
Folder PATH listing for volume OSDisk
Volume serial number is F6C4-7BEF
C:.
│ .gitignore
│ run.ps1
│ Subject.sln
│ Utility.targets
│
├───Subject
│ Subject.csproj
│
└───Utility.Subject
Utility.Subject.proj
C:\work\a [master]>
Subject\Subject.csproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D2CF0D00-8B1B-4FCC-AF4B-5A354044F786}</ProjectGuid>
<OutputType>Library</OutputType>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>Bin\</OutputPath>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
Utility.Subject\Utility.Subject.proj
<Project ToolsVersion="Current">
<PropertyGroup>
<MasterProject>..\Subject\Subject.csproj</MasterProject>
<MasterAsmName>Subject</MasterAsmName>
</PropertyGroup>
<Import Project="..\Utility.targets" />
</Project>
Utility.targets
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>bin</OutputPath>
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="$(MasterProject)" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.Common.CurrentVersion.targets" />
<Target Name="Build" DependsOnTargets="ResolveProjectReferences">
<PropertyGroup>
<DllPath>$(MasterProject)\..\bin\$(MasterAsmName).dll</DllPath>
</PropertyGroup>
<Message Text="*** Good" Importance="High" Condition="Exists('$(DllPath)')"/>
<Message Text="*** Bad" Importance="High" Condition="!Exists('$(DllPath)')"/>
</Target>
</Project>
Subject.sln
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30413.136
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Subject", "Subject\Subject.csproj", "{D2CF0D00-8B1B-4FCC-AF4B-5A354044F786}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Utility.Subject", "Utility.Subject\Utility.Subject.proj", "{EA5BE444-40F0-4972-A03E-5B794FAECF21}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{D2CF0D00-8B1B-4FCC-AF4B-5A354044F786}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D2CF0D00-8B1B-4FCC-AF4B-5A354044F786}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D2CF0D00-8B1B-4FCC-AF4B-5A354044F786}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D2CF0D00-8B1B-4FCC-AF4B-5A354044F786}.Release|Any CPU.Build.0 = Release|Any CPU
{EA5BE444-40F0-4972-A03E-5B794FAECF21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA5BE444-40F0-4972-A03E-5B794FAECF21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA5BE444-40F0-4972-A03E-5B794FAECF21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA5BE444-40F0-4972-A03E-5B794FAECF21}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4DD676B7-ECF7-413E-9FC6-D4ED7EC73889}
EndGlobalSection
EndGlobal
And it works fine on the command line:
C:\work\a [master]> MSBuild.exe /v:m /nologo
Subject -> C:\work\a\Subject\Bin\Subject.dll
*** Good
C:\work\a [master]>
The problem is Visual Studio. When opening the solution in VS the latter modifies the solution file by changing the target platform of the utility project from Any CPU to x86. This causes VS to skip the utility project altogether. We can actually see it when using devenv on the command line. Please, observe:
No modified files initially:
C:\work\a [master]> git st
## master
C:\work\a [master]>
Run devenv on the command line:
C:\work\a [master]> devenv .\Subject.sln /build debug
Microsoft Visual Studio 2019 Version 16.11.3.
Copyright (C) Microsoft Corp. All rights reserved.
Build started...
1>------ Build started: Project: Subject, Configuration: Debug Any CPU ------
1> Subject -> C:\work\a\Subject\Bin\Subject.dll
2>------ Skipped Build: Project: Utility.Subject, Configuration: Debug x86 ------
2>Project not selected to build for this solution configuration
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 1 skipped ==========
C:\work\a [master +0 ~1 -0 !]>
Notice in particular:
2>------ Skipped Build: Project: Utility.Subject, Configuration: Debug x86 ------
2>Project not selected to build for this solution configuration
That means VS skips the utility project. But it also modifies the solution file so as to skip it in the future:
C:\work\a [master +0 ~1 -0 !]> git st
## master
M Subject.sln
C:\work\a [master +0 ~1 -0 !]>
Visual Studio replaces the lines
{EA5BE444-40F0-4972-A03E-5B794FAECF21}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EA5BE444-40F0-4972-A03E-5B794FAECF21}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EA5BE444-40F0-4972-A03E-5B794FAECF21}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EA5BE444-40F0-4972-A03E-5B794FAECF21}.Release|Any CPU.Build.0 = Release|Any CPU
with
{EA5BE444-40F0-4972-A03E-5B794FAECF21}.Debug|Any CPU.ActiveCfg = Debug|x86
{EA5BE444-40F0-4972-A03E-5B794FAECF21}.Debug|x86.ActiveCfg = Debug|x86
{EA5BE444-40F0-4972-A03E-5B794FAECF21}.Debug|x86.Build.0 = Debug|x86
How can I prevent it?
I found a solution. 2 files must be changed:
Subject/Subject.csproj
This change is necessary because this file is a contrived example of a non SDK style project. Visual Studio is in pain because it does not find something that serves like a marker for it, namely PropertyGroup elements conditional on Configuration and Platform. So, here is the modified content of that project:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{D2CF0D00-8B1B-4FCC-AF4B-5A354044F786}</ProjectGuid>
<OutputType>Library</OutputType>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>Bin\</OutputPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>
Notice the new lines near the bottom of the file:
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
It turns out, our utility project must have these as well, otherwise VS botches its configuration in the solution file, so here we go:
Utility.targets
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<OutputPath>bin</OutputPath>
</PropertyGroup>
<!-- These two are expected by Visual Studio. Not needed when building with msbuild on the console. -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
<ItemGroup>
<ProjectReference Include="$(MasterProject)" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.Common.CurrentVersion.targets" />
<!-- This target is expected by Visual Studio. Not needed when building with msbuild on the console. -->
<Target Name="CreateManifestResourceNames" />
<Target Name="Build" DependsOnTargets="ResolveProjectReferences">
<PropertyGroup>
<DllPath>$(MasterProject)\..\bin\$(MasterAsmName).dll</DllPath>
</PropertyGroup>
<Message Text="*** Good" Importance="High" Condition="Exists('$(DllPath)')"/>
<Message Text="*** Bad" Importance="High" Condition="!Exists('$(DllPath)')"/>
</Target>
</Project>
Two changes are needed here. First, the same markers as in the previous file:
<!-- These two are expected by Visual Studio. Not needed when building with msbuild on the console. -->
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'" />
In the previous file these markers were less critical, but in here they are what makes all the difference.
And the second change:
<!-- This target is expected by Visual Studio. Not needed when building with msbuild on the console. -->
<Target Name="CreateManifestResourceNames" />
With these two changes it all works. However, there is also a weird catch with the solution file. One must make sure not to mess with the indentation in that file. It must be tabs, not spaces! I have noticed a weird thing. At the end of this file I have these lines:
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4DD676B7-ECF7-413E-9FC6-D4ED7EC73889}
EndGlobalSection
EndGlobal
If the SolutionGuid is not indented by exactly 2 tabs, then VS insists on adding a new SolutionGuid as if not seeing the already existing one. So, beware.
There is another nuisance - devenv command line hangs after having finished. I do not really care, since I use it just to demo the change in behavior. Anyway, here is the result after modifying the files:
C:\work\a [master]> devenv .\Subject.sln /build debug
Microsoft Visual Studio 2019 Version 16.11.3.
Copyright (C) Microsoft Corp. All rights reserved.
Build started...
1>------ Build started: Project: Subject, Configuration: Debug Any CPU ------
1> Subject -> C:\work\a\Subject\Bin\Subject.dll
2>------ Build started: Project: Utility.Subject, Configuration: Debug Any CPU ------
2> *** Good
========== Build: 2 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
C:\work\a [master]>

Visual Studio project with a custom build step only (no default build)

I want to create a Visual Studio project that would allow me to see a bunch of JavaScript and other files and edit them as normal, but would also have a build step that can run any custom commands I want (currently some npm commands, possibly more later). Basically I want 3 features combined:
Be able to browse and edit files just like for any VS project (C#, C++, etc.)
Be able to run a custom build step by selecting "Build" in Visual Studio (including building the whole solution).
Be able to run that same custom build step from the command line (MSBuild).
Using a "shared project" (.shproj) allows me to easily see and edit the files, but there is no Build item in the context menu, even if I manually add a Build target:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Label="Globals">
<ProjectGuid>...</ProjectGuid>
</PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.Default.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.Common.props" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\CodeSharing\Microsoft.CodeSharing.CSharp.targets" />
<Import Project="MyItems.projitems" Label="Shared" />
<PropertyGroup>
<Configuration>Debug</Configuration>
<Platform>Any CPU</Platform>
</PropertyGroup>
<Target Name="Build">
<Exec Command="ECHO My custom build!" />
</Target>
</Project>
I've also tried using a stripped-down VC++ project (since I don't actually want to run the C++ compiler) and this allows a build to be run from VS, but opening the project logs warnings like error MSB4057: The target "GetProjectDirectories" does not exist in the project. and trying to add files to fails with that error or similar ones.
There must be an easier way to do this!
From your current description, I think you want to create a js project in VS IDE.
However, VS IDE has the node js project template by default. And you should install the workload Node.js development under VS_Installer so that you can use it.
After that, you can create such project.
1) Adding js files or other files by right-click on the project-->Add-->Existing Item so that you can modify the files on VS IDE.
2) If you want to execute a custom build step that does not break the whole build, you should make the custom target depends on the default build.
You can use this:
<Target Name="CustomStep" AfterTargets="Build">
<Exec Command="ECHO My custom build!" />
</Target>
or
<Target Name="CustomStep" BeforeTargets="Build">
<Exec Command="ECHO My custom build!" />
</Target>
Note: If you use
<Target Name="Build">
<Exec Command="ECHO My custom build!" />
</Target>
It will overwrite the system build process and instead, run the command, which breaks the whole default build.
3) If you want to execute the custom build on msbuild command, you should specify the name of the custom target:
msbuild xxx\xxx.proj -t: CustomStep(the name of the custom target)
===============================================
Besides, if you still want to use C++ project template, you could create a empty c++ project which does not contain any clcompile files and then do the same steps.
If you do not want to use C++ compiler, you should only remove any xml node on the vcxproj file like these:
<ClCompile Include="xxx.cpp" />
<ClInclude Include="xxx.h" />
When you use the empty C++ project, you do not have to worry about that.
=========================================
Update 1
If you want to build this project on a build sever without VS IDE, I suggest you could install Build Tool for VS2019 which is an independent, lightweight build command line(It is equivalent to dotnet cli).
Build Tool for VS2019
Under All Downloads-->Tools for Visual Studio 2019--> Build Tools for Visual Studio 2019
Then, you have to install the related build workload such as Node.js Build tools and then we can use the command line to build node.js project on build sever.
The entire installation process is fast.
Inspired by Perry Qian-MSFT's answer, I managed to strip down a Node.js project to the bare minimum that I needed to get Visual Studio to load and build it, but without referencing any external files.
The main trick was VS needs a target named "CoreCompile" to be defined to show the Build menu item! (It also needs a "Build" target, but that one is more obvious.)
My project now looks like this:
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>(some guid)</ProjectGuid>
<ProjectHome>.</ProjectHome>
<ProjectTypeGuids>{3AF33F2E-1136-4D97-BBB7-1795711AC8B8};{9092AA53-FB77-4645-B42D-1CCCA6BD08BD}</ProjectTypeGuids>
</PropertyGroup>
<!-- These property groups can be empty, but need to be defined for VS -->
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
</PropertyGroup>
<Import Project="My.Build.targets" />
<!-- Define empty standard MSBuild targets, since this project doesn't have them. Doing it this way allows My.Build.targets to also be used in a project that does define them. -->
<Target Name="Build" />
<Target Name="ReBuild" />
<Target Name="Clean" />
<!-- NOTE: a target named "CoreCompile" is needed for VS to display the Build menu item. -->
<Target Name="CoreCompile" />
<!-- Files shown in Visual Studio - adding and removing these in the UI works as expected -->
<ItemGroup>
<Content Include="myfile..." />
</ItemGroup>
</Project>
And My.Build.targets looks like this:
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="MyBuild" AfterTargets="Build">(build steps)</Target>
<Target Name="MyReBuild" AfterTargets="ReBuild">(re-build steps)</Target>
<Target Name="MyClean" AfterTargets="Clean">(clean steps)</Target>
<!-- This target is needed just to suppress "warning NU1503: Skipping restore for project '...'. The project file may be invalid or missing targets
required for restore." -->
<Target Name="_IsProjectRestoreSupported" Returns="#(_ValidProjectsForRestore)">
<ItemGroup>
<_ValidProjectsForRestore Include="$(MSBuildProjectFullPath)" />
</ItemGroup>
</Target>
</Project>

Force Visual Studio to rebuild on Start Debugging

This is very different compared to How to tell visual studio to rebuild every time I make a change?
The issue is that I have modified a csproj that is not referenced a project (because it's run-time dependency).
So for example, in my csproj file I have:
<Import Project=".\UnreferencedProjects-Developer.targets" />
In my .targets file, I have:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="BuildDependencyForDevelopers" AfterTargets="Build">
<Message Text="========================================" />
<Message Text="Developer Building Unreferenced Projects" />
<Message Text="========================================" />
<!--MSBuild Projects="$(ProjectToBuild)"-->
<MSBuild Projects="../OtherProject/OtherProject.csproj">
<Output ItemName="ProjectOutputs" TaskParameter="TargetOutputs"/>
</MSBuild>
<Message Text="#(ProjectOutputs)"/>
<Message Text="=======================================" />
<Message Text="Developer Copying Unreferenced Projects" />
<Message Text="=======================================" />
<Copy SourceFiles="$(ProjectDir)\..\OtherProject\bin\$(Configuration)\OtherProject.dll" DestinationFolder="$(OutDir)" ContinueOnError="true"/>
<Copy SourceFiles="$(ProjectDir)\..\OtherProject\bin\$(Configuration)\OtherProject.pdb" DestinationFolder="$(OutDir)" ContinueOnError="true"/>
<Message Text="=============================================" />
<Message Text="Developer Finished with Unreferenced Projects" />
<Message Text="=============================================" />
</Target>
</Project>
The issue is that the .targets. file seems to only execute on a manual build/rebuild, and not on a Start Debugging.
All Configurations are set to Build. Options -> Project and Solutions -> Build and Run -> On Run, when projects are out of date: Always build.
I think the issue is that because the project is unreferenced, when I Start Debugging there are no out of date projects so it launches without a build/rebuild. How do I force it to literally always (re)build.
Add to project:
<PropertyGroup><DisableFastUpToDateCheck>true</DisableFastUpToDateCheck></PropertyGroup>
Keyword here is "fast update check": Visual Studio uses it when you debug to avoid even starting up msbuild.
Related topic: MSBuild Target that always runs when clicking build in VS2013
Background info: https://msdn.microsoft.com/library/ms171468%28v=vs.110%29.aspx
If you start debugging in the in Visual Studio UI (either by choosing the F5 key or by choosing Debug, Start Debugging on the menu bar), the build process uses a fast update check to improve performance. In some cases where customized builds create files that get built in turn, the fast update check does not correctly identify the changed files. Projects that need more thorough update checks can turn off the fast checking by setting the environment variable DISABLEFASTUPTODATECHECK=1. Alternatively, projects can set this as an MSBuild property in the project or in a file the project imports.

Before and AfterBuild Target in Visual Studio not firing

I am doing the following:
I have created a default class file project
Edited the csproj file to include Pre and Post BuildEvents
Uncomment the default commented out BeforeBuild and AfterBuild targets
The BeforeBuild and AfterBuild targets are not called form within Visual Studio but are from msbuild command line, why is that?
I would rather use msbuild targets rather than the PostBuildEvent as if gives me more power and flexibility, assuming it works.
Cheers,
adam
I shortened some of the paths in the output, so if they are inconsistent that is why
ClassLibrary1.csproj changes
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="BeforeBuild">
<Message Text="### BeforeBuild ###" />
</Target>
<Target Name="AfterBuild">
<Message Text="### AfterBuild ###" />
</Target>
<PropertyGroup>
<PreBuildEvent>echo PRE_BUILD</PreBuildEvent>
</PropertyGroup>
<PropertyGroup>
<PostBuildEvent>echo POST_BUILD</PostBuildEvent>
</PropertyGroup>
my build output from VS 2010 is
------ Rebuild All started: Project: ClassLibrary1, Configuration: Debug Any CPU ------
PRE_BUILD
ClassLibrary1 -> c:\ClassLibrary1\bin\Debug\ClassLibrary1.dll
POST_BUILD
========== Rebuild All: 1 succeeded, 0 failed, 0 skipped ==========
and from the command line
#>msbuild ClassLibrary1.sln
Microsoft (R) Build Engine Version 4.0.30319.1
[Microsoft .NET Framework, Version 4.0.30319.239]
Copyright (C) Microsoft Corporation 2007. All rights reserved.
Build started 09/05/2012 13:27:42.
Project "c:.sln" on node 1 (default targets).
ValidateSolutionConfiguration:
Building solution configuration "Debug|Any CPU".
Project "c:.sln" (1) is building "c:\ClassLibrary1.csproj" (2) on node 1 (default targets).
BeforeBuild:
### BeforeBuild ###
PreBuildEvent:
echo PRE_BUILD
PRE_BUILD
GenerateTargetFrameworkMonikerAttribute:
Skipping target "GenerateTargetFrameworkMonikerAttribute" because all output files are up-to-date with respect to the input files.
CoreCompile:
Skipping target "CoreCompile" because all output files are up-to-date with respect to the input files.
CopyFilesToOutputDirectory:
ClassLibrary1 -> c:\bin\Debug\ClassLibrary1.dll
PostBuildEvent:
echo POST_BUILD
POST_BUILD
AfterBuild:
### AfterBuild ###
Done Building Project "c:\ClassLibrary1.csproj" (default targets).
Done Building Project "c:.sln" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.18
Your build events are firing, you're just not seeing them in Visual Studio.
By default VS sets the msbuild verbosity to minimal. You can get your message to show by changing the message importance to high
<Target Name="BeforeBuild">
<Message Text="### BeforeBuild ###" Importance="high" />
</Target>
<Target Name="AfterBuild">
<Message Text="### AfterBuild ###" Importance="high" />
</Target>
You can also change the verbosity setting in VS under Tools->Options then under Projects and Solutions->Build and Run.
Just for others help, when they encounter similar issue but the reason could be different.
If you have import after the target then also AfterBuild may not work.
Make sure all the import you have should be before Target Definition, Target Definition should be at the end

MSBuild pre clean customization

I am working with Visual Studio 2010. I have directed project output to a specific folder which will contain all the DLLs and EXEs when built. However when I clean the solution, the folder is not getting cleaned, and the DLLs are still present in it.
Can anyone tell me how to handle the clean solution command to clear out the folders I want to clean? I tried working with MSBuild and handling the BeforeClean and AfterClean targets, but it did not provide the desired result.
The answer from Sergio should work but I think it could be cleaner to override the BeforeClean/AfterClean targets. These are hooks into the build/clean process provided by microsoft. When you do a clean, VS do call the targets : BeforeClean;Clean;AfterClean and by default the first and the last do nothing.
In one of your existing .csproj file you can add the following :
<Target Name="BeforeClean">
<!-- DO YOUR STUFF HERE -->
</Target>
You can add to your VS .sln file special target named let's say BuildCustomAction.csproj:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
</PropertyGroup>
<ItemGroup>
<CleanOutCatalogFiles Include="..\..\bin\$(Configuration)\**\*.dll">
<Visible>false</Visible>
</CleanOutCatalogFiles>
<CleanOutCatalogFiles Include="..\..\bin\$(Configuration)\**\*.exe">
<Visible>false</Visible>
</CleanOutCatalogFiles>
</ItemGroup>
<Target Name="Build">
</Target>
<Target Name="Rebuild"
DependsOnTargets="Clean;Build">
</Target>
<Target Name="Clean"
Condition="'#(CleanOutCatalogFiles)'!=''">
<Message Text="Cleaning Output Dlls and EXEs" Importance="high" />
<Delete Files="#(CleanOutCatalogFiles)" />
</Target>
</Project>
Place it everywhere you want and specify relative path to the output catalog for your binaries. Add in VS this project as existing. That's all. With this you can do own custom actions for three common actions in VS: Build, Rebuild, Clean.
There exists more complex way to customize build process using CustomBeforeMicrosoftCommonTargets and CustomAfterMicrosoftCommonTargets but it requires to be very good in MSBuild.
Hope this helps.

Resources