Changing EXE name based on compilation constant - vb.net

I have a project in VB.NET 2010 (compiling to x86, .NET 2.0 runtime) that I want to compile into two separate EXEs - a "lite" version and a "full" version.
Unfortunately I cannot make two separate projects as it uses the Adobe Reader COM control - and sharing a form using that control between two projects seems to confuse the IDE (something to do with COM Interop, I assume - if someone knows how to share a form hosting the adobe reader control, that would solve my problem too).
I have found this thread:
Change name of exe depending on conditional compilation symbol however I don't have any MSBuild experience so I need more explicit instructions.
On the "My Project>Compile" tab there is a "Build Events..." button. I was wondering if anyone knows how to set a conditional compilation constant and use that to determine the EXE name (or change it after build).
If all else fails I can rename the EXE manually I suppose, but I'd prefer it to be automated.

If having two separate projects within your solution isn't acceptable, you'll want to look into creating your own MSBuild script.
Here is an example of a custom MSBuild script that will allow you to define your custom compilation constants at build time, and then build your two versions of your application ("Full" and "Lite"):
<Project DefaultTargets="BuildAll" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildVersionFull>FULL</BuildVersionFull>
<BuildVersionLite>LITE</BuildVersionLite>
</PropertyGroup>
<ItemGroup>
<Projects Include="$(MSBuildProjectDirectory)\MyApp.vbproj" />
</ItemGroup>
<Target Name="BuildAll" DependsOnTargets="BuildFull;BuildLite" />
<Target Name="BuildFull">
<MSBuild Projects="#(Projects)" Properties="DefineConstants=$(BuildVersionFull);OutputPath=binFull\;BaseIntermediateOutputPath=objFull\;AssemblyName=MyApp_Full" />
</Target>
<Target Name="BuildLite">
<MSBuild Projects="#(Projects)" Properties="DefineConstants=$(BuildVersionLite);OutputPath=binLite\;BaseIntermediateOutputPath=objLite\;AssemblyName=MyApp_Lite" />
</Target>
</Project>
What you'll need to do:
Create a new file and save it the same directory as your application's project file as "MyBuild.xml".
Change to reflect the name of your application's project file.
Open the Visual Studio Command Prompt and run "msbuild ".
Within your project, you can use the "FULL" and "LITE" conditional compilation constants to determine whether to compile certain statements:
#If FULL Then
' Compile code for the "Full" version.
#End If

Related

How do you disable Roslyn Analyzers when using msbuild through the command line?

