Execute selected ant tasks for a selected set of projects - build

I have defined targets in ant for our games, e.g. clean, build-ios, build-android, deploy-ios, deploy-android, etc. Now I would like to define a new set of targets that represent our games, say game1, game2, game3.
My goal is to be able to launch ant with a set of target games and a set of target tasks so that for each selected game, each selected task is executed.
Example pseudo code: Foreach [game1, game3]: clean, build-ios, deploy-ios
How can I achieve this with ant? A requirement would be to define which games and which tasks are selected through targets, not to write them in a file that is altered manually.

The subant task is useful for situations where you have multiple subprojects that share similar structures.
In your main build.xml, define a target that rubs the desired build target on your game subdirectories, along with all of the generalized build targets.
<target name="deploy-all">
<subant target="deploy">
<dirset dir="." includes="game-*" />
</subant>
<echo message="All games deployed." />
</target>
<target name="deploy" depends="deploy-ios,deploy-android">
<echo message="${ant.project.name} build complete." />
</target>
<target name="clean">
<echo message="Cleaning ${ant.project.name}" />
</target>
<target name="build-ios" depends="clean">
<echo message="Building iOS ${ant.project.name}" />
</target>
<target name="build-android" depends="clean">
<echo message="Building Android ${ant.project.name}" />
</target>
<target name="deploy-ios" depends="build-ios">
<echo message="Deploying iOS ${ant.project.name}" />
</target>
<target name="deploy-android" depends="build-android">
<echo message="Deploying Android ${ant.project.name}" />
</target>
Then, in the game-* subdirectories, create a simple build.xml that links back to the common one.
game-1/build.xml:
<project name="game-1" default="build">
<import file="../build.xml" />
<echo message="=== Building Game 1 ===" />
</project>
game-2/build.xml:
<project name="game-2" default="build">
<import file="../build.xml" />
<echo message="=== Building Game 2 ===" />
</project>
Edit: If your build needs to include/exclude certain subprojects based on the user's input or a pre-defined property, you can modify the subant task's nested resource collection to accommodate for this.
<property name="game.includes" value="game-*" />
<property name="game.excludes" value="" />
<subant target="deploy">
<dirset dir="." includes="${game.includes}" excludes="${game.excludes}" />
</subant>
The user can then run a command that optionally passes values for game.includes and/or game.excludes. If these properties are not specified, the values defined above by the property task will be used as defaults.

Related

How can I execute an ant target depending on the value of an environment variable?

I want to execute an ant script on two different computers. Depending on the name of the computer either one of the two targets should be executed. Following doesn't work:
<project name="import" default="all">
<property environment="env"/>
<target name="staging" if="${env.COMPUTERNAME}='STG'">
<echo>executed on staging</echo>
</target>
<target name="production" if="${env.COMPUTERNAME}='PRD'">
<echo>executed on production</echo>
</target>
<target name="all" depends="staging,production" description="STG or PRD"/>
</project>
As I understood, "if" can only be used with property and it checks whether a property is set or not. But is there a way to to make a condition depending on the value of a property?
I would suggest writing an "init" target that sets any conditions you need for later build steps, and also fails the build if certain required properties don't evaluate to what's expected.
For example:
<target name="all" depends="staging,production,init" />
<target name="staging" if="staging.environment" depends="init">
<echo message="executed on staging" />
</target>
<target name="production" if="production.environment" depends="init">
<echo message="executed on production" />
</target>
<target name="init">
<condition property="staging.environment">
<equals arg1="${env.COMPUTERNAME}" arg2="STG" />
</condition>
<condition property="production.environment">
<equals arg1="${env.COMPUTERNAME}" arg2="PRD" />
</condition>
<fail message="Neither staging nor production environment detected">
<condition>
<not>
<or>
<isset property="staging.environment" />
<isset property="production.environment" />
</or>
</not>
</condition>
</fail>
</target>

MSBuild: Do I need Target Rebuild?

