use system classpath for ant javac task

11,157

Solution 1

Soo... seems I have to answer the question myself. Passing the original classpath to the javac task can be achieved with this:

<!-- load environment into the env property -->
<property environment="env" />

<javac srcdir="${src}" destdir="${obj}"
    includes="**/*.java"
    excludes="**/package-info.java **/deprecated/*.java"
    includeAntRuntime="no" includeJavaRuntime="no"
    debug="true" debuglevel="source,lines"
>
    <!-- add -classpath option manually -->
    <compilerarg value="-classpath" />
    <compilerarg value="${env.CLASSPATH}" />
    <compilerarg value="-Xlint"/>
</javac>

That does the trick at least so far that the javac task now gets passed the correct classpath. Yet it will still not work, javac now spits these complaints:

[javac] warning: [path] bad path element "D:\local\lib\java\*": no such file or directory
[javac] warning: [path] bad path element "C:\lib\java\*": no such file or directory
[javac] warning: [path] bad path element "C:\lib\java\db\*": no such file or directory

This is a straight lie, these paths do very much exist. I use them all the time, and if I manually craft an equivalent javac invocation at the shell, it works like a charm. I suspect ant's javac doesn't resolve the jar files in those directories. I have to examine that.

UPDATE

It is indeed as I suspected, the wildcard is not resolved to the individual present jar files by the javac task. I managed to do the resolving manually, and now it works as it should. And that resolving was in fact a struggle on its own. So I'll leave the solution here for those poor souls fighting the same stupidity, hopefully before they ask people that have nothing else to do than bullshitting around (yes Anon, talking about you).

Turns out, ant lacks the most basic functionality that you would expect from a build tool. Also turns out that I'm not the first one to notice that. While solutions are rare, there is a very good post about Using JavaScript to make Apache Ant less painful, which really saved my day. Yes, ant can indeed be scripted, which seems not to be widely known, although it is not kept secret. You can safely assume, that Javascript is already available without installing additional libraries if you run ant on Java 6.

Soo... down to business. Here is the thing:

<target name="expand_classpath">
    <script language="javascript"><![CDATA[
        // the original classpath
        var ocp = java.lang.System.getenv("CLASSPATH");
        //  ... split in parts
        var ocp_parts = ocp.split(project.getProperty("path.separator"));

        // where our individual jar filenames go,
        //  together with pure directories from ocp_parts
        var expanded_parts = [ ];

        for each (var part in ocp_parts) {
            if (part.endsWith('*')) {
                var dir = part.substring(0, part.length() - 1);
                var f = new java.io.File(dir);

                // don't know how to construct a java.io.FilenameFilter,
                //  therefore filter the filenames manually
                for each (var file in f.listFiles())
                    if (file.getPath().endsWith('.jar'))
                        expanded_parts.push(file.getPath());
            } else
                expanded_parts.push(part);
        }

        var expanded = expanded_parts.join(project.getProperty("path.separator"));
        project.setProperty("classpath.expanded", expanded);
    ]]></script>

    <!-- <echo message="classpath.expanded = ${classpath.expanded}" /> -->
</target>

<target name="build" depends="expand_classpath">
    <mkdir dir="${obj}" />

    <javac srcdir="${src}" destdir="${obj}"
        classpath="${classpath.expanded}"
        includes="**/*.java"
        excludes="**/package-info.java **/deprecated/*.java"
        includeAntRuntime="no" includeJavaRuntime="no"
        debug="true" debuglevel="source,lines"
    >
        <compilerarg value="-Xlint"/>
        <compilerarg value="-Xlint:-fallthrough"/>
    </javac>
</target>

Solution 2

Set includeJavaRuntime=true in javac task.

<target name="build">
    <mkdir dir="${obj}" />
    <javac srcdir="${src}" destdir="${obj}"
        includes="**/*.java"
        excludes="**/package-info.java **/deprecated/*.java"
        includeAntRuntime="no" includeJavaRuntime="true"
        debug="true" debuglevel="source,lines">
        <compilerarg value="-Xlint"/>
    </javac>
</target>

Solution 3

Why wouldn't you set CLASSPATH in Ant? It's perfectly suited to do just that. You're making a mistake if you do anything else. Not only will it work, bu your build.xml will document the requirements as well.

Solution 4

When javac compiles the code , it tries to find the files in rt.jar in a symbol file called ct.sym (which is also present in lib directory). some files are missing in this symbol file. i have to add a compile option to ignore symbol file and look directly in rt.jar.

so i have used this option -XDignore.symbol.file for ant i put this value in javac tag. it works perfectly if you use eclipse or any other ide .

<compilerarg value="-XDignore.symbol.file"/> 

So , whenever you get ClassNotFoundException in using classes from rt.jar , and if the class is still present there , just try to add this argument in java compiler

To reference rt.jar from ant you may use:

<fileset dir="${java.home}/lib" includes="rt.jar"/>

Original details were found here: http://www.javaroots.com/2013/01/javac-error-using-classes-from-rtjar.html

Share:
11,157
T-Bull
Author by

T-Bull

Updated on June 17, 2022

