使用系统类路径进行ant javac任务

时间:2011-05-06 22:31:01

标签: java ant classpath

我希望javac任务使用系统类路径中的jar,我指的是在ant启动之前在shell环境中设置的类路径。该类路径是

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

在我的系统上。我在那里有许多项目使用的流行罐子。我在构建文件中使用的基本代码片段是

<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>

这样ant只会将输出目录作为类路径传递。

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

(jsonc是我正在处理的项目,D:\dev\tbull-projects\jsonc是工作目录。) 我浏览了一段时间的文档并提出了两次尝试。第一个是将属性classpath="${java.class.path}"添加到javac标记。这会将一个非常长的类路径传递给编译器,列出来自ant自己的lib目录的每个jar,最后是来自JDK的tools.jar。不是我想要的类路径。

第二枪是设置

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

在调用javac之前,这让我朝着正确的方向前进。现在这些行都在输出中:

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

嗯,你可以想象这些路径确实不存在。我只是不明白为什么蚂蚁以这种方式构建它们。它会知道如何在Windows上进行路径算法,不是吗?

也许我的方法基本上有缺陷,所以我会让你知道我实际上在追求什么。所以我正在开发这个项目(一个库),它使用另一个库。该项目将是开源的,所以我希望其他开发人员能够在下载了依赖库并将其放置在他们的类路径中之后构建它。

从我在其他关于ant + classpath的问题中看到的情况看来,使用源代码分发依赖库是一种自定义方式(因此类路径就像./libs一样)。但我肯定不想在我的git repo中放入罐子。那怎么可能呢?

8 个答案:

答案 0 :(得分:3)

在javac任务中设置includeJavaRuntime = true。

<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>

答案 1 :(得分:1)

为什么不会在Ant中设置CLASSPATH?它非常适合这样做。如果你做其他事情,那你就犯了一个错误。它不仅会起作用,你的build.xml也会记录这些要求。

答案 2 :(得分:1)

当javac编译代码时,它会尝试在名为ct.sym的符号文件中找到rt.jar中的文件(它也存在于lib目录中)。此符号文件中缺少某些文件。 我必须添加一个编译选项来忽略符号文件并直接查看rt.jar。

所以我使用了这个选项-XDignore.symbol.file for ant我把这个值放在javac标签中。如果你使用eclipse或任何其他ide,它可以很好地工作。

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

因此,每当你在使用rt.jar中的类时得到ClassNotFoundException,并且如果该类仍然存在,那么只需尝试在java编译器中添加此参数

要从蚂蚁中引用rt.jar,您可以使用:

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

在此处找到了原始详细信息:http://www.javaroots.com/2013/01/javac-error-using-classes-from-rtjar.html

答案 3 :(得分:0)

如果有人是java / ANT世界的新手,建议maven的人就是白痴 KISS原则发生了什么?

OP,而不是使用javascript憎恶尝试这个

<project default="build">
<property name="src" value="src" />
<property name="obj" value="obj" />

<property name="parent.dir" value="/jakarta-tomcat/common/lib" />
<path id="project.class.path">
    <pathelement location="lib/" />
    <fileset dir="${parent.dir}" includes="**/*.jar" />
</path>

<target name="build">
    <delete dir="${obj}" />
    <mkdir dir="${obj}" />
    <javac srcdir="${src}" destdir="${obj}" includes="**/*.java" excludes="**/package-info.java **/deprecated/*.java" debug="true" debuglevel="source,lines" classpathref="project.class.path" />
</target>

答案 4 :(得分:0)

很明显java背后的人,(或至少),ant,真的真的不想看到$CLASSPATH结束作为95%的其他主流语言(C / C ++,perl,python,ruby等)使用的用户安装库的存储。因此,如果你习惯于大多数其他主流语言的通用编程,那么这是一个难以接受的范例。

到目前为止,不情愿的是ant 明显地剥离$CLASSPATH离开环境,但是解决这个问题的一个简单方法就是使用不同的变量。

 <property name="classpath" location="${env.JAVALIBPATH}"/>

这对于<javac><java>命令(classpath="${classpath})来说都很有用,这很好,因为如果您尝试这样做:

 <property name="classpath" location="${env.CLASSPATH}"/>

includeAntRuntime="false"没有<java>选项可以让它发挥作用。你根本无法获得$CLASSPATH,并且有人已经不遗余力地确定它(没有,显然,并且喜欢,添加一个沉重的javascript黑客)。

当然,这意味着你需要使用一个单独的env变量,并且你的分布式/生产版本坚持使用Java&#34;抱歉没有用户库!&#34;范例。如果您使用变量名称,那么这不是一个大问题,如果它变得涉及,几乎肯定会在目标系统上未定义。

答案 5 :(得分:-1)

或者,有Maven Ant Tasks。这些将允许您以一种IMO比Ivy更清洁的方式使用Maven的依赖机制。但它仍然不是一个好的解决方案。

答案 6 :(得分:-1)

Soo ......似乎我必须自己回答这个问题。将原始类路径传递给javac任务可以通过以下方式实现:

<!-- 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>

至少到目前为止,javac任务现在传递了正确的类路径。然而它仍然无效,javac现在吐了这些抱怨:

[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

这是一个直截了当的谎言,这些路径确实存在。我一直使用它们,如果我在shell上手动创建一个等效的javac调用,它就像一个魅力。我怀疑ant的javac无法解析那些目录中的jar文件。我必须检查一下。

<强>更新

确实如我所怀疑的那样,通配符未通过javac任务解析为单个存在的jar文件。我设法手动解决,现在它可以正常工作。而这种解决实际上是一场自己的斗争。所以我会在这里留下解决方案,让那些可怜的灵魂同样愚蠢,希望在他们要求别人没有别的事情比胡扯之前(是Anon,谈论你)。

事实证明,ant缺乏构建工具所期望的最基本功能。事实证明,我不是第一个注意到这一点的人。虽然解决方案很少见, 是一篇关于Using JavaScript to make Apache Ant less painful的非常好的帖子,这真的挽救了我的一天。是的,蚂蚁确实可以编写脚本,虽然它是not kept secret,但它似乎并不广为人知。您可以放心地假设,如果您在Java 6上运行ant,则Javascript已经可用,而无需安装其他库。

Soo ......开展业务。事情就是这样:

<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>

答案 7 :(得分:-2)

我将假设您的“流行”JAR是众所周知的开源项目。这意味着它们可以在Maven中央存储库中使用。

虽然我相信使用Maven是这个问题的最佳答案,但你也可以使用Ant的<get>任务来破解某些东西。例如,要下载JUnit JAR(可能有拼写错误):

<property name="dependency.dir" value="${basedir}/dependencies"/>

<property name="junit.jar" value="junit-4.8.2.jar"/>
<property name="junit.url" value="http://search.maven.org/remotecontent?filepath=junit/junit/4.8.2/${junit.jar}"/>

<target name="download.dependencies">
    <mkdir dir="${dependency.dir}/>
    <get url="${junit.url}" dest="${dependency.dir}/${junit.jar}"/>
</target>

当然,如果你这样做,那么你必须仔细配置你的构建脚本,这样你就不会在每次运行时都进行下载。而且你将增加Maven Central存储库的负载。