MSBuild Project build failed but solution build succeeded - msbuild

In MSBuild, errors in a target that is executed after target Build will cause the project to fail but not the solution.
Here is a short sample, just add it to an empty project and build the solution:
<Target Name="DetectWebDeployPackage" AfterTargets="Build">
<Error Text="boom" />
</Target>
Found a workaround that may be adequate in some cases - change the dependency to BeforeTargets="AfterBuild" however for targets using WebDeploy Package target it causes a circular dependency.
Can anyone help me please?

Related

Treat ProjectReference as PackageReference or allow PackageReference to local csproj

I have a netstandard2.0 csproj (let's call it MyPackage) that is packed at build time (as specified by GeneratePackageOnBuild) into a nuget package. This nuget package has custom props and targets in the build directory (so referencing projects get these imported).
I have another project (let's call it MyConsumer) in the same solution for testing MyPackage. I want MyConsumer to have the build asset props and targets imported from MyPackage at build time, just as if it were consuming it as a PackageReference from some remote nuget source.
How can I get this working (most simply)?
I have been able to do it via a very convoluted method where I have MyConsumer add a PackageReference to MyPackage and override the RestoreSources in MyConsumer to point to the bin directory of MyPackage. This gets very weird when running dotnet build or Visual Studio build of the sln, because project metadata is generated upfront for all projects during Restore and thus MyPackage doesn't exist at that point. The resolution was to add nested calls to MSBuild within the MyConsumer project, but then this becomes even worse, since Visual Studio restores operate quite differently than that automatic restores performed by dotnet build.
Is there any simple way of doing this?
This is what I have now
<Project>
<Target Name="Build">
<Message Text="Running inner build" Importance="high" />
<!--
Need to call MSBuild twice, once to restore, then again to restore and build to get the restore of the Sdk to work
because of this bug in MSBuild: https://github.com/Microsoft/msbuild/issues/2455
Note the trailing Prop=1 is required to get MSBuild to invalid it's cache of the project target imports
-->
<MSBuild Projects="$(MSBuildProjectFullPath)" Targets="Restore" Properties="Configuration=$(Configuration);Version=$(Version);IsInnerBuild=true;Prop=1" />
<!-- Have to use dotnet build instead of another call to MSBuild because of another bug that prevents proper imports within the same physical process -->
<Exec Command="dotnet build /p:Configuration=$(Configuration) /p:Version=$(Version) /p:IsInnerBuild=true" />
<Message Text="Finished inner build" Importance="high" />
</Target>
<Target Name="Restore" />
<Target Name="RemoveBin">
<RemoveDir Directories="bin" />
</Target>
<!-- Don't do real cleans old rebuild since it breaks MSBuild due to the same above bug -->
<Target Name="Rebuild" DependsOnTargets="RemoveBin;Build">
</Target>
</Project>
Treat ProjectReference as PackageReference or allow PackageReference to local csproj
If I understand you correct, you want to generate the package with project MyPackage, then install it to the test project MyConsumer and have the build asset props and targets imported from MyPackage at build time.
To accomplish this goal, you need to complete the following few things:
Make sure the project MyPackage build before the project MyConsumer.
Set the package into the packager source
Add the package MyPackage.nupkg to the test project MyConsumer during the build time.
Details for above:
Make sure the project MyPackage build before the project MyConsumer.
Since you wan to test the package which generated by the project MyConsumer, you should make sure this package grnerate before test project using it, so we need set the project MyConsumer reference the the project MyPackage.
Set the package into the packager source
You can use a post-build event for the project MyPackage to copy the package MyPackage.nupkg to the local feed, or you can just add the bin directory of MyPackage.nupkg to the package source.
Add the package MyPackage.nupkg to the test project MyConsumer during the build time.
Using VS 2017 and the PackageReference style of the test project MyConsumer, you can set a Directory.Build.props file into the root of your solution containing the test project MyConsumer you need:
<Project>
<ItemGroup>
<PackageReference Include="MyPackage" Version="1.0.* />
</ItemGroup>
</Project>
This will add these NuGet packages to the test project MyConsumer in the solution, it will be used as a PackageReference from some remote nuget source.
Check the Martin`s answer for some more details.
Hope this helps.

MSbuild run specific targets for specific projects

I am using following code in 2 our of 3 projects from the solution default target is set to PublishMe
<Target Name="PublishMe" DependsOnTargets="PipelinePreDeployCopyAllFilesToOneFolder">
<Error Condition="'$(PublishDestination)'==''" Text="The PublishDestination property must be set to the intended publishing destination." />
<MakeDir Condition="!Exists($(PublishDestination))" Directories="$(PublishDestination)" />
<ItemGroup>
<PublishFiles Include="$(_PackageTempDir)\**\*.*" />
</ItemGroup>
<Copy SourceFiles="#(PublishFiles)" DestinationFiles="#(PublishFiles->'$(PublishDestination)\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="True" />
</Target>
If I run msbuild it builds and publishes just as expected, the problem is that I can't build it from VS anymore, I am having error of missing projectname.dll
Is there way I can change default build target if it is msbuild doing build?
I tried to /t:ProjectName:target;ProjectName2:target, but it isn't working with custom targets for some reason.
Is there way I can specify param from msbuild console to run PublishMe target and if not present run simple build?
You can usefollowing command
msbuild projectname /t:targetname.
Also please check the below link for more info
Building a solution file using msbuild

Referencing macros on msbuild (12.0) command line property assignment

I am curious, is it possible to reference a macro on a command line property assignment for MSBuild?
E.g:
msbuild.exe MySolution.sln /p:CustomBeforeMicrosoftCSharpTargets="$(SolutionDir)\custom.targets"
Would this also work when specified as "MSBuildArguments" from an "Edit Build Definition"/"Queue New Build" from Visual Studio connected to TFS?
E.g:
/p:CustomBeforeMicrosoftCSharpTargets="$(SolutionDir)\custom.targets"
Because it doesn't appear to be importing these targets for me. But the targets file is definitely there, alongside the solution, in the build workspace.
I don't want to have to specify an absolute path. Not sure how working with relative paths is meant to work here, can't find any advice on the internet, and debugging it is quite difficult, as it is called on a build agent using a workflow. The workflow logging is definitely reporting it is calling MSBuild with these arguments, but nowhere in the verbose logging output can I see it is making reference to the CustomBeforeMicrosoftCSharpTargets target, or calling it.
EDIT
I wrote a little test build project buildme.proj to further my understanding.
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SetMe>NotInTheSandbox</SetMe>
</PropertyGroup>
<PropertyGroup>
<SomeMacroValue>c:\Sandbox\BuildTest</SomeMacroValue>
</PropertyGroup>
<PropertyGroup>
<AlreadySet>$(SomeMacroValue)\InTheSandbox</AlreadySet>
</PropertyGroup>
<Target Name="Build">
<Message Text="I am building!" />
<Message Text="Some macro value: $(SomeMacroValue)" />
<Message Text="$(SetMe)" />
<Message Text="$(AlreadySet)" />
</Target>
</Project>
When I execute with the command:
msbuild buildme.proj /p:SetMe="$(SomeMacroValue)\StillNotInSandbox"
I get the following output:
Microsoft (R) Build Engine version 12.0.31101.0
[Microsoft .NET Framework, version 4.0.30319.42000]
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 10/12/2015 22:12:08.
Project "C:\Sandbox\BuildTest\buildme.proj" on node 1 (default targets).
Build:
I am building!
Some macro value: c:\Sandbox\BuildTest
$(SomeMacroValue)\StillNotInSandbox
c:\Sandbox\BuildTest\InTheSandbox
Done Building Project "C:\Sandbox\BuildTest\buildme.proj" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.02
So clearly, it is not behaving how I expected: The macro identifier appears in the output message text.
Is there a solution to this?
A "macro" like $(SolutionDir) exists only in VisualStudio and VS passes the value to MSBuild.
Instead MSBuild makes Environment variables available as properties, so a batch file like this
set SomeMacroValue=foo
msbuild buildme.proj /p:SetMe="$(SomeMacroValue)\StillNotInSandbox"
is probably what you are looking for.
And you can set environment variables per-user or per-machine (Control Panel\All Control Panel Items\System Advanced System Settings, Environment variables).

Why is MSBuild trying to run the projects it's building?

I'm currently writing an msbuild script to build a solution I've been working on, as well as run its tests. On my development machine, this works as expected. However, when I try to run the same build script on our build server, I get several failures. I've tracked the source of the problem down to the fact that my build script appears to be trying to run the .exe file associated with my application. This line during the script execution tipped me off, since it doesn't run that command on my dev box:
MSIAuthoring:
Building MSI
"C:\Program Files (x86)\Jenkins\workspace\Test Build\BuildArtifacts\MsiBuildTool.exe" "/MBSBUILD:MsiBuildTool"
I'm fairly new to build scripting, but my understanding is that the build script shouldn't be trying to run my program unless I explicitly tell it to do so. Does anyone know what might be causing this?
For reference, here is my build script:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="RunTests"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<BuildArtifactsDir Include="BuildArtifacts\"/>
<SolutionFile Include="MsiBuildTool.sln"/>
<NUnitConsole Include="C:\Program Files (x86)\NUnit 2.6.4\bin\nunit-console.exe"/>
<UnitTestsDll Include="BuildArtifacts\MsiBuildToolUnitTests.dll"/>
<TestResultsPath Include="BuildArtifacts\TestResults.xml"/>
</ItemGroup>
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == ''">Release</Configuration>
<Platform Condition="'$(Platform)' == ''">Any CPU</Platform>
</PropertyGroup>
<Target Name="Init" DependsOnTargets="Clean">
<MakeDir Directories="#(BuildArtifactsDir)"/>
</Target>
<Target Name="Clean">
<RemoveDir Directories="#(BuildArtifactsDir)"/>
</Target>
<Target Name ="Compile" DependsOnTargets="Init">
<MSBuild Projects="#(SolutionFile)"
Targets ="Build"
Properties ="OutDir=%(BuildArtifactsDir.FullPath);Configuration=$(Configuration);Platform=$(Platform)"/>
</Target>
<Target Name="RunTests" DependsOnTargets="Compile">
<Exec Command='"#(NUnitConsole)" #(UnitTestsDll) /xml=#(TestResultsPath)'/>
</Target>
</Project>
Update:
After some digging through the output, I found that "MSIAuthoring" step was the result of the Wix# library that I'm using. As described by this thread: https://wixsharp.codeplex.com/discussions/644609#
I disabled the MSIAuthoring step by removing this line in my .csproj files:
<Import Project="..\packages\WixSharp.1.0.22.3\build\WixSharp.targets" Condition="Exists('..\packages\WixSharp.1.0.22.3\build\WixSharp.targets')" />
You're building solution file, thus MSBuild will generate msbuild-xml script first and then will build it. To find why it's being called on build machine but not on your dev machine - follow this advice and obtain generated MSBuild scripts from your dev environment and your build server. Then compare it.
Also enable diagnostic logging (/verbosity:diag in the command line) as Lex Li advised, and you'll see detailed decisions why each target being run or not - grep logs for something like "Conditions A, B, C on target BuildMSI evaluated to False" and this will show you the difference between environments.
It might be some type of post-build script on one of the projects which builds MSI only if it's being run not on dev environment - check actual build script to find where it comes from. Also check that it's really related to your build script, and it's not an extra build step in your Jenkins build configuration.

Stopping Post Build events on project when building directly from MSBuild

I have a project which has some post build events that do some copying around for other projects. I unfortunately cannot change that, and have been asked to write a build script for use on a CI server.
Problem is that the post build steps run off the debug/release bin folders and I compile through the build script to a different folder. So one solution would be to let the project build as is, and then manually copy all files from the bin folders to the output folder I am using. However that feels like a bit of a hack, so I was wondering if there is a way for an MSBuild task to tell the solution it is building to ignore PostBuild events, I believe you could set a property PostBuildEvent='' but it didnt seem to stop them from happening...
Here is an example of the build script target:
<Target Name="Compile" DependsOnTargets="Clean;">
<MSBuild Projects="$(SourceDirectory)\SomeSolution.sln"
Properties="Configuration=Release; OutputPath=$(CompilationDirectory); PostBuildEvent=''" />
</Target>
Anyone had to do anything similar before?
To disable all PostBuildEvents, set the CustomAfterMicrosoftCommonTargets to C:\PostBuild.config (or whatever you name the file) and have PostBuild.config to be:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="PostBuildEvent"/>
</Project>
Add /p:CustomAfterMicrosoftCommonTargets="C:\PostBuild.config" to your msbuild command line
Or update your MsBuild task properties:
<MsBuild Projects="$(ProjectTobuild)" Properties="Configuration=$(Configuration);Platform=$(Platform);CustomAfterMicrosoftCommonTargets='C:\PostBuild.config'" Targets="Build"/>
To disable PostBuildEvents at project level for MSBuild, simply put these codes inside .csproj:
<Target Name="BeforeBuild">
<PropertyGroup>
<PostBuildEvent Condition="'$(BuildingInsideVisualStudio)' == 'false' Or '$(BuildingInsideVisualStudio)' != 'true'"></PostBuildEvent>
</PropertyGroup>
</Target>