Comments

  • T-Bull
    T-Bull almost 2 years

    I want the javac task to use jars from the system classpath, by which I mean the classpath that is set in the shell's environment before ant is started. That classpath is

    CLASSPATH=D:\local\lib\java\*;.;C:\lib\java\*;C:\lib\java\db\*
    

    on my system. I have popular jars there that are used by many projects. The basic snippet I use in the build file is

    <target name="build">
        <mkdir dir="${obj}" />
        <javac srcdir="${src}" destdir="${obj}"
            includes="**/*.java"
            excludes="**/package-info.java **/deprecated/*.java"
            includeAntRuntime="no" debug="true" debuglevel="source,lines"
        >
            <compilerarg value="-Xlint"/>
        </javac>
    </target>
    

    That way ant only passes the output directory as classpath.

    [javac] '-classpath'
    [javac] 'D:\dev\tbull-projects\jsonc\obj'
    

    (jsonc is the project I'm working on, and D:\dev\tbull-projects\jsonc is the working directory.) I browsed the documentation for a while and came up with two attempts. First one was adding the attribute classpath="${java.class.path}" to the javac tag. That would pass a tremendously long classpath to the compiler, listing every single jar from ant's own lib directory and finally tools.jar from the JDK. Not the classpath that I wanted.

    The second shot was setting

        <property name="build.sysclasspath" value="first" />
    

    before javac was invoked, and that got me in the right direction. Now these lines were among the output:

    dropping D:\dev\tbull-projects\jsonc\D:\local\lib\java\* from path as it doesn't exist
    dropping D:\dev\tbull-projects\jsonc\C:\lib\java\* from path as it doesn't exist
    dropping D:\dev\tbull-projects\jsonc\C:\lib\java\db\* from path as it doesn't exist
    dropping D:\dev\tbull-projects\jsonc\C:\Program Files\Java\jdk1.6.0_18\jre\lib\sunrsasign.jar from path as it doesn't exist
    dropping D:\dev\tbull-projects\jsonc\C:\Program Files\Java\jdk1.6.0_18\jre\classes from path as it doesn't exist
    

    Well, you can imagine that these paths really don't exist. I just don't get why ant constructed them this way. It would know how to do path arithmetic on Windows, would it?

    Maybe my approach is flawed more fundamentally, so I'll let you know what I'm actually after. So I'm developing this project (a library), which uses another library. The project is gonna be open source, so I want other developers to be able to build it after they have downloaded the dependency library and placed it somewhere in their classpath.

    From what I saw in other questions about ant+classpath, it appears that it's a custom fashion to distribute the dependency libs with the source code (so the classpath can be just like ./libs). But I surely don't want to have jars in my git repo. So how could that be done?

  • T-Bull
    T-Bull almost 13 years
    I think every developer should have the possibility to define an own classpath (or any other env var, after all that's what they are for). (I'm not talking about company network here, but independent developers.)
  • T-Bull
    T-Bull almost 13 years
    Addendum: If i hardcode the cp in the build file, every developer has to edit it for his own environment, which is not only unpleasant, also it leads to conflicts when merging changes. Didn't we all learn that hardcoding is bad?
  • Anon
    Anon almost 13 years
    And, as a sidenote to this: I think you said you were creating an open-source library. Most of the professional programmers that I know won't use a library that isn't available from Maven Central. The Maven Ant tasks will at least let you create a deployable artifact.
  • T-Bull
    T-Bull almost 13 years
    +1 for at least trying to answer my question, in contrast to the ignorant people in this thread. Unfortunately, the answer is not correct. includeJavaRuntime extends the classpath argument by the path to the JRE jars, like C:\Program Files\Java\jdk1.6.0_18\jre\lib\rt.jar in my case. The originally defined cp doesn't show up.
  • T-Bull
    T-Bull almost 13 years
    Could you point me to the exact location in my text where I asked for how to download a dependency? Probably not. If I could I would downvote you like -10 for this silly post. Please, just go away!
  • kdgregory
    kdgregory almost 13 years
    This might just be the most idiotic posting that I have ever seen on SO. First you whine about the tool. Then you whine about the people who try to teach you how to use the tool properly. Then you post this dreck. If you want a shell script, why don't you just write a shell script? -1, and if I could post an anti-bounty I would.
  • T-Bull
    T-Bull almost 13 years
    +1 for actually reasoning why you downvoted me. :-) But: I asked the people several times to educate me, yet they refused to do so, so I have to assume they don't actually know what they talk about. (Just saying "don't do that" without any explanation is not the way teachers talk, it's the way idiots talk.) And regarding shell scripts, I value their flexibility very much, but they are unfortunately not cross-platform. The same goes for make. I chose ant specifically because it was the nearest I could find to traditional make files.
  • duffymo
    duffymo almost 13 years
    No, dependencies should be in your project in /lib and /test-lib files. I agree that developer-dependent classpaths are bad, but that's not what I'm recommending.
  • T-Bull
    T-Bull almost 13 years
    You supply a handcrafted classpath by including all jars beneath a location hardcoded in the ant file. And this is how exactly related to my question? Not downvoting you though for mentioning the KISS principle, which I value very much and is indeed the driving force behind the question (although my eventual answer doesn't seem to fit in well).