经常,groovy或者groovyc任务会因为找不到GroovySourceAst类而抛出ClassNotFoundException,从而导致失败。
如果这是因为找不到除了GroovySourceAst以外的类而抛出ClassNotFoundException,从而导致失败的话,那么欢迎来到Ant的世界。就如Ant的外部任务的手册所说的:“不要添加任何东西到CLASSPATH环境变量——这常常就是导致非常费解的错误的原因。请使用Ant添加库的机制。”并且它的库目录一节中说:“当CLASSPATH环境变量为空时,Ant可以非常好的工作,这实际上就是-noclasspath选项所强制执行的。我们接到了比我们想象中多很多的维护电话是关于classpath问题的(尤其是引用的问题)。”所以请尝试通过ant -noclasspath命令来运行Ant,或者甚至是在你的shell中的完成这个功能的ant的别名。
如果找不到的类是GroovySourceAst,并且上面所说的都没有帮助,那么在你的classpath的某处存在antlr的冲突。这可能是因为你正在使用maven而它其中一部分污染了classpath,或者在你的classpath的某处存在一个不同的antlr jar。
使用groovy发行包中的groovy-all-VERSION.jar,而不是普通的groovy的jar。groovy-all-VERSION.jar已经在一个独立的命名空间中包含了antlr和asm库,所以应该不会和其他库存在冲突。
有时候不能使用groovy-all-VERSION.jar,例如因为你想在创建jar文件前构建groovy。在这种情况下,你需要添加一个loaderref的任务定义。但是只有那样也不行。你需要添加rootLoaderRef任务来设置这个装载器(loader)引用。例如:
<taskdef name="rootLoaderRef" classname="org.codehaus.groovy.ant.RootLoaderRef" classpathref="task.classpath"/> <rootLoaderRef ref="tmp.groovy.groovyc"> <classpath refid="execution.classpath"/> </rootLoaderRef> <rootLoaderRef /> <taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy" loaderref="tmp.groovy.groovyc"/>
groovy任务现在通过tmp.groovy.groovyc类装载器被建立,这尽量避免了装载冲突的jar文件,如antlr。在使用由rootLoaderRef定义的loaderref的taskdef之前,执行一次rootLoaderRef任务是非常重要的。
你可能需要调整你的classpath设置,来包含你要使用的jar文件。例如,如果你把groovy的jar文件放在了你的Ant LIB目录下,那么Groovy将在Ant的根类装载器(root classloader)中。如果你现在想引用一个外部的库,如一个JDBC驱动程序,你可能需要把这个库也放在你的Ant LIB目录下,从而使它对于在同一个类装载器中的Groovy可见。同时请查看上面有关loaderref的讨论。
同样,Groovy发行包中没有包含完整的Ant发行包。如果你要使用一些可选的Ant任务,你可能需要添加一些额外的jar文件到你的classpath中,来使用这些额外的特征。这里是一份关于一些需要额外的jar文件的Ant任务的不完整的列表:
| Ant任务 | 附加的Jar文件 |
|---|---|
| junitreport | ant-trax.jar, xercesImpl.jar, xml-apis.jar |
| mail.jar, activation.jar, smtp.jar (如果使用了SMTP), ant-javamail.jar (如果需要发送MIME email) | |
| sql | your_JDBC_driver |
不是,如果在某处存在冲突的ant的jar文件或common-logging的jar文件,那么这些解决方案都没有作用。解决方案2可以解决更多困难的jar问题,只要你的classpath是尽可能干净的。如果你想以防万一,那么你必须派生(fork)javaVM,这意味着你必须像这样的使用任务:
<!-- 派生一个JVM来避免classpath地狱 --> <java classname="org.codehaus.groovy.ant.Groovyc" fork="yes" failonerror="true"> <classpath refid="project.classpath"/> <arg value="${build.classes.dir}"/> <arg value="${src.dir}"/> </java>