MSBuild pre clean customization - visual-studio-2010

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.

Related

Custom MSBuild project that can be "run" from Visual Studio

I created a custom MSBuild project that I can "build" from Visual Studio 2019, as described in https://stackoverflow.com/a/64917535/1536933. That is, I can select the Build menu item in VS and it runs my custom MSBuild task. Is there a way to do the same with "Run" - get VS to run my custom MSBuild task when I "Start" (or "Start without debugging") that project? I worked out that for the Build menu item to appear VS needs to see MSBuild targets named "Build" and "CoreCompile" - there is probably some equivalent for Start, but what?
The custom project file 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>
Note that it's not a C# project or C++ project or any other common type of project - VS treats its as a Node.js project due to the ProjectTypeGuids, but if I can get it working some other way, I'd be happy to.

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>

MSBuild conditions based on imported properties

I have a solution that has multiple projects, and I'd like them all to be able to access a set of shared properties that have been defined once in a common file at the solution level.
This mostly works fine using the code below and I can use the imported properties in the BeforeBuild target, however the problem I'm having is that I can't use the imported properties in conditions.
So I have the following in a CommonSettings.targets file in the solution folder:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CommonSettingsTarget">
<PropertyGroup>
<MyCustomProperty>Sample</MyCustomProperty>
</PropertyGroup>
</Target>
</Project>
In my project file I have:
<Project ToolsVersion="14.0" DefaultTargets="Build" InitialTargets="CommonSettingsTarget" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(SolutionDir)CommonSettings.targets" />
<ItemGroup>
<EmbeddedResource Include="EmbeddedDocument.txt" Condition=" '$(MyCustomProperty)' == 'Sample' " />
</ItemGroup>
<Target Name="BeforeBuild">
<Message Text="MyCustomProperty='$(MyCustomProperty)'" Importance="high" />
</Target>
</Project>
In the above, I can see my imported property displayed in the output window as "MyCustomProperty='Sample'" which is great, however when it's used as part of a condition (to optionally include an embedded resource), the condition is never satisfied.
Is there any way to make the imported properties work with conditions?
As your ItemGroup is not within a target, but the PropertyGroup is, the CommonSettingsTarget has not yet been executed when your condition is evaluated and thus MyCustomProperty has not yet been defined.
The Message task is called from within the BeforeBuild target which depends on CommonSettingsTarget and thus MyCustomProperty has been defined when you create the message.
Think of the Import as copying the imported project into your project file. The result would be something like this:
<Project ToolsVersion="14.0" DefaultTargets="Build" InitialTargets="CommonSettingsTarget" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="CommonSettingsTarget">
<PropertyGroup>
<MyCustomProperty>Sample</MyCustomProperty>
</PropertyGroup>
</Target>
<ItemGroup>
<EmbeddedResource Include="EmbeddedDocument.txt" Condition=" '$(MyCustomProperty)' == 'Sample' " />
</ItemGroup>
<Target Name="BeforeBuild">
<Message Text="MyCustomProperty='$(MyCustomProperty)'" Importance="high" />
</Target>
</Project>
This is what happens:
You define a target CommonSettingsTarget which will define MyCustomProperty when it is executed. Not now.
You define the ItemGroup and therefore evaluate the condition. It returns false, because MyCustomProperty has not yet been defined.
You define a target BeforeBuild.
You run the initial target, i.e. CommonSettingsTarget. Now MyCustomProperty is defined.
You run the default target which depends on BeforeBuild and thus runs BeforeBuild. There you evaluate MyCustomProperty which has been defined in step 4.
As a solution, remove the CommonSettingsTarget target and define the PropertyGroup as a child of the Project in CommonSettings.targets instead:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MyCustomProperty>Sample</MyCustomProperty>
</PropertyGroup>
</Project>
In your project file, you should remove the InitialTargets="CommonSettingsTarget" then.

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.

Automatic tracking of build number in VS 2005?

In Visual Studio 2005, is there an easy way to automatically increment the assembly/file build numbers after a successful build?
Emphasis on easy. I would like to track my build version, without having to set up CruiseControl or some similar tool.
You can use this project and include it your .proj file
This url might be of use Updating Porj build number
This didn't fit my needs and I took to adding this as a build.proj which works a treat
<?xml version="1.0" encoding="utf-8"?> <Project DefaultTargets="Build"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/> <PropertyGroup>
<Major>1</Major>
<Minor>0</Minor>
<Build>0</Build>
<Revision>0</Revision> </PropertyGroup> <PropertyGroup>
<BuildDir>C:\svn\Infrastructure</BuildDir> </PropertyGroup>
<ItemGroup>
<SolutionsToBuild Include="Infrastructure.sln"/> </ItemGroup>
<Target Name="Build" DependsOnTargets="ChangeDataAccessAssemblyInfo">
<RemoveDir Directories="$(BuildDir)\Builds" Condition="Exists('$(BuildDir)\Builds')" />
<MSBuild Projects="#(SolutionsToBuild)" Properties="Configuration=Debug" Targets="Rebuild" /> </Target>
<ItemGroup>
<TestAssemblies Include="Build\Logging\Logging.UnitTests.dll" /> </ItemGroup>
<Target Name="ChangeDataAccessAssemblyInfo" >
<Message Text="Writing ChangeDataAccessAssemblyInfo file for 1"/>
<Message Text="Will update $(BuildDir)\DataAccess\My Project\AssemblyInfo.vb" />
<AssemblyInfo CodeLanguage="VB"
OutputFile="$(BuildDir)\DataAccess\My Project\AssemblyInfo_new.vb"
AssemblyTitle="Data Access Layer"
AssemblyDescription="Message1"
AssemblyCompany="http://somewebiste"
AssemblyProduct="the project"
AssemblyCopyright="Copyright notice"
ComVisible="true"
CLSCompliant="true"
Guid="hjhjhkoi-9898989"
AssemblyVersion="$(Major).$(Minor).1.1"
AssemblyFileVersion="$(Major).$(Minor).5.7"
Condition="$(Revision) != '0' "
ContinueOnError="false" />
<Message Text="Updated Assembly File Info"
ContinueOnError="false"/> </Target> </Project>
The Publish options might be what you want... (def. available for C#, not sure abuut C++).
In studio, right click on the project file, and go to Properties, then select the "Publish" tab. There is an option there for auto-incrementing revision number.
What about writing a little macro, which increments the version?
Or what about this VS AddIn ?

Resources