I use a build file to build and test my project.
I have a Compile-Target which has this line "Targets = "Rebuild". Do I really need this line? Using Visual Studio I know that I can clean a Solution and build it again, or I can just rebuild the solution.
In my msbuild-file I delete my main folder BuildArtifacts before creating him again. I used this Tutorial and I don't know why he uses Target=Rebuild?
This is my build file:
<Project ToolsVersion="4.0" DefaultTargets="RunUnitTests" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Falls Eigenschaften nicht gesetzt -> Release & Any CPU als default-->
<PropertyGroup>
<!-- ... -->
</PropertyGroup>
<ItemGroup>
<!-- ... -->
</ItemGroup>
<!-- All the stuff go into my main folder -->
<Target Name="Init" DependsOnTargets="Clean">
<MakeDir Directories="#(BuildArtifacts)" />
</Target>
<!-- delete my main folder -->
<Target Name="Clean">
<RemoveDir Directories="#(BuildArtifactsDir)" />
</Target>
<!-- delete NUnit-Files -->
<Target Name="CleanAfter">
<RemoveDir Directories="#(NunitDir)" />
</Target>
<Target Name="Compile" DependsOnTargets="Init">
<MSBuild Projects="#(SolutionFile)"
Targets="Rebuild"
Properties="OutDir=%(BuildArtifactsDir.FullPath);
Configuration=$(Configuration);
Platform=$(BuildPlatform)" />
</Target>
<Target Name="RunUnitTests" DependsOnTargets="Compile">
<Exec Command='"#(NUnitConsole)" "#(UnitTestsDLL)" --result=console-test.xml --work=BuildArtifacts' />
<CallTarget Targets="CleanAfter" />
</Target>
</Project>
This depends on your own needs: do you need the whole solution to be rebuilt or not? Arguably, on a build server, you want to do a complete clean/rebuild after every commit to make sure the codebase is sane. Removing just the output directory (I assume that is what the Clean starget does) doesn't necessarily remove all object files as well since they typically go into the intermediate directory which might not be the same as the output directory.

MSBuild RegEx Assembly Version