The Roslyn Analyzers are installed as nuget packages, which are dependencies of the FxCop Analyzers (also installed as nuget packages).
I have enabled full solution analysis as instructed here: How to Enable and disable full solution analysis for managed code.
I have a fairly large solution with most of the projects using the FxCop/Roslyn Analyzers and Visual Studio builds fine, usually in under a minute.
However, when running msbuild through the command line using:
"C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/MSBuild/15.0/Bin/MSBuild.exe" "C:\Source\MySolution\MySmartClient.sln" /p:Configuration=Develop;Platform="Any CPU" /
t:Build
Building the solution takes anywhere from 4-15 minutes. The same is true on the build server which uses the same command.
I've tried /p:RunCodeAnalysis=False and that has no effect. I've also used process monitor to emulate the msbuild command that VS sends to msbuild with no change.
And, according to this doc: How to: Enable and disable automatic code analysis for managed code
The Enable Code Analysis on Build check box only affects static code analysis. It doesn't affect Roslyn code analyzers, which always execute at build if you installed them as a NuGet package.
These excessive build times are not practical. Is there any way to disable when using msbuild through the command line?
It's not really supported, but there is a workaround:
Create a Directory.Build.targets (msbuild >= v15.0), After.{SolutionName}.sln.targets (msbuild < 15.0) file in your solution root folder and add:
<Project>
<Target Name="DisableAnalyzers"
BeforeTargets="CoreCompile"
Condition="'$(UseRoslynAnalyzers)' == 'false'">
<!--
Disable analyzers via an MSBuild property settable on the command line.
-->
<ItemGroup>
<Analyzer Remove="#(Analyzer)" />
</ItemGroup>
</Target>
</Project>
You can pass in /p:UseRoslynAnalyzers=false now to remove all analyzers configured in the project.
See also:
https://github.com/dotnet/roslyn/issues/23591#issuecomment-507802134
https://learn.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019#directorybuildprops-and-directorybuildtargets
You can edit the condition to also trigger on RunCodeAnalysis=False or Never.
<Target Name="DisableAnalyzers"
BeforeTargets="CoreCompile"
Condition="
'$(UseRoslynAnalyzers)' == 'false'
or '$(RunCodeAnalysis)' == 'false'
or '$(RunCodeAnalysis)' == 'never'" >
To disable a specific analyzer, use this trick:
We just spent 2 hours figuring out how to disable an analyzer based on an MSBuild property, AMA.
https://twitter.com/Nick_Craver/status/1173996405276467202?s=09
The documentation has changed since the original answers. There is now this page documenting how to disable code analysis from analyzers:
There are 3 MSBuild properties you can use to control analyzer behavior (all default to true):
RunAnalyzersDuringBuild Controls whether analyzers run at build time.
RunAnalyzersDuringLiveAnalysis Controls whether analyzers analyze code live at design time.
RunAnalyzers Disables analyzers at both build and design time. This property takes precedence over RunAnalyzersDuringBuild and RunAnalyzersDuringLiveAnalysis.
Edit: it looks like there is an issue being tracked where these props don't work unless your project has Microsoft.CodeAnalysis.targets included. So your mileage may vary until this is fixed.
In case anyone else happens to find themselves here, I came across this issue on the dotnet/roslyn project on Github:
Feature: MSBuild switch for turning on/off analysis #23591
The preceding issue describes a work-around:
Substitute for old MSBuild properties? #1431
<PropertyGroup>
<RunCodeAnalysis Condition="'$(RunCodeAnalysis)' == ''">true</RunCodeAnalysis>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="<whatever analyzers package you are depending on>" Condition="'$(RunCodeAnalysis)' == 'true'" />
</ItemGroup>
# You'll need to run a restore when changing this value
msbuild /p:RunCodeAnalysis=false
Although, I had a couple of differences though since I'm not using package references. This worked for me.
<ItemGroup>
<Analyzer Include="<whatever analyzers package you are depending on>" Condition="'$(RunCodeAnalysis)' == 'true'" />
</ItemGroup>
<!-- I added the condition to the EnsureNugetPackageBuildImports too. -->
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="'$(RunCodeAnalysis)' == 'true' AND !Exists('<relative path to the prop of whatever analyzers you are depending on>')" Text="$([System.String]::Format('$(ErrorText)', '<relative path to the prop of whatever analyzers you are depending on>'))" />
</Target>

How make MSBuild build custom target specified in csproj building sln?

I am facing an issue with MSBuild I can't overcome it by myself. As a result I rely on community's wisdom.
The real situation I'm having troubles with
I have a soluiton file containing several projects with dependencies to other projects in same solution. I'd like to append a custom target to one of the project's csproj file and build it from the command line. It will allow me to make all the necessary output binaries for this project for further processing during the building of the custom target. But the main thing is that I can't figure out how to do it, googling doesn't help either.
Simplification
To make thing simplier I decided to make a new C# console project, add a simple custom target to the project's file and try to make it build. Still no success! Here what I've done so far:
Created a solution app with a default console project coreapp. This gaves me at least two files:
app.sln
coreapp\coreapp.csproj
Modified coreapp.csproj with addition of my custom target inside of the Project tag
<Target Name="SampleTarget">
<Message Text="This is a SampleTarget" />
</Target>
Run on the command line the following command
%windir%\Microsoft.NET\framework\v3.5\msbuild.exe app.sln /t:coreapp:SampleTarget
or even
%windir%\Microsoft.NET\framework\v3.5\msbuild.exe app.sln /t:coreapp.csproj:SampleTarget
Results
No luck, facing the error
MSB4057: The target "coreapp.csproj:SampleTarget" does not exist in the project.
I suspect that MSBuild thinks somehting fundamentally different from what I want it to think...
BEsides that, I also tried to set on the same command line the environment variable MSBuildEmitSolution=1 to force msbuild dump a temporary solution file it creates while processing the solution. In this file, indeed, no such target. However I guess it isn't the reason because I asked msbuild to build coreapp.proj where target SampleTarget really resides.
The question is how to build SampleTarget in this simplified scenario using solution file since potencially it can contain dependencies for the project containing this SampleTarget target?
I'd be greatful for any sort of help or firection for further investigation!
Instead of inserting a custom target in your project file, you could try creating a new standalone msbuild file, which would:
build the solution file (which builds projects)
defines your extra target
Call it app-custom-Debug.msbuild , for example.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WorkingFolder>$(MSBuildProjectDirectory)</WorkingFolder>
<Configuration>Debug</Configuration>
<SolutionFile>app.sln</SolutionFile>
</PropertyGroup>
<Target Name="Build" DependsOnTargets="Compile" />
<Target Name="Compile">
<Message Text="=== COMPILING $(Configuration) configuration ===" />
<MSBuild Projects="$(SolutionFile)"
Properties="Configuration=$(Configuration)" />
</Target>
<Target Name="SampleTarget">
<Message Text="This is a SampleTarget" />
</Target>
</Project>
Then you execute:
msbuild.exe app-custom-Debug.msbuild /t:SampleTarget
One option is to tie your SampleTarget to the standard Build targets via overriding the appropriate DependsOn property. In this case you could tell BeforeBuild that it DependsOn SampleTarget or you do the same thing with AfterBuild. This will ensure that MSBuild processes your target prior to the standard target indicated.

