Force Visual Studio to rebuild on Start Debugging - visual-studio

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.

Related

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>

How can I create a cycle of compiling my project with incrementing of some option?

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.

MSBuild projects with different build-configs, without using sln

Related
I have two projects in my VS solution, BookApp.Web and BookApp.Domain.
BookApp.Web references BookApp.Domain.
BookApp.Web has the following build configurations: debug, staging, prod-eu, prod-us and prod-as. We have three datacenters for production and a staging environment.
BookApp.Domain so far only has two build configurations, debug.
When building the solution from within Visual Studio, I can use the build configurator to make sure that no matter what build config is selected for the Web project, the debug config is always used for the Domain project.
However, when building with MSBuild on my continuous integration server, things go wrong. I use this in my rollout.msbuild file:
<MSBuild Projects="BookApp.Web\BookApp.Web.csproj" Properties="Configuration=Prod-us" />
When I run this, MSBuild expects all dependent projects to have the same build configuration. Since that's not the case (and shouldn't be IMO), it fails with this error message:
The OutputPath property is not set for project 'BookApp.Domain.csproj'. Please check to make sure that you have specified a valid combination of Configuration and Platform for this project. Configuration='Prod-us' Platform='AnyCPU'.
An answer to a related question suggests creating separate .sln solutions for each build configuration and running that with MSBuild. To me that doesn't sound like a good idea.
Copying all the build configurations to the Domain project is also not ideal.
Is there a better way of telling MSBuild to use different build configs?
Have a look at this answer it explains how the configurations are passed from project to Project through the MSBuild Task and Using the MetaData of the configuration to pass the desired configuration for the target project
here
UPDATE
I created a Solution with a class library(Sample.Domain) and ConsoleApplication(SampleApp.Console). I added two more configurations to the SamplApp.Console: prod-us;prod-eu, Sample.Domain remained with debug;release.
I then Changed the csproj file of the ConsoleApplication, like so:
ProjectReferences
<!--<ItemGroup>
<ProjectReference Include="..\Sample.Domain\Sample.Domain.csproj">
<Project>{73e8a7fd-0a24-47c5-a527-7601550d4b92}</Project>
<Name>Sample.Domain</Name>
</ProjectReference>
</ItemGroup>-->
<ItemGroup>
<ProjectReference Include="..\Sample.Domain\Sample.Domain.csproj" >
<Targets>Build</Targets>
</ProjectReference>
</ItemGroup>
Added a switch case on the configuration passed to MSBuild, to configure some properties for Outputfiles and reference files:
<Choose>
<When Condition="'$(Configuration)' != 'Debug'">
<PropertyGroup>
<OutputProperty>$(OutputPath)\$(Configuration)</OutputProperty>
<FileCopy>$(OutputProperty)</FileCopy>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<OutputProperty>$(OutputPath)</OutputProperty>
<FileCopy>$(OutputProperty)</FileCopy>
</PropertyGroup>
</Otherwise>
</Choose>
Created a Target to switch the Configuration passed to MSBuild, so that Debug will pass Debug to Sample.Domain, everything else it will pass Release
<Target Name="MultiConfiguration" >
<CreateProperty Value="Debug">
<Output TaskParameter="Value" PropertyName="LibConfiguration" Condition="'$(Configuration)' == 'Debug'"/>
</CreateProperty>
<CreateProperty Value="Release">
<Output TaskParameter="Value" PropertyName="LibConfiguration" Condition="'$(Configuration)' != 'Debug' "/>
</CreateProperty>
</Target>
The Build Target uses the Properties we have added so the Output and Copy of references files will have the right values according to Configuration value
<!--Build Process-->
<Target Name="Build" DependsOnTargets="Clean;MultiConfiguration;ComputeProjectReference" >
<Csc Sources="#(Compile)" References="#(NewAssemblies)" TargetType="exe" OutputAssembly="$(OutputProperty)\$(AssemblyName).exe"/>
</Target>
<Target Name="ComputeProjectReference" Inputs="#(ProjectReference)" Outputs="%(ProjectReference.Identity)__Forced">
<MSBuild Projects="#(ProjectReference)" Targets="%(ProjectReference.Targets)" Properties="Configuration=$(LibConfiguration);Platform=AnyCPU;OutputPath=bin\$(LibConfiguration)">
<Output TaskParameter="TargetOutputs" ItemName="ResolvedProjectReferences"/>
</MSBuild>
</Target>
<Target Name="AfterProjectReference" AfterTargets="ComputeProjectReference">
<CreateItem Include="#(ResolvedProjectReferences)">
<Output TaskParameter="Include" ItemName="CopyFiles" />
</CreateItem>
<Copy SourceFiles="#(CopyFiles)" DestinationFolder="$(FileCopy)" SkipUnchangedFiles="false" />
<ItemGroup>
<NewAssemblies Include="$(OutputProperty)\%(CopyFiles.FileName)%(CopyFiles.Extension)" />
</ItemGroup>
</Target>
To call the Debug configuration is done like this
msbuild SampleApp.Console.csproj
To call (Release;prod-us;prod-eu;...) is done like this
msbuild SampleApp.Console.csproj /p:Configuration="prod-us" /p:OutputPath="bin"
I'm sure it can be optimized, and might be some way easier, but it works.

msbuild transform one config few times

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.

Visual Studio. Publish project from command line

Is there a way to publish a web project in MS Visual Studio 2010 using CLI? I use DevEnv.exe /Build to build a project and it works fine, but I could not find option to Publish a project.
One other thing I want to mention. I am trying to publish web project NOT to the IIS directly. I have a location where I publish several projects and then build them automatically into NSIS bundle to be deployed.
From ASP.NET Web Deployment using Visual Studio: Command Line Deployment, you can use
msbuild myproject.csproj /p:DeployOnBuild=true /p:PublishProfile=MyPublishProfile
where MyPublishProfile is the profile name that you've already set up somewhere
What works best is to add following target to the project file:
<Target Name="AfterBuild">
<Message Text="Copying to Deployment Dir:" />
<Copy SourceFiles="#(Content)" DestinationFolder="..\XXX\%(Content.RelativeDir)" />
<CreateItem Include="$(OutputPath)\*">
<Output TaskParameter="Include" ItemName="Binaries"/>
</CreateItem>
<Copy SourceFiles="#(Binaries)" DestinationFolder="..\XXX\bin" />
</Target>
This way, whenever project got build (from command line or from IDE) it automatically get deployed to specified folder. Thank you everybody for pointing me to right direction.
The /t:publish switch is for ClickOnce applications only, it's not applicable to web projects. Hence the error saying it's unpublishable. :)
#RobKent As https://stackoverflow.com/a/2775437/21233364
using
<Target Name="AfterBuild" Condition=" '$(Configuration)' == 'Release' ">
<Message Text="Copying to Deployment Dir:" />
<Copy SourceFiles="#(Content)" DestinationFolder="..\XXX\%(Content.RelativeDir)" />
<CreateItem Include="$(OutputPath)\*">
<Output TaskParameter="Include" ItemName="Binaries"/>
</CreateItem>
<Copy SourceFiles="#(Binaries)" DestinationFolder="..\XXX\bin" />
</Target>
you can have publish only on release compiling.

Resources