Adding and running custom target by msbuild - c#

I am struggling with seemingly very basic task at the moment, that is addding a new custom target to csproj file and run it from the command line using msbuild.
I did an extensive research on the net but I found no solution that is actually working.
Let's say that I add the following target to my csproj file:
<Target Name="TeamCity">
<Message Text="I am Running!"/>
</Target>
or even something that depends on Build:
<Target Name="TeamCity" DependsOnTargets="Build">
<Message Text="I am Running!"/>
</Target>
This is what msbuild documentation suggests.
But running the target seems to be a mission impossible.
While I am able to run predefined target on csproj:
msbuild MySolution.sln /t:MyProject:Rebuild /p:Configuration="Release" /p:Platform="Any CPU"
I am not able to run the target I just added - that is TeamCity target:
msbuild MySolution.sln /t:MyProject:TeamCity /p:Configuration="Release" /p:Platform="Any CPU"
I always get error MSB4057: The target "TeamCity" does not exist in the project.
What is the deeply kept secret to make this running?
PS. Please note that I need the task to be working on a project level not on a solution. And I need to run msbuild MySolution.sln ... not as many incorrectly suggest msbuild MyProject.csproj ...

The secret is pretty simple - you can't make this running.
Because, msbuild generates intermediate project file (YourSolution.sln.metproj) but that will not have the imports from YourProject.csproj, including the .targets files. That's why YourCustomTarget is not recognized.
What you can try is using Before/After Targets to inject your targets in predefined build flow.
Hope it will help.

Related

List defined preprocessor symbols for C#

When running dotnet, msbuild, or csc, I want to output a list of defined preprocessor symbols, similar to gcc -dM -E. How can I do this?
The defined preprocessor symbols are listed in a proprety called DefineConstants. To echo them, you could add a target to your project file like the following:
<Target Name="EchoDebugInfo" BeforeTargets="CoreCompile">
<Message Importance="high" Text="Just before building, current compile defines are $(DefineConstants)"></Message>
</Target>
In my test run (using dotnet build with no extra parameters), this printed:
Just before building, current compile defines are TRACE;DEBUG;NETCOREAPP;NETCOREAPP2_2
Note that if you omit Importance="high", the importance of the message defaults to "normal", which won't show in the default verbosity of dotnet build. Setting Importance="high" allowed me to get the output without changing the default verbosity of the dotnet build command.
BTW, if you try to define the BeforeBuild target as suggested in the Visual Studio docs, you'll discover that it doesn't work if you're using the new .Net Core-style projects (e.g., <Project Sdk="Microsoft.NET.Sdk">). This is because the SDK project is auto-imported after your project file, so you'll see something similar to this message in the build logs:
Overriding target "BeforeBuild" in project "/home/rmunn/path/to/project/Project.fsproj" with target "BeforeBuild" from project "/usr/share/dotnet/sdk/3.0.100/Microsoft.Common.CurrentVersion.targets".
And then your BeforeBuild target doesn't work. There's a note in the MSDN docs that explains this (emphasis in original):
SDK-style projects have an implicit import of targets after the last line of the project file. This means that you cannot override default targets unless you specify your imports manually as described in How to: Use MSBuild project SDKs.
If you need full control over when the SDK project is imported, then that's the way to go. But for this simple use case, I prefer to define my own target and use BeforeTargets="CoreCompile" to place it in the correct place in the build order.

How to exclude one project out of multiple project from one solution during building using MSBuild in command prompt? [duplicate]

