Match subdirectories with wildcards using Apache Ant? - apache

I'm trying to setup a build file and I was curious if you can use wildcards in a property to denote filepaths? Or what a better way to tackle this problem is?
As you can see below I want all the files or directories in ${dirtwo} that start with "foo-" to be resolved, versus having to manually include each directory/file as a property.
<?xml version="1.0" encoding="UTF-8"?>
<project name="core" default="build" basedir=".">
<property name="dirone" value="path/to/dir/one" />
<property name="dirtwo" location="path/to/dir/two/foo-*" />
<target name="phpmd" description="Generate pmd.xml using PHPMD">
<exec executable="phpmd">
<arg line="${dirone},${dirtwo}
xml
codesize,design,naming,unusedcode
--reportfile ${basedir}/build/logs/pmd.xml" />
</exec>
</target>
...
</project>
Currently, all I get are errors no matter how I try to use a wildcard or escape one.
Buildfile: /var/www/server/project/build.xml
phpmd:
[exec] The given file "/var/www/server/project/path/to/dir/two/foo-*" does not exist.
[exec] Result: 1

An Ant DirSet matches directories against includes/excludes patterns. You could combine it with Pathconvert as shown below.
<?xml version="1.0" encoding="UTF-8"?>
<project name="core" default="build" basedir=".">
<property name="mybase.dir" location="/path/to/your/base/dir" />
<dirset dir="${mybase.dir}" includes="**/foo-*" id="directories" />
<pathconvert pathsep=", " property="directory-list" refid="directories" />
<target name="phpmd" description="Generate pmd.xml using PHPMD">
<exec executable="phpmd">
<arg line="${directory-list}
xml
codesize,design,naming,unusedcode
--reportfile ${basedir}/build/logs/pmd.xml" />
</exec>
</target>
</project>
To test the results of dirset and pathconvert, you can use:
<echo message="${directory-list}" />

Related

How can I use MSBuild 'afterbuild' tasks to edit a .config file?