Preventing MSBuild from building a project in a .sln without using Solution Configurations

I want to inhibit the building of certain projects within a solution from building (within a TeamCity Build Configuration in order to optimize the speed of my Commit Build feedback if you must know).
I'm aware of the Solution Configurations mechanism but don't want to have to force lots of .sln files to end up with every permutation of things I want to be able to switch off. I have Convention based rule where I want to say "If I'm doing the Commit Build, I dont want to do the final installer packaging". (And I don't want to break it out into a separate solution).
I'd prefer not to use a solution involving find and replace in the .sln file or in a .proj file created via [MsBuildEmitSolution][1]. I'm aware of questions here which cover the out of the box solution and this slightly related question.
I see MSBuild /v:diag is saying:
2>Target "Build" in file "Z.sln.metaproj" from project "Z.sln" (entry point):
Using "MSBuild" task from assembly "Microsoft.Build.Tasks.v4.0, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a".
Task "MSBuild"
Global Properties:
BuildingSolutionFile=true
CurrentSolutionConfigurationContents=<SolutionConfiguration>
<ProjectConfiguration Project="{C83D035D-169B-4023-9BEE-1790C9FE22AB}" AbsolutePath="X.csproj" BuildProjectInSolution="True">Debug|AnyCPU</ProjectConfiguration>
<ProjectConfiguration Project="{15E7887D-F1DB-4D85-8454-E4EF5CBDE6D5}" AbsolutePath="Y.csproj" BuildProjectInSolution="True">Debug|AnyCPU</ProjectConfiguration>
</SolutionConfiguration>
So the question is:
Is there a neat way of me getting to do an XPath replace or similar to have the effect of changing BuildProjectInSolution="True" to BuildProjectInSolution="False" for Project Y above
Failing that, is there a relatively simple edit I can do within a .ccproj (An Azure 1.4 Package) or a .csproj (a general project) file to cause the effects (including triggering of dependent projects) of the project being enabled within a commandline msbuild Z.sln solution build to be nullified?
Not sure it qualifies as neat, but you can set CustomAfterMicrosoftCommonTargets to import an msbuild file to over-ride the BuildDependsOn property, pointing it to your own custom build task. Basically, by setting CustomAfterMicrosoftCommonTargets you get msbuild to import an msbuild file containing the following:
<PropertyGroup>
<OldBuildDependsOn>$(BuildDependsOn)</OldBuildDependsOn>
<BuildDependsOn>MyBuild</BuildDependsOn>
</PropertyGroup>
<Target Name="OldBuild" DependsOnTargets="$(OldBuildDependsOn)" />
<Target Name="MyBuild">
<CallTarget Targets="OldBuild" Condition="<IfIWantThis>" />
</Target>
Edit
You can use the following MyBuild target to Include/Exclude projects based on regular expressions passed in as IncludeInBuild and ExcludeFromBuild properties. (If you want complex regexes, you may fall foul of MSBuild special character escaping, but this works well enough for simple matching)
> msbuild /p:ExcludeFromBuild="Tests|Install|Azure"
<Target Name="MyBuild">
<CallTarget Targets="OldBuild" Condition="('$(IncludeInBuild)'=='' OR
'$([System.Text.RegularExpressions.Regex]::IsMatch($(MSBuildProjectFullPath),
$(IncludeInBuild),
System.Text.RegularExpressions.RegexOptions.IgnoreCase))'=='True') AND
('$(ExcludeFromBuild)'=='' OR
'$([System.Text.RegularExpressions.Regex]::IsMatch($(MSBuildProjectFullPath),
$(ExcludeFromBuild),
System.Text.RegularExpressions.RegexOptions.IgnoreCase))'=='False')" />
</Target>
You could always pass the particular projects you want to build as parameters to the MSBuild.
The MSBuild command line would look like this:
MSBuild /t:<Project Name>:Rebuild;<Another Project Name>:Rebuild
In TeamCity, you would put <Project Name>:<Target Action> in the target field in the MSBuild runner.
I add a system parameter under Parameters
Name: system.ExcludeFromBuild
Kind: System property (system.)
Value: path to your csproj

