Can I make the Ant copy task OS-specific? - ant

I have an Ant script that performs a copy operation using the 'copy' task. It was written for Windows, and has a hardcoded C:\ path as the 'todir' argument. I see the 'exec' task has an OS argument, is there a similar way to branch a copy based on OS?

I would recommend putting the path in a property, then setting the property conditionally based on the current OS.
<condition property="foo.path" value="C:\Foo\Dir">
<os family="windows"/>
</condition>
<condition property="foo.path" value="/home/foo/dir">
<os family="unix"/>
</condition>
<fail unless="foo.path">No foo.path set for this OS!</fail>
As a side benefit, once it is in a property you can override it without editing the Ant script.

The previously posted suggestions of an OS specific variable will work, but many times you can simply omit the "C:" prefix and use forward slashes (Unix style) file paths and it will work on both Windows and Unix systems.
So, if you want to copy files to "C:/tmp" on Windows and "/tmp" on Unix, you could use something like:
<copy todir="/tmp" overwrite="true" >
<fileset dir="${lib.dir}">
<include name="*.jar" />
</fileset>
</copy>
If you do want/need to set a conditional path based on OS, it can be simplified as:
<condition property="root.drive" value="C:/" else="/">
<os family="windows" />
</condition>
<copy todir="${root.drive}tmp" overwrite="true" >
<fileset dir="${lib.dir}">
<include name="*.jar" />
</fileset>
</copy>

You could use the condition task to branch to different copy tasks... from the ant manual:
<condition property="isMacOsButNotMacOsX">
<and>
<os family="mac"/>
<not>
<os family="unix"/>
</not>
</and>

Declare a variable that is the root folder of your operation. Prefix your folders with that variable, including in the copy task.
Set the variable based on the OS using a conditional, or pass it as an argument to the Ant script.

You can't use a variable and assign it depending on the type? You could put it in a build.properties file. Or you could assign it using a condition.

Ant-contrib has the <osfamily /> task. This will expose the family of the os to a property (that you specify the name of). This could be of some benefit.

Related

How to conditionally specify a compiler argument in the javac task

I'm trying to write a target like so
<!-- Properties that must be set when invoking via antcall:
DestinationDir Directory to compile classes to.
ReleaseVer Java version this is being compiled for (i.e. 8, 9, etc). Must be at least 8.
Debug Whether to compile with debug information. Either 'on' or 'off'.
-->
<target name="java-support-compile" description="compile JavaSupport source">
<mkdir dir="${DestinationDir}"/>
<condition property="ModulesSupported">
<not>
<equals arg1="${ReleaseVer}" arg2="8" />
</not>
</condition>
<!-- Compile GSSUtilWrapper separately, since we can't use the 'release' option when referencing the sun.security.jgss.GSSUtil package,
and we need to add an --add-exports option to access the java.security.jgss module for ${ReleaseVer} > 9 -->
<javac srcdir="${javasupport.src.dir}" source="1.${ReleaseVer}" target="1.${ReleaseVer}" debug="${Debug}" destdir="${DestinationDir}">
<include name="${GSSUtilWrapperFile}"/>
<compilerarg if="${ModulesSupported}" line="--add-exports java.security.jgss/sun.security.jgss=ALL-UNNAMED" />
</javac>
<javac srcdir="${javasupport.src.dir}" release="${ReleaseVer}" debug="${Debug}" destdir="${DestinationDir}">
<exclude name="${GSSUtilWrapperFile}"/>
</javac>
</target>
The error I'm getting is compilerarg doesn't support the "if" attribute. I need it to be conditional as if I pass in ReleaseVer=8, I get the error error: option --add-exports not allowed with target 8
I got that syntax from http://ant-contrib.sourceforge.net/compilerarg.html, but I didn't realize this wasn't in core ant (and I don't want to install anything else if possible).
How can I do this in standard ant?
One option, perhaps not the cleanest, would be to modify your <condition> task to set the text of the compiler arg, rather than a boolean, and use that. Something like this:
<condition property="javac_arg"
value="--add-exports java.security.jgss/sun.security.jgss=ALL-UNNAMED"
else="">
<not>
<equals arg1="${ReleaseVer}" arg2="8" />
</not>
</condition>
<javac compiler="modern" srcdir="." includeantruntime="no">
<compilerarg line="${javac_arg}" />
</javac>
Note the else parameter, which ensures the arg passed to javac is empty when the condition is false.

How to check if Ant is executed under certain path?

