将系统类路径用于 Ant Javac 任务



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

CLASSPATH=D:locallibjava*;.;C:libjava*;C:libjavadb*

在我的系统上。我在那里有很多受欢迎的罐子,被许多项目使用。我在构建文件中使用的基本代码段是

<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:devtbull-projectsjsoncobj'

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

第二个镜头是设定

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

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

dropping D:devtbull-projectsjsoncD:locallibjava* from path as it doesn't exist
dropping D:devtbull-projectsjsoncC:libjava* from path as it doesn't exist
dropping D:devtbull-projectsjsoncC:libjavadb* from path as it doesn't exist
dropping D:devtbull-projectsjsoncC:Program FilesJavajdk1.6.0_18jrelibsunrsasign.jar from path as it doesn't exist
dropping D:devtbull-projectsjsoncC:Program FilesJavajdk1.6.0_18jreclasses from path as it doesn't exist

好吧,你可以想象这些路径真的不存在。我只是不明白为什么蚂蚁会这样建造它们。它会知道如何在Windows上进行路径算术,对吗?

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

从我在关于 ant+classpath 的其他问题中看到的,似乎这是一种将依赖库与源代码一起分发的自定义方式(因此类路径可以像 ./libs 一样)。但我肯定不想在我的 git 存储库中有罐子。那么怎么能做到这一点呢?

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>

为什么不在 Ant 中设置 CLASSPATH? 它非常适合这样做。 如果你做任何其他事情,你就犯了一个错误。 它不仅可以工作,还可以构建.xml还会记录需求。

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

所以我为蚂蚁使用了这个选项 -XDignore.symbol.file 我把这个值放在 javac 标签中。 如果您使用 Eclipse 或任何其他 IDE,它可以完美运行。

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

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

要引用 rt.jar 来自 ant,您可以使用:

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

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

如果有人是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>

很明显

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,有人已经竭尽全力确保它(显然,没有和yikes,加上一个繁琐的javascript黑客)。

当然,这意味着你需要使用一个单独的env变量,并且对于你的分布式/生产版本,坚持Java"对不起,没有用户库!"范式。 如果您使用变量名称,这不是一个大问题,如果涉及该变量名称,几乎可以肯定在目标系统上未定义。

或者,还有Maven Ant任务。这些将允许您以IMO比常春藤更干净的方式使用Maven的依赖机制。但这仍然不是一个很好的解决方案。

所以...看来我必须自己回答这个问题。将原始类路径传递给 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:locallibjava*": no such file or directory
[javac] warning: [path] bad path element "C:libjava*": no such file or directory
[javac] warning: [path] bad path element "C:libjavadb*": no such file or directory

这是一个彻头彻尾的谎言,这些路径确实存在。我一直使用它们,如果我在 shell 上手动制作等效的 javac 调用,它就像一个魅力。我怀疑蚂蚁的javac没有解析这些目录中的jar文件。我必须检查一下。

更新

确实正如我所怀疑的那样,javac 任务不会将通配符解析为单个当前 jar 文件。我设法手动进行解析,现在它可以正常工作。而这种决心本身就是一场斗争。因此,我将把解决方案留给那些与同样愚蠢作斗争的可怜灵魂,希望在他们问那些除了胡说八道之外别无他法的人之前(是的,匿名者,在谈论你)。

事实证明,ant 缺少您期望从构建工具中获得的最基本功能。事实证明,我不是第一个注意到这一点的人。虽然解决方案很少见,但有一篇关于使用 JavaScript 让 Apache Ant 不那么痛苦的非常好的帖子,这真的拯救了我的一天。是的,蚂蚁确实可以写脚本,这似乎并不广为人知,尽管它没有保密。你可以放心地假设,如果你在Java 6上运行ant,Javascript已经可用,而无需安装额外的库。

所以。。。归结为正题。事情是这样的:

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

我会假设你的"流行"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 存储库的负载。

最新更新