我想让项目与两个模块。应用程序和服务器。服务器取决于应用程序。当我编译服务器,我想包括类文件从应用程序到构建。但它解决类路径相对于服务器,而不是应用程序,因为导入问题。如何使ant解析相对于应用程序的应用程序位置和相对于服务器的服务器位置。我不知道蚂蚁文档是怎么做的。你能用更简单的方式解释一下吗?说明问题的代码片段
应用build . xml:
<project name="app">
<property name="app.build.dir" location="build"/>
<target name="compile">
<echo message="Compiling app to ${app.build.dir}"/>
</target>
</project>
build . xml服务器:
<project name="server">
<property name="server.build.dir" location="build"/>
<include file="../app/build.xml"/>
<target name="compile" depends="app.compile">
<echo message="Compiling server to ${server.build.dir} using classpath: ${app.build.dir}"/>
</target>
</project>
输出:Buildfile: D:worktestant-test2serverbuild.xml
app.compile:
[echo] Compiling to D:worktestant-test2serverbuild
compile:
[echo] Compiling server to D:worktestant-test2serverbuild using classpath: D:worktestant-test2serverbuild
BUILD SUCCESSFUL
Total time: 0 seconds
所需输出:Buildfile: D:worktestant-test2serverbuild.xml
app.compile:
[echo] Compiling to D:worktestant-test2appbuild
compile:
[echo] Compiling server to D:worktestant-test2serverbuild using classpath: D:worktestant-test2appbuild
BUILD SUCCESSFUL
Total time: 0 seconds
多模块构建是困难的,因为没有标准,每个构建作者都有自己的方法来解决这个问题。
我个人倾向于模仿Maven的操作方式。每个模块创建一个jar文件并将其发布到"本地"存储库。这个jar文件是使用它的类的其他模块的依赖项。这种方法在模块之间创建了清晰的分离,并且意味着在处理一个子模块时不需要构建整个项目。
那么如何使用ANT来完成呢?那么,您需要接受Maven的另一个概念,依赖管理。ivy插件为ANT提供了这个特性。
<标题> 例子我的虚拟项目。一个名为"app"的模块,它依赖于"server"模块
├── build.xml <-- Builds all modules in correct order
├── app
│ ├── build.xml
│ ├── ivy.xml <-- Describes module dependencies
│ └── src
| ..
└── server
├── build.xml
├── ivy.xml <-- Dependency on the "app" module
└── src
..
除非您自定义位置,否则ivy使用以下目录来存储文件:
~/.ivy2/cache <-- Downloaded 3rd party dependencies go here
~/.ivy2/local <-- Repository which is private to the user.
创建替代存储位置和利用Maven存储库管理器超出了这个问题的范围。
通过构建运行此示例后,生成以下显式版本化文件:
~/.ivy2/local/com.myspotontheweb/demo-app/1.0.0/jars/demo-app.jar
~/.ivy2/local/com.myspotontheweb/demo-server/1.0.0/wars/demo-server.war
build . xml
按正确的顺序构建所有模块。这是由每个模块的ivy.xml文件中记录的模块依赖关系决定的(参见ivy buildlist任务)。当你有大量相互依赖的模块时,这是一个非常有用的特性。
<project name="demo" default="build" xmlns:ivy="antlib:org.apache.ivy.ant">
<available classname="org.apache.ivy.Main" property="ivy.installed"/>
<target name="install-ivy" unless="ivy.installed">
<mkdir dir="${user.home}/.ant/lib"/>
<get dest="${user.home}/.ant/lib/ivy.jar" src="http://search.maven.org/remotecontent?filepath=org/apache/ivy/ivy/2.3.0/ivy-2.3.0.jar"/>
<fail message="Ivy has been installed. Run the build again"/>
</target>
<target name="build-list" depends="install-ivy">
<ivy:buildlist reference="build-path">
<fileset dir="." includes="**/build.xml" excludes="build.xml"/>
</ivy:buildlist>
</target>
<target name="build" depends="build-list">
<subant buildpathref="build-path">
<target name="clean"/>
<target name="publish"/>
</subant>
</target>
<target name="clean" depends="build-list">
<subant buildpathref="build-path">
<target name="clean"/>
</subant>
</target>
<target name="clean-all" depends="clean">
<ivy:cleancache/>
</target>
</project>
指出:
- 包含确保ivy jar依赖项被安装的逻辑,如果缺少
- Ivy将缓存下载的第三方依赖。"clean-all"任务对于确保构建非常干净非常有用:-)
应用中
列出该模块拥有的第三方依赖项。这是一个非常有用的Maven特性。依赖项从Maven Central自动下载。不需要将它们提交到源代码存储库中。
<ivy-module version="2.0">
<info organisation="com.myspotontheweb" module="demo-app"/>
<configurations>
<conf name="compile" description="Required to compile application"/>
<conf name="runtime" description="Additional run-time dependencies" extends="compile"/>
<conf name="test" description="Required for test only" extends="runtime"/>
</configurations>
<publications>
<artifact name="demo-app"/>
</publications>
<dependencies>
<!-- compile dependencies -->
<dependency org="org.slf4j" name="slf4j-api" rev="1.7.5" conf="compile->default"/>
<!-- runtime dependencies -->
<dependency org="org.slf4j" name="slf4j-log4j12" rev="1.7.5" conf="runtime->default"/>
<!-- test dependencies -->
<dependency org="junit" name="junit" rev="4.11" conf="test->default"/>
</dependencies>
</ivy-module>
注意:
- Ivy配置用于对依赖项进行分类和分组。稍后用于填充类路径
app/build . xml
非常标准的构建过程。代码经过编译、测试和打包。注意ivy配置是如何用来控制类路径的。
"publish"目标值得特别注意,它将构建的jar推送到一个本地位置,在那里它可以被其他模块构建拾取。
<project name="demo-app" default="build" xmlns:ivy="antlib:org.apache.ivy.ant">
<!--
================
Build properties
================
-->
<property name="src.dir" location="src/main/java"/>
<property name="resources.dir" location="src/main/resources"/>
<property name="test.src.dir" location="src/test/java"/>
<property name="build.dir" location="target"/>
<property name="dist.dir" location="${build.dir}/dist"/>
<property name="jar.main.class" value="org.demo.App"/>
<property name="jar.file" value="${dist.dir}/${ant.project.name}.jar"/>
<property name="pub.revision" value="1.0"/>
<property name="pub.resolver" value="local"/>
<!--
===========
Build setup
===========
-->
<target name="resolve" description="Use ivy to resolve classpaths">
<ivy:resolve/>
<ivy:report todir='${build.dir}/ivy-reports' graph='false' xml='false'/>
<ivy:cachepath pathid="compile.path" conf="compile"/>
<ivy:cachepath pathid="test.path" conf="test"/>
</target>
<!--
===============
Compile targets
===============
-->
<target name="resources" description="Copy resources into classpath">
<copy todir="${build.dir}/classes">
<fileset dir="${resources.dir}"/>
</copy>
</target>
<target name="compile" depends="resolve,resources" description="Compile code">
<mkdir dir="${build.dir}/classes"/>
<javac srcdir="${src.dir}" destdir="${build.dir}/classes" includeantruntime="false" debug="true" classpathref="compile.path"/>
</target>
<target name="compile-tests" depends="compile" description="Compile tests">
<mkdir dir="${build.dir}/test-classes"/>
<javac srcdir="${test.src.dir}" destdir="${build.dir}/test-classes" includeantruntime="false" debug="true">
<classpath>
<path refid="test.path"/>
<pathelement path="${build.dir}/classes"/>
</classpath>
</javac>
</target>
<!--
============
Test targets
============
-->
<target name="test" depends="compile-tests" description="Run unit tests">
<mkdir dir="${build.dir}/test-reports"/>
<junit printsummary="yes" haltonfailure="yes">
<classpath>
<path refid="test.path"/>
<pathelement path="${build.dir}/classes"/>
<pathelement path="${build.dir}/test-classes"/>
</classpath>
<formatter type="xml"/>
<batchtest fork="yes" todir="${build.dir}/test-reports">
<fileset dir="${test.src.dir}">
<include name="**/*Test*.java"/>
<exclude name="**/AllTests.java"/>
</fileset>
</batchtest>
</junit>
</target>
<!--
=====================
Build project
=====================
-->
<target name="build" depends="test" description="Create executable jar archive">
<ivy:retrieve pattern="${dist.dir}/lib/[artifact]-[revision](-[classifier]).[ext]" conf="runtime"/>
<manifestclasspath property="jar.classpath" jarfile="${jar.file}">
<classpath>
<fileset dir="${dist.dir}/lib" includes="*.jar"/>
</classpath>
</manifestclasspath>
<jar destfile="${jar.file}" basedir="${build.dir}/classes">
<manifest>
<attribute name="Main-Class" value="${jar.main.class}" />
<attribute name="Class-Path" value="${jar.classpath}" />
</manifest>
</jar>
</target>
<!--
=====================
Publish project
=====================
-->
<target name="publish" depends="build" description="Publish artifacts to shared repo">
<ivy:buildnumber organisation="${ivy.organisation}" module="${ivy.module}" revision="${pub.revision}"/>
<ivy:publish resolver="${pub.resolver}" pubrevision="${ivy.new.revision}">
<artifacts pattern="${build.dir}/dist/[artifact].[ext]"/>
</ivy:publish>
</target>
<!--
=============
Clean project
=============
-->
<target name="clean" description="Cleanup build files">
<delete dir="${build.dir}"/>
</target>
</project>
指出:
- ivy buildnumber任务对于确保每次运行构建时正确地增加构建号非常有用。它查看以前发布的文件。
服务器中
该模块只依赖于最新版本的"app"模块。实际的版本号是在构建时根据本地存储库中存在的文件确定的。
<ivy-module version="2.0">
<info organisation="com.myspotontheweb" module="demo-server"/>
<configurations>
<conf name="compile" description="Required to compile application"/>
<conf name="runtime" description="Additional run-time dependencies" extends="compile"/>
<conf name="test" description="Required for test only" extends="runtime"/>
</configurations>
<publications>
<artifact name="demo-server" type="war"/>
</publications>
<dependencies>
<!-- runtime dependencies -->
<dependency org="com.myspotontheweb" name="demo-app" rev="latest.integration" conf="runtime"/>
</dependencies>
</ivy-module>
服务器/build . xml
这个构建只是将这些库打包到一个WAR文件中。值得注意的是它使用了ivy检索任务。它将拉出"app"模块依赖项及其所有的传递依赖项。手动跟踪这些可能很困难。
<project name="demo-server" default="build" xmlns:ivy="antlib:org.apache.ivy.ant">
<!--
================
Build properties
================
-->
<property name="build.dir" location="target"/>
<property name="dist.dir" location="${build.dir}/dist"/>
<property name="war.file" value="${dist.dir}/${ant.project.name}.war"/>
<property name="pub.revision" value="1.0"/>
<property name="pub.resolver" value="local"/>
<!--
===========
Build setup
===========
-->
<target name="resolve" description="Use ivy to resolve classpaths">
<ivy:resolve/>
<ivy:report todir='${build.dir}/ivy-reports' graph='false' xml='false'/>
</target>
<!--
=====================
Build project
=====================
-->
<target name="build" depends="resolve" description="Create executable jar archive">
<ivy:retrieve pattern="${build.dir}/lib/[artifact]-[revision](-[classifier]).[ext]" conf="runtime"/>
<war destfile="${war.file}" webxml="src/resources/web.xml">
<lib dir="${build.dir}/lib"/>
</war>
</target>
<!--
=====================
Publish project
=====================
-->
<target name="publish" depends="build" description="Publish artifacts to shared repo">
<ivy:buildnumber organisation="${ivy.organisation}" module="${ivy.module}" revision="${pub.revision}"/>
<ivy:publish resolver="${pub.resolver}" pubrevision="${ivy.new.revision}">
<artifacts pattern="${build.dir}/dist/[artifact].[ext]"/>
</ivy:publish>
</target>
<!--
=============
Clean project
=============
-->
<target name="clean" description="Cleanup build files">
<delete dir="${build.dir}"/>
</target>
</project>
标题>
一个简单的方法是:在build.xml for app中代替
<property name="app.build.dir" location="build"/>
使用<property name="app.build.dir" location="../app/build"/>
如果您根据位置(并使用相对路径)指定属性,ant将解析相对于当前项目的路径。有了这个符号,ant首先上升到目录级别,然后下降到app目录,这是你的两个项目的正确路径。
更好的方法是将两个构建脚本使用的设置放在一个单独的属性文件中,并从两个构建中包含该文件。