MSBuild conditional Exec? - post

I am building various projects using the <MSBuild Projects="... markup. I am then executing some command line tools after the project is built.
E.g
<Target Name="Name">
<MSBuild Projects="" />
<Exec Command="" />
</Target>
I notice that the project is only built as required and get the following output when the build script is run: "Skipping target "CoreCompile" because all output files are up-to-date". This is great but how do I make my <Exec... commands use the same condition so that they are only run when necessary as well?
Update: I've implemented gregmac's suggestion but its still executing the command regardless. This is what I've got now:
<Target Name="Name">
<MSBuild Projects="">
<Output TaskParameter="TargetOutputs" ItemName="AssembliesBuiltByChildProjects" />
</MSBuild>
<Exec Command="" Condition="'#(AssembliesBuiltByChildProjects)'!=''" />
Any further help is much appreciated. This is a bit of a sticking point for me.
Thanks for any tips.
Alan

You should be able to use the TargetOutputs parameter:
<MSBuild Projects="" >
<Output TaskParameter="TargetOutputs" ItemName="AssembliesBuiltByChildProjects" />
</MSBuild>
<Message Text="Assemblies built: #(AssembliesBuiltByChildProjects)" /> <!-- just for debug -->
<Exec Command="" Condition="'#(AssembliesBuiltByChildProjects)'!=''" />

If you can add the following to each of your projects:
<Target Name="DoStuffWithNewlyCompiledAssembly">
<Exec Command="" />
</Target>
... then you only need to add a property:
<Target Name="Name">
<MSBuild Projects="" Properties="TargetsTriggeredByCompilation=DoStuffWithNewlyCompiledAssembly" />
</Target>
This works because someone smart at Microsoft added the following line at the end of the CoreCompile target in Microsoft.[CSharp|VisualBasic][.Core].targets (the file name depends on the language and MSBuild/Visual Studio version).
<CallTarget Targets="$(TargetsTriggeredByCompilation)" Condition="'$(TargetsTriggeredByCompilation)' != ''"/>
So if you specify a target name in the TargetsTriggeredByCompilation property, your target will run if CoreCompile runs-- and your target will not run if CoreCompile is skipped (e.g. because the output assembly is already up-to-date with respect to the code).

You are asking wrong question.
Exec does not have any condition, But you can have condition on Target element which can be used like this.
<Target Name="Name" Condition="#(AssembliesBuiltByChildProjects)'!=''">
<MSBuild Projects="">
<Output TaskParameter="TargetOutputs" ItemName="AssembliesBuiltByChildProjects" />
</MSBuild>
<Exec Command=""/>
</Target>
<Target Name="Name" Condition="#(AssembliesBuiltByChildProjects)'==''">
...
</Target>

I did manage to find a solution to fit my needs although it may not be the optimal solution.
See my answer to my other question here: MSBuild Post-Build
Thanks,
Alan

Related

jdtCompileLogPublisher javac

