简体   繁体   English

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

[英]use system classpath for ant javac task

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. 我希望javac任务使用系统类路径中的jar,我指的是在ant启动之前在shell环境中设置的类路径。 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. 这样,ant只将输出目录作为类路径传递。

[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. (jsonc是我正在研究的项目,而D:\\dev\\tbull-projects\\jsonc是工作目录。)我浏览了一段时间的文档并提出了两次尝试。 First one was adding the attribute classpath="${java.class.path}" to the javac tag. 首先是将属性classpath="${java.class.path}"到javac标记中。 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. 这会将一个非常长的类路径传递给编译器,列出来自ant自己的lib目录的每个jar,最后是来自JDK的tools.jar。 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. 在javac被调用之前,这让我朝着正确的方向前进。 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? 它会知道如何在Windows上进行路径算术,不是吗?

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. 该项目会是开源的,所以我希望其他开发人员能够构建它,他们已经下载了依赖库,并在 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). 从我在其他关于ant + classpath的问题中看到的情况来看,使用源代码分发依赖库是一种自定义方式(因此类路径就像./libs一样)。 But I surely don't want to have jars in my git repo. 但我肯定不想在我的git repo中放入罐子。 So how could that be done? 那怎么可能呢?

Set includeJavaRuntime=true in javac task. 在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>

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

so i have used this option -XDignore.symbol.file for ant i put this value in javac tag. 所以我使用了这个选项-XDignore.symbol.file for ant我把这个值放在javac标签中。 it works perfectly if you use eclipse or any other ide . 如果你使用eclipse或任何其他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 因此,每当你在使用rt.jar中的类时获得ClassNotFoundException,并且如果该类仍然存在,那么只需尝试在java编译器中添加此参数

To reference rt.jar from ant you may use: 要从蚂蚁中引用rt.jar,您可以使用:

<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 原始详细信息请参见: http//www.javaroots.com/2013/01/javac-error-using-classes-from-rtjar.html

Why wouldn't you set CLASSPATH in Ant? 为什么不在 Ant中设置CLASSPATH? 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. 它不仅可以工作,而且build.xml也会记录要求。

It's pretty clear the folks behind java , and (or at least), ant , really really really don't want to see $CLASSPATH end up as storage for user installed libraries of the sort that 95% of other mainstream languages (C/C++, perl, python, ruby, etc. etc.) use. 很明显java背后的人,(或者至少), ant ,真的真的不想看到$CLASSPATH最终成为95%其他主流语言的用户安装库的存储(C / C ++) ,perl,python,ruby等等)使用。 So this is a tough paradigm to swallow if you are used to general programming in most other mainstream languages. 因此,如果您习惯于大多数其他主流语言的通用编程,那么这是一个难以接受的范例。

The disinclination goes so far that it is obvious ant intentionally strips $CLASSPATH out of the environment, but an easy way around this is to just use a different variable. 不情愿的是,很明显ant 故意将 $CLASSPATH剥离出环境,但是解决这个问题的一个简单方法就是使用另一个变量。

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

This will then work, no fuss, with both <javac> and <java> commands ( classpath="${classpath} ) which is good, because if you try this instead: 然后,这对于<javac><java>命令( classpath="${classpath} )都很有效,因为如果您尝试这样做:

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

There is no includeAntRuntime="false" option to <java> which would allow this to work. <java>没有includeAntRuntime="false"选项,这将允许它工作。 You simply cannot get $CLASSPATH in and someone has gone to lengths to make sure of it (without, apparently, and yikes, adding in a ponderous javascript hack). 你根本无法获得$CLASSPATH ,并且有人已经不遗余力地确保它(没有,显然,并且哎呀,添加了一个沉重的javascript黑客)。

Of course that means you need to use a separate env variable and for your distributed/production version stick to the Java "Sorry no user libs!" 当然,这意味着您需要使用单独的env变量,并且您的分布式/生产版本坚持使用Java“抱歉没有用户库”! paradigm. 范例。 That's not a big problem if you use a variable name that, if it becomes involved, will almost certainly be undefined on the target system. 如果您使用变量名称,那么这不是一个大问题,如果它变得涉及,几乎肯定会在目标系统上未定义。

If someone is new to java/ANT world, people who suggest maven are idiots whatever happened to KISS principle? 如果有人是java / ANT世界的新手,建议maven的人都是白痴,无论KISS原则发生了什么?

OP, instead of using javascript abomination try this 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>

Alternatively, there are the Maven Ant Tasks . 或者,还有Maven Ant Tasks These will allow you to use Maven's dependency mechanism in a way that, IMO, is cleaner than Ivy. 这些将允许您以一种IMO比Ivy更清洁的方式使用Maven的依赖机制。 But it's still not a great solution. 但它仍然不是一个好的解决方案。

Soo... seems I have to answer the question myself. 所以...似乎我必须自己回答这个问题。 Passing the original classpath to the javac task can be achieved with this: 将原始类路径传递给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>

That does the trick at least so far that the javac task now gets passed the correct classpath. 这至少到目前为止,javac任务现在传递了正确的类路径。 Yet it will still not work, javac now spits these complaints: 然而它仍然无效,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

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. 我一直使用它们,如果我在shell上手动创建一个等效的javac调用,它就像一个魅力。 I suspect ant's javac doesn't resolve the jar files in those directories. 我怀疑ant的javac无法解析那些目录中的jar文件。 I have to examine that. 我必须检查一下。

UPDATE UPDATE

It is indeed as I suspected, the wildcard is not resolved to the individual present jar files by the javac task. 确实如我所怀疑的那样,通配符不能通过javac任务解析为个别存在的jar文件。 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). 因此,我会在这里留下解决方案,让那些可怜的灵魂同样愚蠢,希望在他们要求别人没有别的事情比胡扯之前(是Anon,谈论你)。

Turns out, ant lacks the most basic functionality that you would expect from a build tool. 事实证明,ant缺乏构建工具所期望的最基本功能。 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. 虽然解决方案很少见,但是一篇关于使用JavaScript来减轻Apache Ant痛苦的帖子非常好,这真的挽救了我的一天。 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. 您可以放心地假设,如果您在Java 6上运行ant,则Javascript已经可用,而无需安装其他库。

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>

I will assume that your "popular" JARs are well-known open-source projects. 我将假设您的“流行”JAR是众所周知的开源项目。 This means that they're available in the Maven central repository. 这意味着它们可以在Maven中央存储库中使用。

While I believe that using Maven is the best answer to this question, you can also hack something using Ant's <get> task. 虽然我相信使用Maven是这个问题的最佳答案,但您也可以使用Ant的<get>任务来破解某些东西。 For example, to download the JUnit JAR (may have typos): 例如,要下载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>

Of course, if you do this then you'll have to carefully configure your build scripts so that you don't do the download with every run. 当然,如果你这样做,那么你将不得不仔细配置你的构建脚本,这样你就不会在每次运行时都进行下载。 And you'll increase load on the Maven Central repository. 而且你将增加Maven Central存储库的负载。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM