tags:

views:

228

answers:

6

I have the following defined in a file called build-dependencies.xml

<?xml version="1.0" encoding="UTF-8"?>
<project name="build-dependencies">
  ...
  <path id="common-jars">
    <fileset file="artifacts/project-1/jar/some*.jar" />
    <fileset file="artifacts/project-2/jar/someother*.jar" />
  </path>
  ...
</project>

I include it at the top of my build.xml file. Now I need to make the artifacts folder a parameter so it can be changed during execution of different targets. Having this...

<?xml version="1.0" encoding="UTF-8"?>
<project name="build-dependencies">
  ...
  <path id="common-jars">
    <fileset file="${artifacts}/project-1/jar/some*.jar" />
    <fileset file="${artifacts}/project-2/jar/someother*.jar" />
  </path>
  ...
</project>

...and defining an "artifacts" property (and changing it) in the target does not work because it seems that the property substitution happens when the path is defined in build-dependencies.xml

How can I solve this? One way I was thinking was to have a parameterized macro and call that before the path is actually used, but that seems not elegant. Something like this:

<macrodef name="create-common-jars">
  <attribute name="artifacts"/>
  <sequential>
    <path id="common-jars">
      <fileset file="@{artifacts}/project-1/jar/some*.jar" />
      <fileset file="@{artifacts}/project-2/jar/someother*.jar" />
    </path>
  </sequential>
</macrodef>

EDIT: Ivy and command line parameters are not an option.

A: 

Lets call your file build.xml. So you execute it by running ant command. In the first case the artifacts names is hardcoded in the property defined on the third line below

<?xml version="1.0" encoding="UTF-8"?>
<project name="build-dependencies">
<property name="artifacts" value="first-value" />
  ...
  <path id="common-jars">
    <fileset file="artifacts/project-1/jar/some*.jar" />
    <fileset file="artifacts/project-2/jar/someother*.jar" />
  </path>
  ...
</project>

Now when you want to change it and use another value for that artifacts property, we run the script thus

ant -Dartifacts=new-value

This will override the hardcoded artifacts value in build.xml

If working in terms of ant targets you can do something similar, in the target on first line define the property, and if you want to overwrite the default value then pass the property as a parameter when that target is called.

Your comment reminded me of something else. Have your developers create a artifacts-dir-name.xml file. It will have only one line:

<?xml version="1.0" encoding="UTF-8"?>
<project name="artifacts-file">
<property name="artifacts" value="new-value" />
</project>

Now in your build.xml file, before the line where artifacts property is defined, import that file thus:

<import file="artifacts-dir-name.xml" optional="true" />

Now in Eclipse if this file exists, then the property is read from it and artifacts is set to "new-value", else the property is read from build.xml and is set to "first-value". All the developers need to do is to ensure artifacts-dir-name.xml file exists in that directory. This can run within Eclipse too.

omermuhammed
Yeah, I thought about this way, but I want these targets to be runnable from Eclipse with no configuration of extra options. So developers can check out the code and just run the ant targets. So this is not an option either :(
Strelok
A: 

I'd recommend using ivy to manage your classpath dependencies. Ivy has a neat concept called configurations that allows you to group collections of artifacts based on their usage.

Here's an adaption from one of my own build files:

<target name="retrieve" description="3rd party dependencies">
    <ivy:resolve/>

    <ivy:cachepath pathid="build.path" conf="build"/>
    <ivy:cachepath pathid="runtime.path" conf="runtime"/>
</target>

The configurations are managed in the ivy.xml file (Would replace your build-dependencies.xml file)

<ivy-module version="2.0">
    <info organisation="com.myspotontheweb" module="HelloWorld"/>

    <configurations>
        <conf name="build" description="jars needed for build" />
        <conf name="runtime" extends="build" description="jars needed at runtime" />
    </configurations>

    <dependencies>
        <dependency org="org1" name="project1" rev="1.0" conf="build->default"/>
        <dependency org="org2" name="project2" rev="1.0" conf="build->default"/>
        <dependency org="org3" name="project3" rev="1.0" conf="runtime->default"/>
        <dependency org="org4" name="project4" rev="1.0" conf="runtime->default"/>
    </dependencies>
</ivy-module>

The jar artifacts associated with each project would be downloaded and cached automatically from the on-line maven repositories or you can create your own local repository to hold collections of locally owned artifacts.

Mark O'Connor
I am aware of Ivy, unfortunately it is not an option.
Strelok
A: 

is using environment variables an option (if they are set when eclipse is launched they will be picked up)? If so, have each one set ARTIFACTS and this should work:

<?xml version="1.0" encoding="UTF-8"?>
<project name="build-dependencies">
  <property environment="env"/>
  <path id="common-jars">
    <fileset file="${env.ARTIFACTS}/project-1/jar/some*.jar" />
    <fileset file="${env.ARTIFACTS}/project-2/jar/someother*.jar" />
  </path>
</project>
raticulin
A: 

OK, I think there is no other obvious way for me to do what I am trying to do, except use a macro that takes a parameter and creates the path with the appropriate artifacts folder.

To give a bit of context, why I was trying to what I wanted is to have "instrumented" and "not-instrumented" artifacts in separate folders. And in my "targets" I could just vary the artifacts mode. So what I do now is I have a macro: <initialise-build-settings artifacts-mode="instrumented" /> that sets up all the paths and other variables.

Thanks for your answers guys.

Strelok
There's a different approach; see my answer. You may decide you like the macros better anyway, but the PatternSet is more declarative.
Zac Thompson
+1  A: 

You don't want a parameterized path. You want a PatternSet. You can define the patternset at the top-level and then just refer to it in individual targets when you need it. For your example:

<?xml version="1.0" encoding="UTF-8"?>
<project name="build-dependencies">
  ...
  <patternset id="common-jars">
    <include name="project-1/jar/some*.jar" />
    <include name="project-2/jar/someother*.jar" />
  </patternset>
  ...
  <path id="instrumented-jars">
    <fileset dir="instrumented">
      <patternset refid="common-jars" />
    </fileset>
  </path>
  ...
  <path id="standard-jars">
    <fileset dir="not-instrumented">
      <patternset refid="common-jars" />
    </fileset>
  </path>
  ...
</project>
Zac Thompson
This is nice. Thanks!
Strelok
A: 

You can do this with different dependencies:

setpath.xml:

<project name="setpath">
  <target name="setCommonJars">
    <path id="common-jars">
      <fileset file="${param1}/some*.jar" />
      <fileset file="${param1}/someother*.jar" />
    </path>
  </target>
</project>

build.xml:

<project name="Test path" basedir=".">
  <import file="./setpath.xml" />

  <target name="buildT1" depends="setT1,setCommonJars">
    <property name="jar-str" refid="common-jars" />
    <echo message="buildT1: ${jar-str}" />
  </target>

  <target name="buildT2" depends="setT2,setCommonJars">
    <property name="jar-str" refid="common-jars" />
    <echo message="buildT2: ${jar-str}" />
  </target>

  <target name="setT1">
    <property name="param1" value="t1" />
  </target>

  <target name="setT2">
    <property name="param1" value="t2" />
  </target>

</project>

If you call target buildT1 then the t1 directory will be used, if you call buildT2 then the t2 directory will be used.

mamboking