I have ant build script that compiles and generates the compilation logs(in file compileLog.xml) of my project. I use jdtCompileLogPublisher to publish these results the RTC.
Now the situation is when i have the sources in the project its working fine. When i don't have any sources in the project(this is a one of the requirement that there will be an empty project with out sources), compilation step is skipped as there are no sources,and jdtCompileLogPublisher is failing because, the file compileLog.xml which is an input for this step is not created as compilation itself has been skipped.
I tried creating a new compileLog.xml if it is not available. As a result of this I'm encountering new issues as file is empty.
Why javac is skipped when there are no java files to compile?
Is there any possibility to make the compiler to execute javac in build.xml
Version ant: 1.7.1.
snippet of my code related to :
<target name="compile" depends="prepare" description="Compile the source code">
<mkdir dir="${nameBuild}/${dir.build}/bin" />
<!-- Add target ${label.jdkversion} to make it possible to build with a certain jdk version-->
<javac compiler="org.eclipse.jdt.core.JDTCompilerAdapter" destdir="${nameBuild}/${dir.build}/bin" debug="true" deprecation="on" encoding="ISO-8859-1" source="${buildInfo.jdkVersion}" target="${buildInfo.jdkVersion}" debuglevel="lines,source" failonerror="false" errorProperty="buildFailed">
<compilerarg line="-warn:+raw" />
<compilerarg line="-warn:-serial" />
<compilerarg line="-enableJavadoc" />
<compilerarg line="-log source/${name}/compileLog.xml" />
<src path="${name}/${dir.src}" />
<classpath refid="application.classpath" />
</javac>
<jdtCompileLogPublisher buildResultUUID="${buildInfo.buildResultUUID}" repositoryAddress="${repositoryAddress}" userId="${userId}" passwordFile="${psFile}" filePath="compileLog.xml" />
<fail if="buildFailed" message="Compile ${name} failed." />
</target>
I modified the tag
<jdtCompileLogPublisher buildResultUUID="${buildInfo.buildResultUUID}" repositoryAddress="${repositoryAddress}" userId="${userId}" passwordFile="${psFile}" filePath="compileLog.xml" />
to
<jdtCompileLogPublisher buildResultUUID="${buildInfo.buildResultUUID}" repositoryAddress="${repositoryAddress}" userId="${userId}" passwordFile="${psFile}" filePath="compileLog.xml" failonerror="false" />
Which solved my problem

Msbuild ignores continueonerror?

I am trying to invoke MSbuild for a couple of projects twice. First time without any clean, but if this fails I will invoke a clean followed by a new build. (The reasoning is that I want my build to be fast but if that fails fall back to clean, restore nuget packages build etcetera). This works but the build will still fail if the first call failed (it has continueonerror set to true so I dont want it to fail...). Here are the relevant parts of the build file:
<ItemGroup>
<ProjectsToPublish Include="X.sln" />
</ItemGroup>
<Target Name="RestoreAllPackages">
<Message Text="#(ProjectsToPublish)" />
<Exec Command='"$(MSBuildProjectDirectory)\.nuget\nuget.exe" restore "$(MSBuildProjectDirectory)\%(ProjectsToPublish.Identity)"'
/>
</Target>
<Target Name="Build" >
<MSBuild Projects="#(ProjectsToPublish)" Properties="SkipRestore=True;RunCodeAnalysis=False;Retries=10;RetryDelayMilliseconds=50"
BuildInParallel="true" ContinueOnError="WarnAndContinue" />
<!-- MSBuildLastTaskResult outcome of previous task-->
<PropertyGroup>
<FastBuildFailed>false</FastBuildFailed>
<FastBuildFailed Condition="'$(MSBuildLastTaskResult)' == 'false'" >true</FastBuildFailed>
</PropertyGroup>
<Message Importance="high" Text="Initial build failed? $(FastBuildFailed)" />
<Message Importance="high" Text="Initial build failed will retry" Condition="'$(FastBuildFailed)'" />
<CallTarget Targets="FullBuild" Condition="'$(FastBuildFailed)'" />
</Target>
<Target Name="FullBuild" DependsOnTargets="RestoreAllPackages">
<!--Fake property below to reexecute build exact same properties prevents the build lform re-->
<MSBuild Projects="#(ProjectsToPublish)" Properties="SkipRestore=True;RunCodeAnalysis=False;FakeProperty=one" BuildInParallel="true" />
</Target>
<Target Name="RestoreAllPackages">
<Message Text="#(ProjectsToPublish)" />
<Exec Command='"$(MSBuildProjectDirectory)\.nuget\nuget.exe" restore "$(MSBuildProjectDirectory)\%(ProjectsToPublish.Identity)"'
/>
</Target>
For anyone with this rather exotic error. It seems that when a build is run inside of a TFS build server, the build server parses log output and will fail a build even with a ContinueOnError setting. My workaround ended up being <Exec Command="$(MSBuildBinPath)\msbuild.exe #(ProjectsToPublish) /noconlog " ContinueOnError="true" />
Basically spawning a new MSBuild using Exec and making sure that it did not output anything using /noconlog
Rather than using <OnError..., you can assign ContinueOnError the value 'WarnAndContinue', and then use a condition to check the $(MSBuildLastTaskResult) property.
Generic example:
<Error ContinueOnError="WarnAndContinue" />
<Message Importance="High" Text ="$(MSBuildLastTaskResult)" />
<!-- (Returns false) -->
(I believe both "WarnAndContinue" and $(MSBuildLastTaskResult) were introduced in MSBuild 4.0; they should be available on your TFS 2012 build server.)