How to always execute a target in MSBuild

I have an MSBuild file that manipulates the AssemblyInfo file before the application is compiled. At the end of the build, it restores the AssemblyInfo file. It does this by backing up the file, manipulating it, and then after build time, restoring the file.
This works fairly well except when an error occurs during the build. It then does not restore the original file. Is there a way I can tell MSBuild to execute a target at the end of a build no matter if it succeeded or failed?
Based on your last comment to the original question I would take another approach, and forget the approach you are currently taking. You should know that your version info doesn't have to be in the AssemblyInfo.cs file. It can be in any code file, just as long as you only have attributes AssemblyVersion and AssemblyFileVersion defined once each. With that being said what I would do is follow these steps:
Remove AssemblyVersion & AssemblyFileVersion from AssemblyInfo.cs
Create a new file, name it whatever you want want in my case I put it at Properties\VersionInfo.cs. Do not add this file to the project.
Edit the project file to include that file into the list of file to be compiled only when you want it
Let's expand a bit on #3. When you build a .NET project, the project itself is an MSBuild file. Inside that file you will find an item declared Compile. This is the list of files that will be sent to the compiler to be compiled. You can dynamically include/exclude files from that list. In you case you want to include the VersionInfo.cs file only if you are building on the build server (or whatever other condition you define). For this example I defined that condition to be if the project was building in Release mode. So for Release mode VersionInfo.cs would be sent to the compiler, and for other builds not. Here are the contents of VersionInfo.cs
VersionInfo.cs
[assembly: System.Reflection.AssemblyVersion("1.2.3.4")]
[assembly: System.Reflection.AssemblyFileVersion("1.2.3.4")]
In order to hook this into the build process you have to edit the project file. In that file you will find an element (maybe more than 1 depending on project type). You should add a target similar to the following there.
<Target Name="BeforeCompile">
<ItemGroup Condition=" '$(Configuration)'=='Release' ">
<Compile Include="Properties\VersionInfo.cs" />
</ItemGroup>
</Target>
Here what I've done here is to define a target, BeforeCompile, which is a well-known target that you can override. See this MSDN article about other similar targets. Basically this is a target which will always be called before the compiler is invoked. In this target I add the VersionInfo.cs to the Compile item only if the Configuration property is set to release. You could define that property to be whatever you wanted. For instance if you have TFS as your build server then it could be,
<Target Name="BeforeCompile">
<ItemGroup Condition=" '$(TeamFoundationServerUrl)'!='' ">
<Compile Include="Properties\VersionInfo.cs" />
</ItemGroup>
</Target>
Because we know that TeamFoundationServerUrl is only defined when building through TFS.
If you are building form the command line then something like this
<Target Name="BeforeCompile">
<ItemGroup Condition=" '$(IncludeVersionInfo)'=='true' ">
<Compile Include="Properties\VersionInfo.cs" />
</ItemGroup>
</Target>
And when you build the project just do msbuild.exe YourProject.proj /p:IncludeVersion=true. Note: this will not work when building a solution.
What about changing the problem:
Add a "template" AssemblyInfo.cs.template to version control that represents your "ideal" AssemblyInfo.cs with regex hooks in there
Before build, copy the template to the real and apply your regexes
Add some kind of subversion ignore for AssemblyInfo.cs (I'm no svn expert, but I'm pretty sure there is a way you can tell it to ignore certain files)
In the event that your devs need to add some kind of customization that would normally appear in an AssemblyInfo.cs (eg InternalsVisibleTo), then get them to add it to a different .cs file that IS checked in.
As a further refinement, combine Sayed's solution with mine and remove version info stuff from the actual AssemblyInfo.cs and have a VersionInfo.cs.template that is checked in, that creates a VersionInfo.cs in BeforeBuild.
I never used it, but from the documentation it seems that the OnError Element is useful to what you're trying to achieve.
Causes one or more targets to execute,
if the ContinueOnError attribute is
false for a failed task.