I have a .config in a target project and I need to add a line to it programmatically via an MSBuild task.
Pseduo operations like:
find target .config file
determine the value of attributes for new node (e.g. 'id' and 'version' for 'package' node)
insert new node in correct parent node
save changes
The .config file at $TargetProjectDir\Config\packages.config:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ABC" version="1.1.0.4" />
<package id="XYZ" version="2.0.0.0" />
</packages>
Needs to look like this afterwards:
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ABC" version="1.1.0.4" />
<package id="XYZ" version="2.0.0.0" />
<package id="CarDataWidget" version="3.0.0.0" />
</packages>
So far i've considered using 'inline tasks', the 'EXEC' task and 'XmlPoke' task but haven't managed to get any of them working.
Here is my attempt with XmlPoke and XmlPeek:
I used the following article as an inspiration on how to add nodes to the packages.config file:
http://weblogs.asp.net/bsimser/appending-nodes-in-xml-files-with-xmlpeek-and-xmlpoke-using-nant
<Target Name="AfterBuild" DependsOnTargets="AddPackage">
</Target>
<Target Name="AddPackage">
<!-- Load existing nodes into a Property -->
<XmlPeek XmlInputPath="config/packages.config" Query="/packages/package" >
<Output TaskParameter="Result" PropertyName="Peeked" />
</XmlPeek>
<Message Text="From Peek: $(Peeked)"></Message>
<!-- Load new node into Property -->
<PropertyGroup>
<WidgetName>CarDataWidget</WidgetName>
<WidgetVersion>2.0.0.0</WidgetVersion>
<NewNode><package id="$(WidgetName)" version="$(WidgetVersion)" /></NewNode>
<!-- Concatenate existing and new node into a Property -->
<ConcatenatedNodes>$(Peeked)$(NewNode)</ConcatenatedNodes>
</PropertyGroup>
<Message Text="New pacakges: $(ConcatenatedNodes)"></Message>
<!-- Replace existing nodes with concatenated nodes -->
<XmlPoke Value="$(ConcatenatedNodes)" XmlInputPath="config/packages.config" Query="/packages">
</XmlPoke>
</Target>
The output from the above build is:
1>AddPackage:
1> From Peek: <package id="ABC" version="1.1.0.4" />;<package id="XYZ" version="2.0.0.0" />
1> New pacakges: <package id="ABC" version="1.1.0.4" />;<package id="XYZ" version="2.0.0.0" /><package id="CarDataWidget" version="2.0.0.0" />
1> C:\_dev\CarDataWidget.csproj(184,14):
error MSB4094: "<package id="ABC" version="1.1.0.4" />;<package id="XYZ" version="2.0.0.0" /><package id="CarDataWidget" version="2.0.0.0" />"
is an invalid value for the "Value" parameter of the "XmlPoke" task.
Multiple items cannot be passed into a parameter of type "Microsoft.Build.Framework.ITaskItem".
1>
1>Build FAILED.
THE QUESTION:
How can get it to add to a .config file with existing package nodes???
I had the same problem. I found the solution here.
The problem is than XmlPoke considers semicolon as a value separator.
Should replace this:
<NewNode><package id="$(WidgetName)" version="$(WidgetVersion)" /></NewNode>
With:
<NewNode>&lt%3Bpackage id&#61%3B&quot%3B$(WidgetName)&quot%3B version&#61%3&quot%3$(WidgetVersion)&quot%3 /&gt%3</NewNode>
Must replace each semicolon by the secuence %3B
Here is a way to do it using MSBuild Extension Pack.
Set the packages and versions in the NewPackage item group and it adds them to the XML file.
<Project
ToolsVersion="4.0"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks" />
<Target Name="Test" DependsOnTargets="AddPackage">
</Target>
<ItemGroup>
<NewPackage Include="CarDataWidget">
<Version>3.0.0.0</Version>
</NewPackage>
<NewPackage Include="FooBarWidget">
<Version>1.2.3.4</Version>
</NewPackage>
</ItemGroup>
<Target Name="AddPackage">
<PropertyGroup>
<InputFile>in.xml</InputFile>
<OutputFile>out.xml</OutputFile>
</PropertyGroup>
<Copy SourceFiles="$(InputFile)" DestinationFiles="$(OutputFile)" />
<MSBuild.ExtensionPack.Xml.XmlFile
TaskAction="AddElement"
File="$(OutputFile)"
XPath="//packages"
Element="package"
Key="id"
Value="%(NewPackage.Identity)" />
<MSBuild.ExtensionPack.Xml.XmlFile
TaskAction="AddAttribute"
File="$(OutputFile)"
XPath="//packages/package[#id='%(NewPackage.Identity)']"
Key="version"
Value="%(NewPackage.Version)" />
</Target>
</Project>
Not hoping to wake up an old thread.I had the exact scenario were I had to add new keys to the appsettings section of web.config. I started off with OPs code and was stuck with the same problem with ; in the peeked value preventing the new concatenated value to be written. I fixed it by using Replace function to remove the ;
<ConcatenatedNodes>$(Peeked)$(NewNode)</ConcatenatedNodes>
<!--in the concatenatednode, remove semicolon-->
<ChangedPeek>$(ConcatenatedNodes.Replace(";",""))</ChangedPeek>
<!-- Replace existing nodes with concatenated nodes-->
<XmlPoke XmlInputPath="%(WebConfigFilesSolutionDir.FullPath)" Query="//appSettings" Value="$(ChangedPeek)" />
For the complete answer on how to add a new key to appsetting section of webconfig using MSBuild refer https://stackoverflow.com/a/56760009/6664129
Take a look at my blog post http://sedodream.com/2011/12/29/UpdatingXMLFilesWithMSBuild.aspx which compares the following methods.
Use SlowCheetah to transform the files for you
Use the TransformXml task directly
Use the built in (MSBuild 4.0) XmlPoke task
Use a third party task library

how to set msbuild script path in ccnet.config using cruise control.net

I am getting below execption when i am trying to run script using cruise control.please look into my code and let me know where i am doing mistake
<build date="2013-07-02 16:38:56" buildtime="00:00:00" error="true" buildcondition="ForceBuild">MSBUILD : error MSB1008: Only one project can be specified.
Switch: e:\mybuild.xml
ccnet.config file
<cruisecontrol>
<project name="Visteon">
<webURL>http://localhost:333/ccnet/</webURL>
<triggers>
<intervalTrigger seconds="110" buildCondition="ForceBuild" />
</triggers>
<tasks>
<msbuild>
<executable>C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe</executable>
<workingDirectory>E:\workingproject_5145</workingDirectory>
<projectFile>myproject.sln</projectFile>
<buildArgs>msbuild e:\mybuild.xml /t:Buildrun</buildArgs>
<timeout>120</timeout>
<logger>C:\Program Files (x86)\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
</tasks>
</project>
</cruisecontrol>
my scriptsis defined in mybuild.xml which is below mentioned
<Target Name="GetSource">
<Message Text="Checking out trunk into $(SourceDirectory)" />
<SvnCheckout RepositoryPath="$(SvnCheckoutPath)"
LocalPath="$(CheckOutPath)"
UserName="aa"
Password="aa">
<Output TaskParameter="Revision" PropertyName="Revision" />
</SvnCheckout>
</Target>
<Target Name="Buildrun" DependsOnTargets="GetSource;Clean;" />
<Target Name="Clean">
<!-- Clean, then rebuild entire solution -->
<MSBuild Projects="$(CheckOutPath)\myproject.sln" Targets="Clean;Rebuild" />
</Target>
Your projectFile should be the xml file...(that is a .msbuild file)
I think this is what you want.
<projectFile>mybuild.xml</projectFile>
<buildArgs>/t:Buildrun</buildArgs>
LONG VERSION:
<executable>C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe</executable>
<workingDirectory>E:\workingproject_5145</workingDirectory>
<projectFile>myproject.sln</projectFile>
<buildArgs>msbuild e:\mybuild.xml /t:Buildrun</buildArgs>
So it's gonna concatenate these values
$executable $workingDirectory $projectFile $buildArgs
So you get something like this:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe E:\workingproject_5145\myproject.sln msbuild e:\mybuild.xml /t:Buildrun
which is not what you want.
If you make the values like this:
<executable>C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe</executable>
<workingDirectory>E:\workingproject_5145</workingDirectory>
<projectFile>mybuild.xml</projectFile>
<buildArgs>/t:Buildrun</buildArgs>
You'll get something like this:
C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe "E:\workingproject_5145\mybuild.xml" /t:Buildrun