Trying to create an MSBuild task that outputs my code to a folder. All is working except the Regular Expression. My code:
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="$(OutDir)$(TargetFileName)">
<Output TaskParameter="Assemblies" ItemName="TheVersion" />
</GetAssemblyIdentity>
<PropertyGroup>
<Pattern>(\d+)\.(\d+)\.(\d+)</Pattern>
<In>%(TheVersion.Version)</In>
<OutVersion>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern)))</OutVersion>
</PropertyGroup>
<ItemGroup>
<OutputFiles Include="$(OutDir)*" Exclude="*.tmp" />
<SolnFiles Include="$(SolutionDir)INDIVIDUAL.txt;$(SolutionDir)LICENSE.txt;$(SolutionDir)README.md" />
</ItemGroup>
<Copy SourceFiles="#(OutputFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\Tests" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(SolnFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\" SkipUnchangedFiles="true" />
</Target>
When I run this, I get this error:
The item "D:\Dache\INDIVIDUAL.txt" in item list "SolnFiles" does not define a value for metadata "OutVersion". In order to use this metadata, either qualify it by specifying %(SolnFiles.OutVersion), or ensure that all items in this list define a value for this metadata.
When I try %(SolnFiles.OutVersion) it comes up blank. I'm doing something dumb here, what is it?
Took me a few to figure it out. PropertyGroup variables are referenced as $(Var) while ItemGroup output variables are #() and GetAssemblyIdentity is %() - so I changed:
<Copy SourceFiles="#(OutputFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\Tests" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(SolnFiles)" DestinationFolder="$(SolutionDir)dache-%(OutVersion)\" SkipUnchangedFiles="true" />
to this:
<Copy SourceFiles="#(OutputFiles)" DestinationFolder="$(SolutionDir)dache-$(OutVersion)\Tests" SkipUnchangedFiles="true" />
<Copy SourceFiles="#(SolnFiles)" DestinationFolder="$(SolutionDir)dache-$(OutVersion)\" SkipUnchangedFiles="true" />
and it worked.

Apache Tomcat 7, ant list = BUILD FAILED

For the simple MVC springapp, I am trying to deploy the web app.
"ant" and "ant deploywar" build commands work fine. It gives error when doing "ant list"
Here is the build.properties
appserver.home=/usr/local/apache-tomcat-7.0.47
appserver.lib=${appserver.home}/lib
deploy.path=${appserver.home}/webapps
tomcat.manager.url=http://localhost:8080/manager
tomcat.manager.username=tomcat
tomcat.manager.password=s3cret
Here is the part related to list in build.xml
<target name="list" description="List Tomcat applications">
<list url="${tomcat.manager.url}"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"/>
</target>
The manager is working in the browser and I can browse the list of applications as well
OK: http://localhost:8080
OK: http://localhost:8080/manager
OK: http://localhost:8080/manager/html
OK: http://localhost:8080/manager/html/list
and when I go to ListApplications from the HTML interface of manager, it goes to
http://localhost:8080/manager/html/list?org.apache.catalina.filters.CSRF_NONCE=BFCB46A8A1735E7DA7FBE4033D6BEC2D
which is fine. But when I do "ant list" via terminal from the root directory of my app, it gives this error,
Buildfile: /Users/saad/Development/springapp/build.xml
list:
BUILD FAILED
/Users/saad/Development/springapp/build.xml:144: java.io.FileNotFoundException: http://localhost:8080/manager/list
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1624)
at org.apache.catalina.ant.AbstractCatalinaTask.execute(AbstractCatalinaTask.java:230)
at org.apache.catalina.ant.AbstractCatalinaTask.execute(AbstractCatalinaTask.java:150)
at org.apache.catalina.ant.ListTask.execute(ListTask.java:51)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.Target.execute(Target.java:390)
at org.apache.tools.ant.Target.performTasks(Target.java:411)
at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1399)
at org.apache.tools.ant.Project.executeTarget(Project.java:1368)
at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
at org.apache.tools.ant.Project.executeTargets(Project.java:1251)
at org.apache.tools.ant.Main.runBuild(Main.java:809)
at org.apache.tools.ant.Main.startAnt(Main.java:217)
at org.apache.tools.ant.launch.Launcher.run(Launcher.java:280)
at org.apache.tools.ant.launch.Launcher.main(Launcher.java:109)
Total time: 0 seconds
When I changed the url in build.properties to
tomcat.manager.url=http://localhost:8080/manager/html
It returns the whole html page of the manager on doing "ant list".
or if
tomcat.manager.url=http://localhost:8080/manager/text
It gives the error:
/Users/saad/Development/springapp/build.xml:144: java.io.IOException: Server returned HTTP response code: 403 for URL: http://localhost:8080/manager/text/list
Its a change from tomcat 6 to tomcat 7. Use ':8080/manager/text' instead of ':8080/manager/' in the tomcat.manager.url in build properties.
I had the same issue. I fixed it by following the below procedure.
It worked for me by changing tomcat.manager.url='http://apache.host123.com:8080/manager/' to
tomcat.manager.url='http://apache.host123.com:8080/manager/text' in build.properties.
My build.xml is below:
<?xml version="1.0"?>
<project name="springapp" basedir="." default="usage">
<property file="build.properties"/>
<property name="src.dir" value="src"/>
<property name="web.dir" value="war"/>
<property name="build.dir" value="${web.dir}/WEB-INF/classes"/>
<property name="name" value="springapp"/>
<path id="master-classpath">
<fileset dir="${web.dir}/WEB-INF/lib">
<include name="*.jar"/>
</fileset>
<!-- We need the servlet API classes: -->
<!-- * for Tomcat 5/6 use servlet-api.jar -->
<!-- * for other app servers - check the docs -->
<fileset dir="${appserver.lib}">
<include name="servlet*.jar"/>
</fileset>
<pathelement path="${build.dir}"/>
</path>
<target name="usage">
<echo message=""/>
<echo message="${name} build file"/>
<echo message="-----------------------------------"/>
<echo message=""/>
<echo message="Available targets are:"/>
<echo message=""/>
<echo message="build --> Build the application"/>
<echo message="deploy --> Deploy application as directory"/>
<echo message="deploywar --> Deploy application as a WAR file"/>
<echo message="install --> Install application in Tomcat"/>
<echo message="reload --> Reload application in Tomcat"/>
<echo message="start --> Start Tomcat application"/>
<echo message="stop --> Stop Tomcat application"/>
<echo message="list --> List Tomcat applications"/>
<echo message=""/>
</target>
<target name="build" description="Compile main source tree java files">
<mkdir dir="${build.dir}"/>
<javac destdir="${build.dir}" source="1.5" target="1.5" debug="true"
deprecation="false" optimize="false" failonerror="true">
<src path="${src.dir}"/>
<classpath refid="master-classpath"/>
</javac>
</target>
<target name="deploy" depends="build" description="Deploy application">
<copy todir="${deploy.path}/${name}" preservelastmodified="true">
<fileset dir="${web.dir}">
<include name="**/*.*"/>
</fileset>
</copy>
</target>
<target name="deploywar" depends="build" description="Deploy application as a WAR file">
<war destfile="${name}.war"
webxml="${web.dir}/WEB-INF/web.xml">
<fileset dir="${web.dir}">
<include name="**/*.*"/>
</fileset>
</war>
<copy todir="${deploy.path}" preservelastmodified="true">
<fileset dir=".">
<include name="*.war"/>
</fileset>
</copy>
</target>
<!-- ============================================================== -->
<!-- Tomcat tasks - remove these if you don't have Tomcat installed -->
<!-- ============================================================== -->
<path id="catalina-ant-classpath">
<!-- We need the Catalina jars for Tomcat -->
<!-- * for other app servers - check the docs -->
<fileset dir="${appserver.lib}">
<include name="catalina-ant.jar"/>
<include name="catalina-ant.jar"/>
<include name="tomcat-coyote.jar"/>
<include name="tomcat-util.jar"/>
</fileset>
<fileset dir="${appserver.home}/bin">
<include name="tomcat-juli.jar"/>
</fileset>
</path>
<taskdef name="install" classname="org.apache.catalina.ant.DeployTask">
<classpath refid="catalina-ant-classpath"/>
</taskdef>
<taskdef name="reload" classname="org.apache.catalina.ant.ReloadTask">
<classpath refid="catalina-ant-classpath"/>
</taskdef>
<taskdef name="list" classname="org.apache.catalina.ant.ListTask">
<classpath refid="catalina-ant-classpath"/>
</taskdef>
<taskdef name="start" classname="org.apache.catalina.ant.StartTask">
<classpath refid="catalina-ant-classpath"/>
</taskdef>
<taskdef name="stop" classname="org.apache.catalina.ant.StopTask">
<classpath refid="catalina-ant-classpath"/>
</taskdef>
<target name="install" description="Install application in Tomcat">
<install url="${tomcat.manager.url}"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"
path="/${name}"
war="${name}"/>
</target>
<target name="reload" description="Reload application in Tomcat">
<reload url="${tomcat.manager.url}"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"
path="/${name}"/>
</target>
<target name="start" description="Start Tomcat application">
<start url="${tomcat.manager.url}"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"
path="/${name}"/>
</target>
<target name="stop" description="Stop Tomcat application">
<stop url="${tomcat.manager.url}"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"
path="/${name}"/>
</target>
<target name="list" description="List Tomcat applications">
<list url="${tomcat.manager.url}/text"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"/>
</target>
<!-- End Tomcat tasks -->
</project>
My build.properties is below:
# Ant properties for building the springapp
appserver.home=D:/apache-tomcat-7.0.42
# for Tomcat 5 use $appserver.home}/server/lib
# for Tomcat 6 use $appserver.home}/lib
appserver.lib=${appserver.home}/lib
deploy.path=${appserver.home}/webapps
tomcat.manager.url='http://apache.localhost.com:8080/manager/text'
tomcat.manager.username=tomcat
tomcat.manager.password=s3cret
Hope these changes will fix your problem.
2 things needed
1) in build.xml
the url has to "${tomcat.manager.url}/text"
<target name="list" description="List Tomcat applications">
<list url="${tomcat.manager.url}/text"
username="${tomcat.manager.username}"
password="${tomcat.manager.password}"/>
</target>
2) in ${appserver.home}/conf/tomcat-users.xml
the manager should have 'manager-script' one of the roles.
<?xml version='1.0' encoding='utf-8'?>
<tomcat-users>
<role rolename="manager"/>
<user username="tomcat" password="s3cret" roles="manager,manager-script"/>
</tomcat-users>