I need to build a solution, but exclude one project. How should I do it?
I searched a lot about this issue, but nothing could help.
An ItemGroup section rises the following exception:
Invalid element . Unknown task or datatype.
PropertyGroup also rises the exception.
Below is my code sample:
<project name="TI 8.1.6 build script">
<ItemGroup>
<Solution Include="${ROOT}\Core\TI Core.sln" Exclude="${ROOT}\Utilities\DTS Indexing Service\Tdi.Origami.IndexUpdaterServiceSetup\Tdi.Origami.IndexUpdaterServiceSetup.wixproj"/>
</ItemGroup>
...
</project>
How can I do this?
You can exclude projects at the solution level for a specific build configuration by using the Configuration Manager Dialog in Visual Studio:
Then you can simply invoke msbuild on the solution file specifying the build configuration to use:
msbuild /property:Configuration=Release MySolution.sln
The solution suggested by Enrico is the most versatile solution that would work always. An alternative solution might be to use a <MSBuild> task directly. This will work for you if you have all your project files under a particular directory, or be able to easily enumerate all projects you want to build (i.e. number of projects in your solution is not very big).
For example, this MSBuild file will build every project under your current directory except for a specific project:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<MyProjectReferences Include="**\*.*proj" />
<MyProjectReferences Exclude="Utilities\DTS Indexing Service\Tdi.Origami.IndexUpdaterServiceSetup\Tdi.Origami.IndexUpdaterServiceSetup.wixproj" />
</ItemGroup>
<Target Name="BuildAllExceptWixProject">
<MSBuild Projects="#(MyProjectReferences)" Targets="Build" />
</Target>
</Project>
Then you can build that using command line msbuild <myproject> /t:BuildAllExceptWixProject
In your solution file (.sln), remove the Build.0 entries. For example:
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MyProject", "MyProject.vcxproj", "{2281D9E7-5261-433D-BB04-176A61500CA3}"
EndProject
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{2281D9E7-5261-433D-BB04-176A61500CA3}.Debug|x86.Build.0 = Debug|x64
If you delete this "Build.0" entry, it will load in the solution fine, but will not be built, either through the GUI or via external MSBuild.
Since VS 2019 and MSBuild 16.7, the right way is to use Solution filters. Ref
create a master.proj file:
in another ItemGroup add DefaultExclude properties for programs - put it in front of the solution
-- BA was Canadian
Configuration=Release
Release
drop the master.proj into the directory with the programs and msbuild the master.proj
compiles everything except... that HelloWorld

MSBuild not skipping target even though no changes were made to target inputs

I use MSBuild Version 14.0.
Following the documentation here, I defined my own Build task like this:
<Target Name="Build"
Inputs="#(Compile)"
Outputs="MyLibrary.dll">
<Csc
Sources="#(Compile)"
OutputAssembly="MyLibrary.dll"/>
</Target>
The idea is to reduce build time by only building incrementally -- the build task is supposed to run only when any of the files in the #(Compile) list (which is currently a collection of all .cs files in the project) is edited after the creation of the latest version of MyLibrary.dll.
Using MSBuild, I ran the following command:
msbuild MyProject.csproj /t:Build /p:Platform="AnyCPU" /fileLogger
/flp:logfile=Output.log;verbosity:minimal
The first time I executed the command, everything was built from scratch, as expected.
However, on subsequent occasions when I ran the command again without having made any changes to any of my .cs files, the project was also built from scratch each time.
Why didn't MSBuild just skip the Build target even though there were no changes to any of the files included in the Inputs parameter to Target?
It was not working because I forgot to delete the following line from my .csproj file:
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
So the Build task generated by Microsoft.CSharp.targets overrode the Build task that I had defined.
Once that line was deleted, my custom Build task ran as expected.

roslyn compiler not copied to AspnetCompileMerge folder using msbuild

