Antcall vs Depends

Occasionally, I see issues pop up on Java projects about builds not working properly.  They usually look like, “The build worked yesterday, but it doesn’t work today.  I was working on it, but I didn’t change anything related to the part that is failing.” or “I can’t figure out why, when I run this by itself, it passes.  When I run it as a part of a full build, it fails.”  Build errors are almost always caused by a misunderstanding of the function of “depends”.

How Not to Use Depends

This probably works, but only by chance.

<target name="test" depends="clean,compile,compile-test,test,service-test" />

Depends is not a list of tasks to be executed.  It’s a list of dependencies that must be satisfied before a target can be completed.  Execution is not guaranteed and side effects are common.  Take a close look at what each of the tasks in the following script is doing.

<target name="functional-test" depends="start-server,run-tests,stop-server" />
<target name="service-test" depends="start-server,run-service-tests,stop-server" />
<target name="test" depends="clean,compile,compile-test,functional-test,service-test" />

To a human, the intent is obvious.

  1. Clean and compile
  2. Start server
  3. Run functional tests
  4. Stop server
  5. Start server
  6. Run service tests
  7. Stop server

But Ant will not execute those tasks.  Since they were declared as dependencies, Ant evaluates them as such.  The functional tests will run as you would expect.  When it runs the service-test target, though, it will see the start and stop server tasks again.  As far as it’s concerned, those dependencies have already been satisfied, so it will skip start-server and stop-server.  Ant is evaluating the script correctly, but the script is wrong.

Dependencies are not tasks to be executed.  They are dependencies.  Once a dependency has been satisfied, it will not be executed again.

Antcall Is Not Evil

It exists for a reason.  Use it when it is useful.  Dependencies are dependencies, not task lists.  In this case, it would be impractical to try to write the script to use depends attributes to cause events to happen correctly.  It can be fixed very easily by using antcall.  Each antcall is evaluated separately, so you don’t have to worry about side effects like the script above.  It’s also easier to read.  You can see what’s intended to happen before this target and you can see what this target is intended to do.

<target name="functional-test" depends="start-server,run-tests,stop-server" />
<target name="service-test" depends="start-server,run-service-tests,stop-server" />
<target name="test" depends="clean,compile,compile-test">
  <antcall target="functional-test" />
  <antcall target="service-test" />
</target>

Use Your Own Judgement

Obviously, antcall should not be used instead of depends.  Depends is far more useful.  But don’t throw a tool like antcall away just because it doesn’t seem as cool or concise or elegant.

Posted in Java at September 17th, 2010. 4 Comments.

Terracotta setup using Ant tutorial

I just started using Terracotta for the first time.  This is how I put it in my project and configured Ant tasks to start and stop the server. The client is using Windows, so the Ant tasks work in Windows.

  1. Create tc-config.xml in the root of the project.
    <?xml version="1.0" encoding="UTF-8"?>
    <con:tc-config xmlns:con="http://www.terracotta.org/config">
      <servers>
        <server host="%i" name="localhost">
          <dso-port>9510</dso-port>
          <jmx-port>9520</jmx-port>
          <data>bin/terracotta/server-data</data>
          <logs>bin/terracotta/server-logs</logs>
          <statistics>bin/terracotta/cluster-statistics</statistics>
        </server>
        <update-check>
          <enabled>true</enabled>
        </update-check>
      </servers>
      <clients>
        <logs>bin/terracotta/client-logs</logs>
        <statistics>bin/terracotta/client-statistics/%D</statistics>
        <modules>
          <module name="tim-hibernate-3.2.5" version="1.2.2"/>
          <module name="tim-ehcache-1.3" version="1.2.2"/>
          <module name="tim-cglib-2.1.3" version="1.2.2"/>
          <module name="tim-ehcache-commons" version="1.2.2"/>
        </modules>
      </clients>
    </con:tc-config>
  2. Put Terracotta into your library directory.  You’ll need bin, docs, lib and schema directories and the license files.
    1. You don’t need the licenses for the software to work, but it’s always a good idea to include them, especially if your app is not open source 😛
  3. Create the Ant targets.
    <target name="start.terracotta.windows">
      <exec executable="cmd">
        <arg value="/c"/>
        <arg value="lib\terracotta-2.7.2\bin\start-tc-server.bat"/>
      </exec>
    </target>
    
    <target name="stop.terracotta.windows">
      <exec executable="cmd">
        <arg value="/c"/>
        <arg value="lib\terracotta-2.7.2\bin\stop-tc-server.bat"/>
      </exec>
    </target>
    1. Notice the backslashes there.  Normally, Ant will convert forward slashes to backslashes in Windows.  Since this is a command line argument, though, it doesn’t.  If you’re running on both *nix and Windows, you’ll have to have two versions of the paths, one with forward and one with back slashes.
  4. That’s it. Run the start task to start and the stop task to stop.
Posted in Terracotta at January 27th, 2009. No Comments.