MSBuild property set in target is not visible in parent target - msbuild

here is my targets file
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="BuildApp" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="12.0">
<PropertyGroup>
<TestInitResult>false</TestInitResult>
</PropertyGroup>
<Target Name="BuildApp">
<CallTarget Targets="TestInit" ContinueOnError="true"/>
<Message Importance="high" Text="2 Value of flag: $(TestInitResult)"/>
<CallTarget Targets="target2" />
<CallTarget Targets="RunBuild" Condition="'$(TestInitResult)'=='true'"/>
</Target>
<Target Name="TestInit">
<PropertyGroup>
<TestInitResult>true</TestInitResult>
</PropertyGroup>
<Message Importance="high" Text="1 Value of flag: $(TestInitResult)"/>
</Target>
<Target Name="target2">
<Message Importance="high" Text="Inside run target2"/>
</Target>
<Target Name="RunBuild">
<Message Importance="high" Text="Inside run build"/>
</Target>
</Project>
Output of msbuild:
D:\CQF>msbuild build.targets
Microsoft (R) Build Engine version 12.0.30501.0
[Microsoft .NET Framework, version 4.0.30319.34209]
Copyright (C) Microsoft Corporation. All rights reserved.
Build started 7/31/2015 4:38:49 AM.
Project "D:\CQF\build.targets" on node 1 (default targets).
TestInit:
1 Value of flag: true
BuildApp:
2 Value of flag: false
target2:
Inside run target2
Done Building Project "D:\CQF\build.targets" (default targets).
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.06
The value of TestInitResult set in target "TestInit" is not visible in target "BuildApp" when it returns.
Please let me know how to make TestInitResult visible in parent target.
Or is there better approach to achieve the required result.
What I want to achieve is, first TestInit target should run, then target2 should run, and target RunBuild should run only if TestInit was successful. If TestInit failed, only run target2, skip RunBuild.
Thank you.

Related

Target runs even if dependency target condition false

I was surprised that a target runs even if the target it's after (via AfterTargets) does not:
<Target Name="StepOne" AfterTargets="PostBuildEvent" Condition="false">
<Message Text="StepOne" Importance="high"/>
</Target>
<Target Name="StepTwo" AfterTargets="StepOne">
<Message Text="StepTwo" Importance="high"/>
</Target>
Output:
1>StepTwo
Any way to make a chain of targets that stops when one of them has a false condition? Adding DependsOnTargets="StepOne" didn't help. CallTarget works but then properties aren't shared with subsequent targets, which I want.
MSBuild creates a dependency graph of all of the targets. The targets will then be invoked in order. Conditions don't change the dependency graph and conditions are not checked until the target is invoked.
The chain of targets doesn't stop because one of the targets has a false condition.
But a target can set properties that are used in the conditions of other targets. For example:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" TreatAsLocalProperty="IsStepOneEnabled">
<PropertyGroup>
<!-- IsStepOneEnabled: default to true if not set; normalize to true|false -->
<IsStepOneEnabled Condition="'$(IsStepOneEnabled)' == ''">true</IsStepOneEnabled>
<IsStepOneEnabled Condition="'$(IsStepOneEnabled)' != 'true'">false</IsStepOneEnabled>
<!-- IsStepOne: initilize to false -->
<IsStepOne>false</IsStepOne>
</PropertyGroup>
<Target Name="Test">
<Message Text="Test" />
<Message Text="Step One will be run." Condition="$(IsStepOneEnabled)"/>
</Target>
<Target Name="StepOne" AfterTargets="Test" Condition="$(IsStepOneEnabled)">
<PropertyGroup>
<IsStepOne>true</IsStepOne>
</PropertyGroup>
<Message Text="StepOne" />
</Target>
<Target Name="StepTwo" AfterTargets="StepOne" Condition="$(IsStepOne)">
<Message Text="StepTwo" />
</Target>
</Project>
Save this in a file named test.proj and run it as with the command:
msbuild test2.proj
and the output will be:
Test:
Test
Step One will be run.
StepOne:
StepOne
StepTwo:
StepTwo
Run it with the command:
msbuild test2.proj /p:IsStepOneEnabled=false
and the output will be:
Test:
Test

