Question

I am building projects with Ant and want to cause a recompile when sources have changed. This mostly works automatically with the standard javac-task, apart from one case:

When a source file is deleted but no other change is made, nothing is recompiled.

Even if this would leave the project unable to be cleanly compiled without error, the build will still succeed because of the left-over class-files.

Doing a clean build every time is not an option, since we have 137 dependent modules that take quite a while to build. Also, we do not want to retry every failed CI-build after a full clean to see if it will work then.

This seems like a very basic thing that should be supported by the javac-task, but I did not see any attribute that looked helpful. I cannot get it right after trying anything else I can think of:

Finding out if there are any left-over classes:

<difference id="targets.todelete">
  <union>
    <mappedresources>
      <fileset dir="${src.java.dir}" includes="**/*.java" />
      <globmapper from="*.java" to="*.class" />
    </mappedresources>
  </union>
  <union>
    <fileset dir="${build.java.dir}" includes="**/*.class" />
  </union>
</difference>

The above does not work, the difference actually seems to act more like a union. If a java-file has a corresponding class-file, there will be two entries for the class file in the difference.

This was another try:

<fileset id="del" dir="${src.java.dir}" includes="**/*.java" casesensitive="false" defaultexcludes="false">
  <different targetdir="${build.java.dir}" ignoreFileTimes="true">
    <globmapper from="*.java" to="*.class" />
  </different>
</fileset>

I was trying to find all files that were different, but the result was more or less the same as above.

When I tried without the mapper, using partly-overlapping sets of source files in the and the , it did work as intended. This leads me to suspect that anything to do with resource differences does not play nicely with mapped files.

Does anyone here have any idea how to achive my goal in any other way?

UPDATE: This is the working solution with correct handling of inner classes. I need multiple source directories, which complicates things further.

<!-- find class files with without corresponding source files -->
<fileset id="src1" dir="${src.java.dir}" includes="**/*.java" />
<fileset id="src2" dir="${build.generated.java.dir}" includes="**/*.java" erroronmissingdir="no" />
<fileset id="src3" dir="${generated.java.dir}" includes="**/*.java" erroronmissingdir="no" />

<fileset id="build" dir="${build.java.dir}" includes="**/*.class" erroronmissingdir="no" />
<difference id="targets.todelete">
  <resourcelist>
    <string>${toString:src1};${toString:src2};${toString:src3}</string>
    <filterchain>
      <tokenfilter>
        <replacestring from=";" to="${line.separator}" />
        <replacestring from=".java" to=".class" />
      </tokenfilter>
      <ignoreblank/>
      <prefixlines prefix="${build.java.dir}/" />
    </filterchain>
  </resourcelist>
  <resourcelist>
    <string>${toString:build}</string>
    <filterchain>
      <tokenfilter>
        <replacestring from=";" to="${line.separator}" />
        <!-- map inner classes back to their java files -->
        <replaceregex pattern="\$.*(.class)" replace="\1" flags="gi"/>
      </tokenfilter>
      <prefixlines prefix="${build.java.dir}/" />
    </filterchain>
  </resourcelist>
</difference>

<if>
  <not>
    <length string="${toString:targets.todelete}" trim="true" length="0" />
  </not>
  <then>
    <echo>Source files were deleted, cleaning output to force re-build!</echo>
    <delete dir="${build.java.dir}" />
    <delete dir="${build.test.java.dir}" />
    <delete dir="${cobertura_html_report.dir}" />
  </then>
</if>
Était-ce utile?

La solution

The issue is that the difference resource collection does the difference based on the absolute paths, not the relative paths. And there is no way to make Ant apply mappers to a full absolute path. It is just works with resource names.

The solution is to serialize the fileset into a list of strings, make the appropriate change to that list of strings, and interpret them back as files for the difference.

Here is suggested piece of build.xml which should compute the correct list of class files:

<fileset id="src" dir="${src.java.dir}" includes="**/*.java"/>

<fileset id="build" dir="${build.java.dir}" includes="**/*.class"/>

<difference id="targets.todelete">
    <resourcelist>
        <string>${toString:src}</string>
        <filterchain>
            <tokenfilter>
                <replacestring from=";" to="${line.separator}" />
                <replacestring from=".java" to=".class" />
            </tokenfilter>
            <prefixlines prefix="${build.java.dir}" />
        </filterchain>
    </resourcelist>
    <resourcelist>
        <string>${toString:build}</string>
        <filterchain>
            <tokenfilter>
                <replacestring from=";" to="${line.separator}" />
            </tokenfilter>
            <prefixlines prefix="${build.java.dir}" />
        </filterchain>
    </resourcelist>
</difference>
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top