How does Apache Ant resolve duplicate targets - apache

Say I have two targets in ant with the same name in two different build files but, one is imported into the other.
build.xml
<project>
<target name="once">
<echo>once</echo>
</target>
<target name="twice">
<echo>twice-a in build.xml</echo>
</target>
<!-- duplicate target twice imported again from build2.xml -->
<import file="build2.xml"/>
</project>
build2.xml
<project>
<target name="twice">
<echo>twice-a in build2.xml</echo>
</target>
</project>
How does ant resolve duplicate targets ?
If it had been in a single file it would have a duplicate target thrown an error however since its imported its not throwing an error.
When I run ant twice I get
$ ant twice
Buildfile: /Users/nav/Codes/build.xml
twice:
[echo] twice-a in build.xml
BUILD SUCCESSFUL
Total time: 0 seconds
If ant does take the first declaration as the target then why doesn't moving the import statement up in build.xml
<?xml version="1.0"?>
<project>
<!-- import moved to the top -->
<import file="build2.xml"/>
<target name="once">
<echo>once</echo>
</target>
<target name="twice">
<echo>twice-a in build.xml</echo>
</target>
</project>
still outputs the same as
$ ant twice
Buildfile: /Users/nav/Codes/build.xml
twice:
[echo] twice-a in build.xml
BUILD SUCCESSFUL
Total time: 0 seconds

When you assign project names, then you can access the both targets
<project name="build1">
<target name="once">
<echo>once</echo>
</target>
<target name="twice">
<echo>twice-a in build.xml</echo>
</target>
<!-- duplicate target twice imported again from build2.xml -->
<import file="build2.xml"/>
</project>
Build2
<project name="build2">
<target name="twice">
<echo>twice-a in build2.xml</echo>
</target>
</project>
call ant -p
Buildfile: build.xml
Main targets:
Other targets:
build2.twice
once
twice
If not project name is assign the imported target are hidden if they have the same name.

Related

MSBuild batch task output