How can I use Ant to verify that the current working directory is located (arbitrarily deeply nested) under a certain path? For example, I want to execute a target only if the current directory is part of /some/dir/, for example if Ant is executed in directory /some/dir/to/my/project/.
The best I could come up with is a String contains condition:
<if>
<contains string="${basedir}" substring="/some/dir/"/>
<then>
<echo>Execute!</echo>
</then>
<else>
<echo>Skip.</echo>
</else>
</if>
This works for my current purpose but I'm afraid it might break some time in the future... for example when a build is executed in path /not/some/dir/ which is also contains the specified directory string.
Are there any more robust solutions like a startsWith comparison or even better a file system based check...?
There is no specific startswith condition in native Ant, but there is a matches condition that takes regular expressions.
As a side note, ant-contrib is rarely necessary for most build scripts, and will often lead to unreliable code. I would strongly recommend avoiding it.
Here's a sample script to illustrate how you can use the matches condition with native Ant. The test target is, of course, just for demonstration.
<property name="pattern" value="^/some/dir" />
<target name="init">
<condition property="basedir.starts.with">
<matches pattern="${pattern}" string="${basedir}" />
</condition>
</target>
<target name="execute" depends="init" if="basedir.starts.with">
<echo message="Executing" />
</target>
<target name="test">
<condition property="dir1.starts.with">
<matches pattern="${pattern}" string="/some/dir/" />
</condition>
<condition property="dir2.starts.with">
<matches pattern="${pattern}" string="/some/dir/to/my/project/" />
</condition>
<condition property="dir3.starts.with">
<matches pattern="${pattern}" string="/not/some/dir/" />
</condition>
<echo message="${dir1.starts.with}" />
<echo message="${dir2.starts.with}" />
<echo message="${dir3.starts.with}" />
</target>

Failing task if variable undefined

I would like my Ant task to fail if a variable it uses is not defined. E.g. currently
<mkdir dir="${some.dir}"/>
always succeeds, if some.dir is defined, it creates a directory under the variable's value, if not it creates a directory named literally ${some.dir}.
Is there a way and how to switch between the currently lenient and a more strict mode of resolving variables in Ant? I am running this in Eclipse.
<fail unless="some.dir"/>
<mkdir dir="${some.dir}"/>
or
<fail>
<condition>
<not>
<isset property="some.dir"/>
</not>
</condition>
</fail>
<mkdir dir="${some.dir}"/>

Conditional property in Ant properties' file

Is it possible to set a property value in Ant property files (as opposed to build.xml) in a conditional way? For example, if apache.root property is set - the my_property will be ${apache.root}/myapp, /var/www/myapp otherwise. If not, what would be the common practice - reusable build.xml files?
Use the condition task:
<project name="demo" default="run">
<condition property="my_property" value="${apache.root}/myapp" else="/var/www/myapp">
<isset property="apache.root"/>
</condition>
<target name="run">
<echo message="my_property=${my_property}"/>
</target>
</project>
You can include different property files based on environments or the conditional variables. For example
<echo>Building ${ant.project.name} on OS: ${os.name}-${os.arch}</echo>
<property file="build-${os.name}.properties" />
this would include a file named 'build-Windows 7.properties' or 'build-Linux.properties' depending on where the build is being run. Of course the property directive looks in the current directory as well as home directory. So the property file could be a part of the build source or in the home directory of the build account.
You can use the condition tag to generate part of the name of the property file as well to select
One of the simplest form of condition you can use is:
<exec executable="hostname" outputproperty="hostname"/>
<condition property="python" value="/usr/bin/python3.4">
<equals arg1="${hostname}" arg2="host0"/>
</condition>
<property name="python" value="/usr/bin/python"/>
to accommodate different python installation path for example. Here, default install path is /usr/bin/python except for host0 where it /usr/bin/python3.4
The OP was asking about a properties file, not within the ant build file. Unfortunately conditionals cannot be done from within the build file. What you can do is have separate property files for each set of dependant properties. For instance:
Build.xml
<condition property="app.name" value="appA">
<equals arg1="${appName}" arg2="A" />
</condition>
<condition property="app.name" value="appB">
<equals arg1="${appName}" arg2="B" />
</condition>
<property file="${app.name}.properties" />
<!-- since properties are immutable, set your defaults here -->
<property name="apache.root" value="/var" />
<property file="restOfProps.properties" />
appA.properties
apache.root=/appA
restOfProps.properties
my_property=${apache.root}/myapp

Different targets on Different OS with Ant

I have an ant target I don't want called unless I am running ant on Linux (Not called on Windows)
<target name="jar.all" depends="clean,compile.nic,jar,jar.resources"/>
The target that I don't want called on Windows is: compile.nic
How can I do this?
Insert an if property to your compile.nic target.
<target name="compile.nic" if="windowsos">
And use this conditions before running your target
<condition property="windowsos">
<os family="windows" />
</condition>
<condition property="linuxos">
<os family="unix" />
</condition>
Here is a real-world example for Windows vs. UNIX commands. The ELSE improves on previous answers.
<condition property="maven.executable" value="mvn.bat" else="mvn">
<os family="windows" />
</condition>
<target name="clean">
<exec executable="${maven.executable}">
<arg value="clean" />
</exec>
</target>
Can I make the Ant copy task OS-specific?
Additionally some tasks support os attribute. for example exec:
<exec executable="cmd" os="windows"/>
You can use the os condition to set a property, and then skip your target by adding either the if or unless attributes (depending on how you define your property)

Resources