AfterPublish target not working

World's simplest task (see below) is not being executed after I publish my web application project. Any idea why?
<Target Name="AfterPublish">
<Copy SourceFiles="C:\A.txt" DestinationFiles="C:\B.txt" />
</Target>
Note: The following applies to VS2010 and publishing web-application projects with the "Web Deploy" publish method selected in the 'Build/Publish {projectname}' dialog.
Julien Hoarau's correct in that "Publish" is NOT the name of the msbuild target invoked in the above case; the actual target name is "MSDeployPublish".
Therefore, you have to define a "Target" element whose "AfterTarget" attribute's value is set to "MSDeployPublish" - the "Name" attribute's value does not matter (as long as it's unique among target names).
Here's how to do it:
Open the project file (e.g. *.csproj) in a text/XML editor and, just before the closing </Project> tag, add a <Target Name="CustomPostPublishAction" AfterTargets="MSDeployPublish"> element; pick a name of your choice for "CustomPostPublishAction".
Add a so-called Task child element that performs the desired action; for instance, to add a command to be passed to cmd.exe, use an <Exec Command="..." /> element.
Example:
<Target Name="CustomPostPublishActions" AfterTargets="MSDeployPublish" >
<Exec Command="echo Post-PUBLISH event: Active configuration is: $(ConfigurationName)" />
</Target>
Note:
In command strings, use XML entity(?) references in place of characters that would break XML parsing, e.g. "&gt" in place of "<".
For documentation of the <Target> element in general, see http://msdn.microsoft.com/en-us/library/t50z2hka.aspx
Task-elements reference here: http://msdn.microsoft.com/en-us/library/7z253716.aspx
In general, if you need to determine the name of the msbuild.exe target that is actually invoked by Visual Studio 2010, do the following:
Go to Tools/Options..., Project and Solutions/Build and Run, select 'Detailed' (or, for even more information, 'Diagnostic') from the dropdown list labeled 'MSBuild project build output verbosity.
After running the build/publish action, e.g. Build/Publish, examine the Output window for the last occurrence of the string "Done building target" to determine the top-level target that was invoked.
Visual Studio 2013. Publish Web application to file system.
<Target Name="Moose" AfterTargets="GatherAllFilesToPublish" >
<Message Importance="high" Text="***Moooooooooooooooose***$(WPPAllFilesInSingleFolder)***$(TargetDir)" />
</Target>
Note: Make sure that build logging is set to at least to Detailed. Look for it under Tools -> Options -> Projects and Solutinos -> Build and Run -> MSBuild output verbosity. Diagnostic is also fine if you want to investigate which build target was last run before actual publish.
This seems to work in Visual Studio 2019
<Target Name="MyCustomTarget" AfterTargets="Publish">
<Copy SourceFiles="C:\A.txt" DestinationFiles="C:\B.txt" />
</Target>
You must define override the target at the end of your file, after <Import ... />
Launch MSBuild with detailed verbosity to see why your target is ignored :
msbuild project.csproj /t:Target_to_Launch /v:d
AfterPublish is called after Publish target, but Publish is not the target called when you publish a web application. Publish is the target for publishing ClickOnce application.
You'll have to find the target used when you call Publish in Visual Studio, it could be Package, WebPublish...
I'm a little lazy right now to figure out the mess of targets to find the right one for a file-based publish (which you might be interested in). What you can do in the meantime is defining an AfterBuild target in the *.pubxml file.
<Target Name="AfterBuild">
...
I recommend also turning off the property "DeleteExistingFiles" because if you copy files into the directories being published, it does a clean somewhere during the publishing process.
<DeleteExistingFiles>False</DeleteExistingFiles>