I want to use NAnt's foreach to iterate files in a folder, how to force alphabetic iteration?

I have an NAnt task "ship" to package my current .sql scripts into a build, then name the build with an incrementing int {######} and copy it to a build folder.
I have another NAnt task which executes those build scripts.
They must execute in order, but in my last attempt, they were not. Can I "force" NAnt to work alphabetically?
FAIL:
<fileset basedir="source\tsql\builds\" id="buildfiles">
<include name="*.sql.template.sql" />
<exclude name="*.sql" />
<exclude name="*asSentTo*" />
</fileset>
<foreach item="File" property"filename">
<in refid="buildfiles">
<echo message="${filename}" />
</in>
</foreach>
PASS:
<foreach item="File" property="filename" in="source\tsql\builds">
<do>
<if test="${string::ends-with(filename,'.sql.template.sql')}">
<echo message="${filename}" />
</if>
</do>
</foreach>
To satisfy my curiosity I tried to reproduce the problem with this script:
<?xml version="1.0"?>
<project name="foreach.test" default="foreach.alpha">
<target name="foreach.alpha">
<foreach item="File" in="C:\foo" property="filename">
<do>
<echo message="${filename}" />
</do>
</foreach>
</target>
</project>
The filenames are printed out in alphabetical order. So conventional use of foreach already seems to be the solution to the problem.
Here is how you do it with a fileset
<fileset id="mySet">
<include name="*.sql" />
</fileset>
<copy>
<fileset refid="mySet" />
</copy>
<foreach item="File" property="filename">
<in>
<items refid="mySet" />
</in>
<do>
<echo message="Copied files: ${filename} to directory: ${Folder}." />
</do>
</foreach>