我有一个Ant build.xml
文件,它在命令行上工作得很好:它编译、构建JAR,并且我能够很好地从JAR执行主方法。build.xml
文件引用了几个分散在各处的第三方库。在构建JAR时,脚本不会将所有第三方库包含到JAR本身中。相反,它将它们的路径放入JAR的清单中。这有助于保持我的JAR精简和整洁。
我希望能够在Eclipse中编辑和调试我的项目,但是我找不到这样做的简单方法。我可以让我的项目使用Ant文件来构建项目,这似乎行得通。但是,Eclipse在寻找第三方库时遇到了麻烦,因此Eclipse有两个问题:
- 显示(在文本编辑器中)许多编译错误,因为很多类是未定义的,
- 它不能执行JAR。
我可以通过在两个不同的地方(即,通过Properties->Java Build Path->Libraries
的构建路径和通过Run Configurations->Classpath
的执行类路径)手动指定所有第三方库来解决上述两个问题。但是,我似乎不应该手动执行此操作,因为所有第三方库已经在JAR的清单中列出。我做错了什么?
build.xml
文件:
<!-- Set global properties for this build -->
<property name="src" location="./src" />
<property name="build" location="./build"/>
<property name="dist" location="./dist"/>
<property name="logs" location="./logs"/>
<property name="docs" location="./docs"/>
<property name="jar" location="${dist}/dynamic_analyzer.jar"/>
<property name="lib" location="../../thirdparty/lib"/>
<property name="hive-util" location="../../hive-utils/dist"/>
<property name="hpdb" location="../../hive-db/hpdb/dist"/>
<property name="static" location="../../hive-backend/static_analyzer/dist"/>
<property name="mainclass" value="com.datawarellc.main.DynamicMain"/>
<path id="dep.runtime">
<fileset dir="${lib}" includes="**/*.jar"/>
<fileset dir="${hive-util}" includes="**/*.jar"/>
<fileset dir="${hpdb}" includes="**/*.jar"/>
<fileset dir="${static}" includes="**/*.jar"/>
</path>
<target name="clean">
<delete dir="${build}"/>
<delete dir="${dist}"/>
<delete dir="${docs}"/>
<delete dir="${logs}"/>
</target>
<target name="init">
<tstamp/>
<mkdir dir="${build}"/>
<mkdir dir="${dist}"/>
<mkdir dir="${logs}"/>
</target>
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${build}" debug="on" includeantruntime="false">
<classpath refid="dep.runtime" />
</javac>
<!-- Debug output of classpath -->
<property name="myclasspath" refid="dep.runtime"/>
<echo message="Classpath = ${myclasspath}"/>
</target>
<target name="jar" depends="compile">
<!-- Put the classpath in the manifest -->
<manifestclasspath property="manifest_cp" jarfile="${jar}" maxParentLevels="10">
<classpath refid="dep.runtime" />
</manifestclasspath>
<jar jarfile="${jar}" basedir="${build}">
<manifest>
<attribute name="Main-Class" value="${mainclass}"/>
<attribute name="Class-Path" value="${manifest_cp}"/>
</manifest>
<zipfileset dir="${src}" includes="**/*.xml" />
</jar>
</target>
您可以看到我在几个目录(${lib}
, ${hive-util}
, ${hpdb}
和${static}
)中有第三方库。我用这些来创建一个名为dep.runtime
的path
。然后在构建jar时将dep.runtime
包含在清单中。如何让Eclipse在执行时为构建路径和类路径使用相同的dep.runtime
?
perl的另一种替代方法是使用嵌入式groovy任务:
<project name="demo" default="eclipse-files">
<property name="src.dir" location="src"/>
<property name="classes.dir" location="build/classes"/>
<path id="dep.runtime">
<fileset dir="${lib}" includes="**/*.jar"/>
<fileset dir="${hive-util}" includes="**/*.jar"/>
<fileset dir="${hpdb}" includes="**/*.jar"/>
<fileset dir="${static}" includes="**/*.jar"/>
</path>
<target name="bootstrap">
<mkdir dir="${user.home}/.ant/lib"/>
<get dest="${user.home}/.ant/lib/groovy-all.jar" src="http://search.maven.org/remotecontent?filepath=org/codehaus/groovy/groovy-all/2.1.4/groovy-all-2.1.4.jar"/>
</target>
<target name="eclipse-files">
<taskdef name="groovy" classname="org.codehaus.groovy.ant.Groovy"/>
<groovy>
import groovy.xml.MarkupBuilder
project.log "Creating .classpath"
new File(".classpath").withWriter { writer ->
def xml = new MarkupBuilder(writer)
xml.classpath() {
classpathentry(kind:"src", path:properties["src.dir"])
classpathentry(kind:"output", path:properties["classes.dir"])
classpathentry(kind:"con", path:"org.eclipse.jdt.launching.JRE_CONTAINER")
project.references."dep.runtime".each {
classpathentry(kind:"lib", path:it)
}
}
}
</groovy>
</target>
<target name="clean">
<delete file=".classpath"/>
</target>
</project>
指出:
- 引导目标将下载第三方groovy jar(不依赖perl)
- Groovy可以直接访问"deep .runtime"ANT路径并遍历其内容
- Groovy对编写XML文件有很好的支持。
下面的答案是类似的,并额外生成Eclipse .project文件。
- 使用Apache Ivy与netbeans
受@leeand00提供的链接的启发,我想出了以下的解决方案。
首先,我编写了一个简单的Perl脚本(称为genClasspath.pl
),它生成Eclipse使用的.classpath
文件。
#!/usr/bin/perl
use strict;
if (@ARGV != 2) {
print STDERR "Usage: $0 OUTFILE CLASSPATHSTRINGn";
print STDERR "e.g., $0 .classpath path1:path2:path3n";
exit 1;
}
my $OUTFILE = $ARGV[0];
my $CLASSPATHSTRING = $ARGV[1];
open my $out_fh, '>', $OUTFILE or die "Couldn't open output file: $!";
print $out_fh q{<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="build"/>
};
my @libs = split(":", $CLASSPATHSTRING);
foreach my $thisLib (@libs){
print $out_fh " <classpathentry kind="lib" path="$thisLib"/>n";
}
print $out_fh "</classpath>n";
然后,我让我的build.xml
文件调用这个脚本,内容为dep.runtime
:
<target name="compile" depends="init">
<javac srcdir="${src}" destdir="${build}" debug="on" includeantruntime="false">
<classpath refid="dep.runtime" />
</javac>
<property name="myclasspath" refid="dep.runtime"/>
<exec dir="." executable="../../scripts/genClasspath.pl" os="Linux">
<arg value=".classpath"/>
<arg value="${myclasspath}"/>
</exec>
</target>
唯一的问题是,在Eclipse中打开项目之前,我需要在命令行上运行Ant至少一次。但是当我这样做时,Eclipse能够很好地编译和执行我的项目,因为类路径与Ant的完全相同。