Ant fixCRLF target seems to alter other characters

I'm trying to create an Ant target to convert all line endings in a project to CRLF
<target name="eol-conversion">
<echo message="Converting EOL" />
<property name="workspace.root" location="../../.." />
<property name="theproject" location="${workspace.root}/theproject" />
<echo message="${theproject}" />
<fixcrlf srcdir="${theproject}" includes="**/*.fileext" eol="crlf" />
</target>
It finds the target directory ok, but it changes all occurrences of (£) to (�) when I run the target.
I'm not sure what I've done wrong as I've basically just taken one of the examples from the Ant Apache manual.
Nevermind! I found that adding encoding="ISO-8859-1" into the fixcrlf tag fixed the issue.

How do I setup a TFS 2010 Workflow build process similar to my current NAnt process

I have a build process is in NAnt and resembles the following structure (a lot has been abreviated). I'm having some confussion on what I can put into Workflow from my NAnt Script and what I should convert to MSBuild(or not). Various targets within the NAnt script will set properties, copy files, delete files, make calls to external processes such as compiling VB6 projects. Any tips on porting this over to workflow would be greatly appreciated. Please let me know if you have questions.
<?xml ... >
<project ...>
<!-- Get and combine paths -->
<properties name="" value=""/>
<properties name="" value=""/>
<properties name="" value=""/>
.
.
.
.
<target name="Main">
<!--Set Log Folder Name to include date and time.
<mkdir dir="${LogDir}"/>
<call target="DeleteTicketsFile"/>
<call target="GetTickets"/>
<call target="WriteTicketsToFile"/>
<call target="WriteProperties"/>
<call target="DeleteFolders" failonerror="true"/>
<call target="GetLatest" failonerror="true"/>
<call target="BuildDOTNETSolution" failonerror="true"/>
<call target="BuildVB6Projects" failonerror="true"/>
.
.
.
<target name="BuildDOTNETSolution">
<property name="ProjectName" value="Localcache" />
<echo message="VCVarsAllBatFile = ${VCVarsAllBatFile}"/>
<exec program="${VCVarsAllBatFile}"/>
<property name="dotnetSlnFile" value="${path::combine(ProductDir, 'dot.net.sln')}"/>
<property name="dotnetOutFile" value="${path::combine(LogDir, 'dotnet.out.txt')}"/>
<echo message="dotnetSlnFile = ${dotnetSlnFile}"/>
<echo message="dotnetOutFile = ${dotnetOutFile}"/>
<delete file="${dotnetOutFile}" if="${file::exists(dotnetOutFile)}" failonerror="false"/>
<!-- Build .NET solution in Release mode -->
<exec program="${DevenvExe}">
<environment>
<variable name="COMSUPPORT" value="N"/>
<variable name="COPYEXECENV" value="N"/>
</environment>
<arg value='"${dotnetSlnFile}"'/>
<arg value='/Rebuild "Release|Any CPU"'/>
<arg value='/Out "${dotnetOutFile}"'/>
</exec>
</target>
</project>
My suggestion is to replace NAnt entirely and use TFS Build Workflow to customize and maintain your build. That said, if your NAnt script it pretty involved that can take some time (especially if you're new to TFS Workflow-based builds). My suggestion to start is to simply have the Workflow Build use the InvokeProcess Activity to execute the NAnt build via the command-line (nant.exe). Then you can incrementally move pieces of the build out of the NAnt script into the workflow.
For example, the entire BuildDotNETSolution target looks like it doesn't do anything special, all this is already included in the DefaultTemplate.xaml build workflow, you simply specify the SolutionsToBuild argument in the Build Definition.

ANT: Using conditional tags, <IF>

I would like to do something like this
<target name="clean" description="clean">
<if>
<available file="${build}" type="dir" />
<then>
<delete dir="${build}" />
</then>
</if>
</target>
As per suggestions found on stackoverflow.com, i downloaded ant-contrib-1.0b3.jar and put it into my path
Additionally, under Ant Build configuration, in classpath, i have
When running my ANT script, i still get
BUILD FAILED
C:\Repositories\blah\build.xml:60: Problem: failed to create task or type if
Cause: The name is undefined.
Action: Check the spelling.
Action: Check that any custom tasks/types have been declared.
Action: Check that any <presetdef>/<macrodef> declarations have taken place.
What am i missing? Please advise
Alternately, once can include the following line in your ANT script
<taskdef resource="net/sf/antcontrib/antlib.xml" />
As long as ant-contribs is in your path, nothing else is needed
This solution is a bit cleaner, as one gains access to ALL the tags, not just the ones manually specified
Same issue resolved in that way.
I put at the start of the buid XML file the following lines:
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="your/path/to/ant-contrib-${version}.jar" />
</classpath>
</taskdef>
The standard ant way would be something like =
<target name="check">
<condition property="delbuild">
<available file="${build}" type="dir"/>
</condition>
</target>
<target name="delbuild" depends="check" if="delbuild">
<delete dir="${build}"/>
<!-- .. -->
</target>
A snippet with the Ant Plugin Flaka, a recent alternative to antcontrib. Installation in Eclipse as usual via Preferences | Ant | Runtime | Global Entries | ant-flaka-1.02.-1.02.jar =
<project xmlns:fl="antlib:it.haefelinger.flaka">
<!-- some standalone if construct -->
<fl:when test=" '${build}'.isdir ">
<delete dir="${build}"/>
</fl:when>
<!-- some if/then/else construct -->
<fl:choose>
<!-- if -->
<when test=" '${buildtype}' eq 'prod' ">
<!-- then -->
<echo>..starting ProductionBuild</echo>
</when>
<when test=" '${buildtype}' eq 'test' ">
<!-- then -->
<echo>..starting TestBuild</echo>
</when>
<!-- else -->
<otherwise>
<fl:unless test="has.property.dummybuild">
<fail message="No valid buildtype !, found => '${buildtype}'"/>
</fl:unless>
<echo>.. is DummyBuild</echo>
</otherwise>
</fl:choose>
</project>
output with ant -f build.xml -Dbuildtype=prod or
ant -f build.xml -Dbuildtype=prod -Ddummybuild=whatever
[echo] ..starting ProductionBuild
output with typo => ant - build.xml -Dbuildtype=testt
BUILD FAILED
/home/rosebud/workspace/AntTest/build.xml:21: No valid buildtype !, found => 'testt'
output with ant -f build.xml -Ddummybuild=whatever
[echo] .. is DummyBuild
I got the same issue. I resolved in the following way.
Sometimes this might be compatibility problem.
Right click on build.xml.
Go to "Run As" --> 2 Ant... Select Classpath tab check Ant Home version (Sometimes eclipse selects default ant version).
If the version listed is different, then change Ant Home Classpath
to C:\XXXX\ant\X.X.X.
Finally click on the User Entries --> Add External JARS..--> add
ant-contrib.x.x.jar form C:\XXXX\ant\X.X.X\ant-contrib\ directory.
On the tasks tab for your Ant Runtime do you see the 'if' task?
export ANT_HOME=/path/to/ant/apache-ant-1.8.2
I couldn't get it to work until I had ANT_HOME set properly. Java kept picking another ant installed on the box.

Resources