how to run msbuild script through cruise control

I am getting execption when i am running cruise control by iis or cctray and below is ccnet.config.i wanted to run my scrip through cruise control .please let me know how to relove this issue
<project name="Visteon">
<webURL>http://localhost/ccnet/</webURL>
<triggers>
<intervalTrigger seconds="110" buildCondition="ForceBuild" />
</triggers>
<tasks>
<msbuild>
<executable>C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe
</executable>
<workingDirectory>E:\workingfolder_123</workingDirectory>
<buildArgs>E:\CCnet.xml /p:Configuration=release</buildArgs>
<timeout>1800</timeout>
<!-- 30 minutes -->
<logger>C:\Program Files\CruiseControl.NET\server\
ThoughtWorks.CruiseControl.MSBuild.dll</logger>
</msbuild>
</tasks>
</project>
</cruisecontrol>
my scripts is like this
<Target Name="GetSource">
<Message Text="Checking out trunk into $(SourceDirectory)" />
<SvnCheckout RepositoryPath="$(SvnCheckoutPath)"
LocalPath="$(CheckOutPath)"
UserName="aa"
Password="aa">
<Output TaskParameter="Revision" PropertyName="Revision" />
</SvnCheckout>
</Target>
<Target Name="Build" DependsOnTargets="GetSource;Clean;" />
<Target Name="Clean">
<!-- Clean, then rebuild entire solution -->
<MSBuild Projects="$(CheckOutPath)\SUPPLIER_SOFTWARE.sln" Targets="Clean;Rebuild" />
</Target>
Try using the CruiseControl Template below
<project name="MyCodeFolder Project" queue="MyQueue" queuePriority="1">
<tasks>
<msbuild>
<executable>C:\Windows\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe</executable>
<workingDirectory>D:\Projects\MyCodeFolder</workingDirectory>
<projectFile>CCnet.xml</projectFile>
<buildArgs>/noconsolelogger /nologo /p:Configuration=Release</buildArgs>
<targets>
</targets>
<timeout>4800</timeout>
</msbuild>
</tasks>
As for the build script you will need the root to have Project node and set default target name main as the entry point. Please see below:
<Project DefaultTargets="Main">
<Target Name="Main">
//Do Something
</Target>
</Project>
You are missing project file tag e.g.
<projectFile>your_msbuild_script-here</projectFile>
http://build.sharpdevelop.net/ccnet/doc/CCNET/MsBuild%20Task.html
I'm also not sure what exactly E:\CCnet.xml is. If this is your msbuild file, put it
inside <projectFile/> and try again.
I hope that helps.

Unexpected attribute "BuildInParallel" on element <msbuild>

I am trying to use the BuildInParallel option on MsBuild.
I have an NAnt & NAntContrib script e.g.
<project xmlns="http://nant.sf.net/release/0.90/nant.xsd" name="Css Manager Solution Build" ToolsVersion="3.5" default="deploy">
<target name="clean" description="Delete all previously compiled binaries.">
<delete>
<fileset>
<include name="**/bin/**" />
<include name="**/obj/**" />
<include name="**/*.suo" />
<include name="**/*.user" />
</fileset>
</delete>
</target>
<target name="deploy" description="Build and deploy all targets.">
<msbuild project="CssManager.sln" BuildInParallel="true">
<property name="Configuration" value="${configuration}"/>
<property name="OutDir" value="${bin.output.dir}"/>
</msbuild>
</target>
</project>
but I get this error message:
Unexpected attribute "BuildInParallel" on element <msbuild>
Please advise?
The MSBuild task of nant-contrib doesn't have a BuildInParallel attribute. You'll have to use the Maxcpucount command line argument.
<target name="deploy" description="Build and deploy all targets.">
<msbuild project="CssManager.sln" BuildInParallel="true">
<property name="Configuration" value="${configuration}"/>
<property name="OutDir" value="${bin.output.dir}"/>
<arg value="/maxcpucount:${environment::get-variable('NUMBER_OF_PROCESSORS')}"/>
</msbuild>
</target>
More info on MaxCpuCount
More info on msbuild task

