Multiple source folders: Avoid implicit compilation with Ant

14,134

Solution 1

This is extremely ugly and needs some cleaning, but it should do what your looking for

<target name="compile" depends="clean,init" description="Compiles all source files.">
    <mkdir dir="temp"/>
    <javac srcdir="src1" sourcepath="src2" destdir="temp">
        <classpath>
            <fileset dir="lib">
                <include name="**/*.jar"/>
            </fileset>
        </classpath>
    </javac>
    <javac srcdir="src2" sourcepath="src1" destdir="temp">
        <classpath>
            <fileset dir="lib">
                <include name="**/*.jar"/>
            </fileset>
        </classpath>
    </javac>
    <javac srcdir="src1" destdir="bin1">
        <classpath>
            <fileset dir="lib">
                <include name="**/*.jar"/>
            </fileset>
            <pathelement location="temp"/>
        </classpath>
    </javac>
    <javac srcdir="src2" destdir="bin2">
        <classpath>
            <fileset dir="lib">
                <include name="**/*.jar"/>
            </fileset>
            <pathelement location="temp"/>
        </classpath>
    </javac>
    <delete dir="temp"/>
</target>

Solution 2

I had the same problem. And i found some pretty easy solution.

You just need to specify several source foulders in srcdir attribute in javac task. And you should not specify destdir attribute.

Something like this:

    <javac srcdir="src1:src2" />

All the binaries (.class files) will be placed in the same places as sources. So the structure of the class files will be exactly the same. Then you can move all *.class to the separate place, so they won't be stored in the source foulders.

And no double compilation as in Kurt Kaylor's example.

Solution 3

Here is what works for me when dealing with complex source trees with no duplicate compilation. The key is how you build and reference the paths.

<path id="src.separate.java.path">
    <pathelement path="separate-src1/java" />
    <pathelement path="separate-src2/java" />
</path>

<property name="src.separate.java.path" refid="src.separate.java.path" />

<path id="src.java.path">
    <pathelement path="src1/java" />
    <pathelement path="src2/java" />
    <pathelement path="src3/java" />
</path>

<property name="src.java.path" refid="src.java.path" />

<path id="src.java.all.path">
    <path refid="src.separate.java.path" />
    <path refid="src.java.path" />
</path>

<property name="src.java.all.path" refid="src.java.all.path" />

<target name="compile-java">
    <mkdir dir="${separate.classes.dir}" />
    <javac srcdir="${src.separate.java.path}" sourcepath="${src.java.all.path}" destdir="${separate.classes.dir}">
        <classpath refid="project.class.path" />
        <compilerarg value="-implicit:none"/>
    </javac>

    <mkdir dir="${build.classes.dir}" />
    <javac srcdir="${src.java.path}" sourcepath="${src.java.all.path}" destdir="${build.classes.dir}">
        <classpath refid="project.class.path"/>
        <compilerarg value="-implicit:none"/>
    </javac>
</target>

In order for this to work the source paths need to be made up of <pathelement>s. If you use <fileset>s the srcdir attribute for javac will choke on the path. You also need to expand the path into a property for srcdir and sourcepath to be able to use it.

So srcdir is the path to the source to compile and sourcepath is the path to all source the compiler needs to do resolve references. <compilerarg value="-implicit:none"/> tells the compiler to not generate class files for implicitly loaded source.

Of course you can do the same thing using nested <src> elements in javac instead of the <path>s and <property>s, but that will force you to list your source paths twice.

Share:
14,134
Michael Piefel
Author by

Michael Piefel

Scientist by vocation, programmer by profession.

Updated on June 04, 2022

Comments

  • Michael Piefel
    Michael Piefel almost 2 years

    Consider the following project layout (assuming A and B depend on each other):

    .
    |-- bin1
    |-- bin2
    |-- src1
    |   `-- A.java
    `-- src2
        `-- B.java
    

    After compilation, I want the classes to reside in their respective folders liike this:

    .
    |-- bin1
    |   `-- A.class
    |-- bin2
    |   `-- B.class
    |-- src1
    |   `-- A.java
    `-- src2
        `-- B.java
    

    This is quite simple from the command line:

     $ javac -implicit:none -sourcepath src1:src2 -d bin1 src1/*
     $ javac -implicit:none -sourcepath src1:src2 -d bin2 src2/*
    

    Eclipse also does it that way if so configured. But I cannot figure out how to do it with Ant.

    Appendix: My current javac tasks:

        <javac destdir="${classes.1.dir}">
            <src path="${src.1.dir}" />
            <src path="${src.2.dir}" />
        </javac>
        <javac destdir="${classes.2.dir}">
            <classpath path="${classes.1.dir}" />
            <src path="${src.2.dir}" />
        </javac>
    

    Note the circular dependency. The second task works well, it only compiles what’s in src2 as it has a classpath dependency on the other build. The first task, however, cannot take a classpath, since nothing is yet compiled, and with src it of course compiles too much.

  • Michael Piefel
    Michael Piefel over 13 years
    You can combine the first two javac tasks. Apart from that, this solution seems to be as clean as possible. Compilation tends to be the smallest part of my Ant run (unit tests take much longer), so double compilation seems quite acceptable.