MSBuild /m:4 fails because it builds the same project twice

My team has a large solution (~500 csproj's). We use VS2012, and build using TFS Build, which uses MSBuild 4. Currently we build serially, but we want to build in parallel (using msbuild /maxcpucount:4). However, when I try it on my 4-proc machine, I get a weird failure:
11:2>CSC : fatal error CS0042: Unexpected error creating debug information file 'C:\Common\obj\Debug\Common.PDB' -- 'C:\Common\obj\Debug\Common.pdb: The process cannot access the file because it is being used by another process. [C:\Common\Common.csproj]
Looking at the log, 2 msbuild nodes were trying to build that same csproj, and thus colliding on writing some output:
10>Project "C:\Utils\Utils.csproj" (10) is building "C:\Common\Common.csproj" (11) on node 4 (default targets).
46:2>Project "C:\Objects\Objects.csproj" (46:2) is building "C:\Common\Common.csproj" (11:2) on node 1 (default targets).
Why would MSBuild try to build the same project twice?
Cause: Someone was calling <MSBuild Projects="Common.csproj" Properties="..." />. Then, MSBuild thinks that it should build Common.csproj again with those different properties, and it happened to occur at the same time with the regular compilation of Common.csproj.
Fix: Call <MSBuild ... /> without those unneeded properties.
Test:
Common.targets
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build">
<Message Importance="high" Text="Build in $(MSBuildThisFile)" />
</Target>
<Target Name="After" DependsOnTargets="Build">
<Message Importance="high" Text="After in $(MSBuildThisFile)" />
</Target>
</Project>
Other.targets
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Build">
<Message Importance="high" Text="Build in $(MSBuildThisFile)" />
<MSBuild Projects="common.targets" Targets="Build" /> <!-- regular builds -->
<MSBuild Projects="common.targets" <!-- custom invocation with properties -->
Targets="After"
Properties="myprop=myvalue"
/>
</Target>
</Project>
Run:
> msbuild other.targets /clp:verbosity=minimal
Build in other.targets
Build in common.targets
Build in common.targets <<<< Common.targets Build is invoked again
After in common.targets
And indeed, removing Properties="myprop=myvalue" solves the issue.
I found someone had added two project references (from the same project) and that apparently caused msbuild to build twice also.. something to watch out for

TeamCity MSBuild refer to build counter

I have a property group which includes a property for the build_number which is being passed in from TeamCity as solely the Build Counter. The build number format being set in TeamCity as simply {0} for the counter.
<PropertyGroup>
<Major>10</Major>
<Minor>1</Minor>
<Build>$(BUILD_NUMBER)</Build>
<Release>0</Release>
...
</PropertyGroup>
The Major, Minor and Release properties are then updated from values in a file in source control.
So that TeamCity logs the build as the full 4 part build reference (not just the counter), I set it thus:
<TeamCitySetBuildNumber BuildNumber="$(Major).$(Minor).$(Build).$(Release)" />
However, now when I reference my $(Build) property, it's now set to the 4 part build reference, and any property I have made which makes reference to $(BUILD_NUMBER) prior to setting using TeamCitySetBuildNumber also gets overwritten with the 4 part reference.
NB I've also changed it with a system message:
<Message Text="##teamcity[buildNumber '$(Major).$(Minor).$(Build).$(Release)']" />
but the overall effect is the same.
How Can I refer to the build counter (only) AFTER I have set the BuildNumber above?
If you're using a project file, you could try calling the TeamCitySetBuildNumber command in the AfterBuild section of the *.vbproj or *.csproj file:
<Target Name="AfterBuild">
<TeamCitySetBuildNumber BuildNumber="$(Major).$(Minor).$(Build).$(Release)" />
</Target>
If you're using a solution file, I'd create a *.proj file that calls your solution file and then after that call the TeamCitySetBuildNumber command (not sure if you can call the TeamCitySetBuildNumber command within the target like this though...):
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="SetBuildNumber">
<PropertyGroup>
<Major>10</Major>
<Minor>1</Minor>
<Build>$(BUILD_NUMBER)</Build>
<Release>0</Release>
</PropertyGroup>
<Target Name="Build">
<Message Text="Build task called... " Importance="high"/>
<MSBuild Projects="$(teamcity_build_checkoutDir)\your_solution.sln" Properties="Configuration=Release"/>
</Target>
<Target Name="SetBuildNumber" DependsOnTargets="Build">
<Message Text="Setting build number back to TeamCity... " Importance="high"/>
<TeamCitySetBuildNumber BuildNumber="$(Major).$(Minor).$(Build).$(Release)" />
</Target>
</Project>

Log4Net configuration error causing MSBuild to fail

I'm trying to set up a CI environment at a new client site using Team City and MSbuild and the MS build community extensions. Compiling the code seems to work fine. However, when I run my unit tests I get the following error coming from the NUnit task:
log4net : error XmlConfigurator: Failed to find configuration section 'log4net' in the application's .config file.
I've identified the two test projects that are causing this issue. However, I've ran the tests directly from nunit-console, and the resharper nunit test runner and though I see the warning the tests don't fail. I don't want to do anything with the Log4net configuration file or the assembly.cs in any project. All I want to do is make the MSBuild script behave like Visual Studio which doesn't consider the log4net error as a failure.
Here's the build file
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0" DefaultTargets="Compile">
<Import Project=".\MSBuild.Community.Tasks.Targets"/>
<PropertyGroup>
<Configuration Condition="'$(Configuration)' == ''"> Debug</Configuration>
</PropertyGroup>
<ItemGroup>
<BuildArtifacts Include=".\build_artifacts\"/>
<SolutionFile Include ="..\Core.Services.sln"/>
<NUnitPath Include="..\Packages\NUnit.2.5.10.11092\tools"/>
</ItemGroup>
<Target Name="Clean">
<RemoveDir Directories="#(BuildArtifacts)"/>
</Target>
<Target Name="Init" DependsOnTargets="Clean">
<MakeDir Directories="#(BuildArtifacts)"/>
</Target>
<Target Name="Compile" DependsOnTargets="Init">
<MSBuild
Projects="#(SolutionFile)"
Targets="Rebuild"
Properties="OutDir=%(BuildArtifacts.FullPath)">
</MSBuild>
</Target>
<Target Name="DevelopmentBuild" DependsOnTargets="Compile">
<Message Text="Running Unit Tests from %(BuildArtifacts.FullPath)...." ContinueOnError="true"></Message>
<CreateItem Include="%(BuildArtifacts.FullPath)*.Tests.dll">
<Output TaskParameter="Include" ItemName="TestAssembly" />
</CreateItem>
<NUnit Assemblies="#(TestAssembly)"
ToolPath="#(NUnitPath)\"
ContinueOnError="false"
OutputXmlFile="%(BuildArtifacts.FullPath)test-results.xml"
DisableShadowCopy="true"/>
</Target>
</Project>

MSBUILD AdditionalProperties not respected in teamcity

I have the following piece of master msbuild script which triggers child scripts with the respective properties.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build" ToolsVersion="3.5">
<PropertyGroup>
<BuildLabel>0.8.1.2</BuildLabel>
</PropertyGroup>
<Target Name="Build" >
<CallTarget Targets="BuildApplication"/>
</Target>
<Target Name="BuildApplication" >
<ItemGroup Condition="'$(Configuration)'==''">
<ProjectToBuild Include="./Application/Application.msbuild">
<AdditionalProperties>Configuration=Publish - Beta</AdditionalProperties>
</ProjectToBuild>
<ProjectToBuild Include="./Application/Application.msbuild">
<AdditionalProperties>Configuration=Publish - Production</AdditionalProperties>
</ProjectToBuild>
</ItemGroup>
<MSBuild Projects="#(ProjectToBuild)" Properties="BuildLabel=$(BuildLabel);Platform=Any CPU" />
</Target>
</Project>
While this script works fine on my local as well as the build server, it does NOT work( $(Configuration) is not available to child as shown by teamcity logs) when the same build server checks out the code and runs the script.
what could be the problem?
The tools version in teamCity was set to default due to which the child scripts were being called using MSbuild 2.0.Changing it to 4.0 fixed it