I have a .NET MVC project that I'm trying to deploy using Jenkins.
I had been letting Jenkins run msbuild, then copying the resulting files out using RoboCopy. I wanted to switch to just use a publish profile. The publishing profile works fine on my local machine using Visual Studio, but on the Jenkins host it fails using msbuild.
The error it gives is
ASPNETCOMPILER : error ASPRUNTIME: Could not find a part of the path 'C:\Program Files (x86)\Jenkins\jobs\myProject\workspace\myProject\obj\Debug\AspnetCompileMerge\Source\bin\roslyn\csc.exe'. [C:\Program Files (x86)\Jenkins\jobs\myProject\workspace\myProject\calendar.csproj]
I'm using the Microsoft.Net.Compilers nuget package to pull in the C# compiler, because some of the collaborators on the project are still on Visual Studio 2013, but we're using C#6 language features in the project.
Thing is, the project built just fine using MSBuild on jenkins before I added the publish flag. It's only since adding the /p:DeployOnBuild=true;PublishProfile=MyProfile setting that it started failing... yet the publish step works fine from withing Visual Studio, and the roslyn compiler even gets copied to the obj\Debug\AspnetCompileMerge\Source\bin\ folder on my local machine. What gives?
Honestly, since msbuild14 is available on the Jenkins server, it probably doesn't even need the roslyn csc.exe file. Is there a way I can make msbuild ignore it?
My Publish Profile
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<WebPublishMethod>FileSystem</WebPublishMethod>
<LastUsedBuildConfiguration>Debug</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<SiteUrlToLaunchAfterPublish />
<LaunchSiteAfterPublish>True</LaunchSiteAfterPublish>
<ExcludeApp_Data>False</ExcludeApp_Data>
<publishUrl>\\myserver\someshare\mysite</publishUrl>
<DeleteExistingFiles>True</DeleteExistingFiles>
<PrecompileBeforePublish>True</PrecompileBeforePublish>
<EnableUpdateable>True</EnableUpdateable>
<DebugSymbols>False</DebugSymbols>
<WDPMergeOption>DonotMerge</WDPMergeOption>
</PropertyGroup>
</Project>
What I've Tried So Far
I've tried updating the compiler package.
Manually copying compiler
I added steps to my .csproj file to force-copy the missing compiler files to the AspnetCompileMerge directory (I'd already been copying them to the bin\roslyn directory to resolve another problem)
<Target Name="CopyRoslynFiles" AfterTargets="BeforeBuild">
<ItemGroup>
<RoslynFiles Include="$(SolutionDir)packages\Microsoft.Net.Compilers.1.1.1\tools\*" Exclude="$(SolutionDir)packages\Microsoft.Net.Compilers.1.1.1\tools\*.sys" />
</ItemGroup>
<MakeDir Directories="$(WebProjectOutputDir)\bin\roslyn" />
<MakeDir Directories="$(WebProjectOutputDir)\obj\$(Configuration)\AspnetCompileMerge\Source\bin\roslyn" />
<Copy SourceFiles="#(RoslynFiles)" DestinationFolder="$(WebProjectOutputDir)\bin\roslyn" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" />
<Copy SourceFiles="#(RoslynFiles)" DestinationFolder="$(WebProjectOutputDir)\obj\$(Configuration)\AspnetCompileMerge\Source\bin\roslyn" SkipUnchangedFiles="true" Retries="$(CopyRetryCount)" RetryDelayMilliseconds="$(CopyRetryDelayMilliseconds)" />
</Target>
Turning off Updateability in the publish profile
Based on Wesley Rathburn's answer on a similar question, I tried making the precompiled site so it could not be updated in the publish profile:
<EnableUpdateable>False</EnableUpdateable>
Though this revealed some dead code that needed removed in my views, it didn't fix the error during the jenkins build.
Running MsBuild locally
I can successfully run the msbuild command on my local machine. It deploys to the server and everything. Here's the command I run in powershell:
&"C:\Program Files (x86)\MSBuild\14.0\Bin\MSBuild.exe" /p:Configuration=Debug "/p:DeployOnBuild=true;PublishProfile=MyProfile" myproject\myproject.csproj
Removing the statements that copy the compiler entirely
It occurred to me that maybe I didn't need the statements to copy the roslyn compiler to the bin folder anymore, since msbuild14 was available on Jenkins now (and I'm not sure it was when I first built the project). Sadly, same error occurs. It's looking for the roslyn\csc.exe file, even though there's no apparent need for it to do so!
Just putting this here, because I spent two days trying to resolve this same issue (roslyn csc.exe not copied), but none of these answers solved my problem.
It turns out that Microsoft.CodeDom.Providers.DotNetCompilerPlatform 1.0.6 (and 1.0.7) is broken. Downgrade to 1.0.5.
I was getting the same errors as everyone else here, but I'm using VS 2017, and both local WebDeploy as well as AzureDeploy were broken (no csc.exe found). I tried all the suggestions that I could find on the internet (most of them redirect back to this SO post) but nothing worked until I downgraded to 1.0.5.
So I hope this is helpful to anyone who is struggling and has just recently upgrade to 1.0.6!
See:
https://github.com/aspnet/RoslynCodeDomProvider/issues/13
and
https://github.com/dotnet/roslyn/issues/21340
So, the workaround I'm using for now (which I don't entirely like), is just to remove the dependencies on the Compilers and CodeDOM Compilers packages. I also had to clear out the references in the .csproj and web.config files. That involved removing those packages from a shared assembly as well.
This will break the project for people still using Visual Studio 2013, which I don't like, but it builds on my Jenkins host now, which I do like. If anyone has a better solution, I'd be happy to hear it.
So yeah, I have this problem too with VS2017 & VS2019 and Microsoft.CodeDom.Providers.DotNetCompilerPlatform2.0.1 too. Did a lot of troubleshoooting msbuild and digging deep and trying to do my own workarounds and the changes in build file that just did nothing, but that didn't seem right at all. So I started looking in a different direction.
What I discovered was I couldn't build a .csproj directly and have either the nuget targets in Microsoft.CodeDom.Providers.DotNetCompilerPlatform get run by msbuild, or my own custom ones.
However using msbuild with the .sln with a target of myproj:Rebuild made everything work.