I have several solutions (plugins) for a project. For each solution there is a defined range of metadata:
<ItemGroup>
<Plugins Include="Plugin1\Plugin1.sln">
<Disabled>false</Disabled>
<ProjectDirectory>plugin1\</ProjectDirectory>
<ProjectName>Plugin1</ProjectName>
</Plugins>
<Plugins Include="Plugin2\Plugin2.sln">
<Disabled>true</Disabled>
<ProjectDirectory>plugin2\</ProjectDirectory>
<ProjectName>Plugin2</ProjectName>
</Plugins>
<Plugins Include="Plugin3\Plugin3.sln">
<Disabled>false</Disabled>
<ProjectDirectory>plugin3\</ProjectDirectory>
<ProjectName>Plugin3</ProjectName>
</Plugins>
</ItemGroup>
I need to build not Disabled plugins by running its own build script and add the result directory to Plugins metadata for subsequent processing (for example: Copy each plugin build output to its own folder).
But I can't find a way to concatenate it.
Below is my target:
<Target Name="BuildPlugin" Inputs="%(Plugins.Identity)" Outputs="%(Plugins.Identity -> %(PluginOutput.Identity))" Returns="%(PluginOutput.Identity)">
<MSBuild
Condition="!%(Disabled)"
Projects='%(ProjectDirectory)BuildProject.target'
Targets="Clean;Build;" >
<Output ItemName="PluginOutput" TaskParameter="TargetOutputs"/>
</MSBuild>
<ItemGroup>
<Plugins Condition="%(ProjectName)=%(Plugins.ProjectName)">
<PluginOutput>%(PluginOutput.Identity)</PluginOutput>
</Plugins>
</ItemGroup>
<Message Text="%(Plugins.ProjectName) %(PluginOutput.Identity)" Condition="%(Plugins.Disabled)" />
</Target>
BuildProject.target returns output directories (Ex:Plugin1\Plugin1\bin\Release\)
In this case buuilding fails with next errors:
error MSB4096: item list "PluginOutput" does not define a value for
metadata "ProjectName". In order to use this metadata, either qualify
it by specifying %(PluginOutput.ProjectName), or ensure that all items
in this list define a value for this metadata.
error MSB4113: Specified condition "%(Plugins.Disabled)" evaluates to
"" instead of a boolean.
But if remove ItemGroup and condition for Message task
<Target Name="BuildPlugin" Inputs="%(Plugins.Identity)" Outputs="%(Plugins.Identity -> %(PluginOutput.Identity))" Returns="%(PluginOutput.Identity)">
<MSBuild
Condition="!%(Disabled)"
Projects='%(ProjectDirectory)BuildProject.target'
Targets="Clean;Build;" >
<Output ItemName="PluginOutput" TaskParameter="TargetOutputs"/>
</MSBuild>
<Message Text="%(Plugins.ProjectName) %(PluginOutput.Identity)" />
</Target>
seems msbuild correctly batches plugins. BuildPlugin target output produced by Message task is:
BuildPlugin:
Plugin1
Plugin1\Plugin1\bin\Release\
BuildPlugin:
Plugin2
BuildPlugin:
Plugin3
Plugin3\Plugin3\bin\Release
But in this case I don't have any ability to filter disabled plugins and add plugins output folder to metadata.
Any ideas?
The following should work
<Target Name="BuildPlugin" Outputs="%(Plugins.Identity -> %(PluginOutput.Identity))" Returns="%(PluginOutput.Identity)">
<MSBuild
Condition="!%(Plugins.Disabled)"
Projects='%(Plugins.ProjectDirectory)BuildProject.target'
Targets="Clean;Build;" >
<Output ItemName="PluginOutput" TaskParameter="TargetOutputs"/>
</MSBuild>
<Message Text="%(Plugins.ProjectName) %(PluginOutput.Identity)" Condition="!%(Plugins.Disabled)"/>
</Target>
I couldn't test all steps, because of missing BuildProject.target, but this should work:
<Target Name="BuildPlugin"
Inputs="%(Plugins.Identity)"
Outputs="%(Plugins.Identity)\dummy.txt"
Returns="%(PluginOutput.Identity)">
<PropertyGroup> <!-- transform Metadata to Properties -->
<ProjectName>%(Plugins.ProjectName)</ProjectName>
<PluginDisabled>%(Plugins.Disabled)</PluginDisabled>
</PropertyGroup>
<MSBuild
Condition="'$(PluginDisabled)' != 'true'"
Projects='%(Plugins.ProjectDirectory)BuildProject.target'
Targets="Clean;Build;" >
<Output ItemName="PluginOutput" TaskParameter="TargetOutputs"/>
</MSBuild>
<ItemGroup>
<!-- batching 2 ItemGroups in one task is usually not working or creates side effects-->
<Plugins Condition="'%(PluginOutput.ProjectName)' == '$(ProjectName)'">
<PluginOutput>%(PluginOutput.Identity)</PluginOutput>
</Plugins>
</ItemGroup>
<!-- batching 2 ItemGroups in one task is usually not working or creates side effects-->
<Message Text="$(ProjectName) %(PluginOutput.Identity)" Condition="$(PluginDisabled)" />
</Target>

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

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

Msbuild compile website without placing the site in IIS

I am trying to create a msbuild script that will compile and place a test app into a folder on my desktop. I do not want this app published to IIS. I have followed several blgos and looked through hashimi's book but I still cannot figure this out. Below is my script. Thank you very much!
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Clean">
<ItemGroup>
<BinFiles Include="bin\*.*" />
</ItemGroup>
<Delete Files="#(BinFiles)" />
</Target>
<Target Name="Compile" DependsOnTargets="Clean">
<MSBuild Projects="test.vbproj"/>
</Target>
<Target Name="Publish" DependsOnTargets="Compile">
<RemoveDir Directories="$(OutputFolder)"
ContinueOnError="true"/>
<MSBuild Projects="test.vbproj"
targets="ResolveReferences;_CopyWebApplication"
Properties="WebProjectOutputdir=$(OutputFolder; OutDir=$WebProjectOutputDir)\"/>
</Target>
</Target>
</Project>
Your script is a bit awkward (you redefined the clean target to do the same as the the basic clean target).
I'm pretty sure your problem comes from the CopyWebApplication which does lots of stuff according to the properties set in your project file and pass by command line.
Can you try the following script :
<Project DefaultTargets="Compile" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Compile">
<MSBuild
Projects="test.vbproj"
Targets="Clean;Build"
Properties="OutputPath=C:\tmp"/>
</Target>
</Project>
if your test project is a website then the build target should create it on the folder specified in the OutputPath/OutDir property