Do I have a way to check the existence of a directory in Ant (not a file)?

How do I check for the existence of a folder using Ant?
We can check the existence of a file, but can we do the same for a folder as well?
You use the available task with type set to "dir".
For example:
<available file="${dir}" type="dir"/>
The standard way to do conditional processing is with the condition task. In the example below, running doFoo will echo a message if the directory exists, whereas running doBar will echo a message unless the directory exists.
The dir.check target is required by both doFoo and doBar, it sets the dir.exists property to true or false depending on the result of the available task. The doFoo target will only run if that propery is set to true and doBar will only run if it is not set or set to false.
<?xml version="1.0"?>
<project name="test" default="doFoo" basedir=".">
<property name="directory" value="c:\test\directory"/>
<target name="doFoo" depends="dir.check" if="dir.exists">
<echo>${directory} exists</echo>
</target>
<target name="doBar" depends="dir.check" unless="dir.exists">
<echo>${directory} missing"</echo>
</target>
<target name="dir.check">
<condition property="dir.exists">
<available file="${directory}" type="dir"/>
</condition>
</target>
</project>
Antelope provides additional tasks, including an If task that can make the processing simpler (and to me, more intuitive), you can download the Antelope tasks from the download page.
Here's a small example incorporating the available element into an if test.
<!-- Test if a directory called "my_directory" is present -->
<if>
<available file="my_directory" type="dir" />
<then>
<echo message="Directory exists" />
</then>
<else>
<echo message="Directory does not exist" />
</else>
</if>
Warning: you need ant-contrib.jar in your ANT_HOME\lib directory otherwise you won't have access to the if elements, and your script will fail with this error:
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.
Here's my solution, which doesn't require setting properties and using targets with 'if' or 'unless':
Macro:
<macrodef name="assertDirAvailable">
<attribute name="dir" />
<sequential>
<fail message="The directory '#{dir}' was expected to be available but is not">
<condition>
<not>
<available file="#{dir}" type="dir" />
</not>
</condition>
</fail>
</sequential>
</macrodef>
Usage:
<assertDirAvailable dir="${dirToCheck}" />
My solution using ANT 1.8 version, older versions may not work due if/unless not supporting ${evalTrueOrFalse} syntax.
<?xml version="1.0" encoding="UTF-8"?>
<project name="DoMagic" default="build" basedir=".">
<property environment="env" />
<property name="name" value="Do the ANT Magic" />
<property name="somedir" value="./must_exist_folder"/>
<tstamp><format property="TODAY" pattern="yyyy-MM-dd HH:mm:ss" /></tstamp>
<target name="doMagic" if="${dir.exists}">
<echo message="Do the magic stuff" />
</target>
<target name="doUsage" unless="${dir.exists}">
<echo message="Do usage and help" />
</target>
<target name="build">
<echo message="Do the magic" />
<condition property="dir.exists" else="false"><available file="${somedir}" type="dir" /></condition>
<echo message="Folder found: ${dir.exists}" />
<antcall target="doCustomize"></antcall>
<antcall target="doUsage"></antcall>
</target>
</project>
ANT 1.6 or early ANT 1.7 does not work, upgrade to ANT 1.8 release.
Target attributes if and unless evaluates ${var} syntax to true/false
Condition attribute else value is set to property if available condition was false, without it variable is not set. NotSet value is not same as an explicit false value.
call any target but if/unless attribute defines whether its actually run
http://ant.apache.org/manual/properties.html#if+unless
[If/Unless] In Ant 1.7.1 and earlier, these attributes could only be property names. As of Ant 1.8.0, you may instead use property expansion. Compared to the older style, this gives you additional flexibility.
Here is another approach, allows to call just one task without using ant-contrib.jar.
<target name="my-task" depends="dir-check">
<antcall target="my-task-install"/>
<antcall target="my-task-update"/>
</target>
<target name="my-task-install" unless="dir.exists" >
{some task}
</target>
<target name="my-task-update" if="dir.exists" >
{another task}
</target>
<target name="dir-check">
<condition property="dir.exists">
<available file="my-dir" type="dir" />
</condition>
</target>
Here is another example involving for loop. Fail if a directory does not exist.
<for list="dir1/, dir2/, dir3/" param="local.dir" >
<sequential>
<fail message="Directory #{local.dir} does not exist">
<condition>
<not>
<available file="#{local.dir}" type="dir" />
</not>
</condition>
</fail>
</sequential>
</for>