Build only the VS Setup project via command line

I have a solution that contains many projects and a setup project (.vdproj).
I want to be able to build ONLY the setup project via command line.
I tried to use
devenv /build Debug "C:\\MySolution\MySolution.sln" /project "CSharpWinApp\CSharpWinApp.vdproj" /projectconfig Debug
but it also built the rest of my solution projects and I want to avoid it. I tried it few times in a row - no project has changed but it stil built it all. I tried to remove the .vdproj project dependencies but it didn't work. I got the message "This dependency was added by the project system and cannot be removed".
Any suggestions?
Use the following command line to build setup projects.
Note: Support for setup projects has been dropped from Visual Studio 2012.
devenv "c:\your solution file.sln" /Project "c:\your setup project file.vdproj" /Build "Release"
If you really have to use msbuild, create a msbuild project file and use the Exec task to call the command line above as demonstrated in Hassan's answer.
you can isolate your setup in a setup solution to be sure that it will not compile your application.
for building your setup project you can do this with TFSBuild 2010 as follow:
First, to automate the building of .vdproj project, you’re going to need to write your own msbuild file because they are not in msbuild format and therefore TFS Build does not know what to do with them. I found some good examples on the net on how to do this, but I updated mine a little for 2010. Here it is:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<Target Name="Build">
<PropertyGroup>
<DevEnv>$(ProgramFiles)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.com</DevEnv>
<SolutionFile>$(MSBuildProjectDirectory)\MySolution.sln</SolutionFile>
<ProjectFile>$(MSBuildProjectDirectory)\MySetupProject\MySetup.vdproj</ProjectFile>
<Configuration>Release</Configuration>
</PropertyGroup>
<Exec Command="$(DevEnv) $(SolutionFile) /Rebuild $(Configuration) /Project $(ProjectFile) /ProjectConfig $(Configuration) /Log" ContinueOnError="false" IgnoreExitCode="false" WorkingDirectory="$(MSBuildProjectDirectory)" />
</Target>
</Project>
thank to Leonard Woody

Categories

Resources