如何正确导入ant build文件中的属性和目标?

时间:2014-08-04 08:39:46

标签: ant build multi-module

我想用两个模块制作项目。应用和服务器。服务器取决于应用程序当我编译服务器时,我想将app中的类文件包含到构建中。但是由于导入问题,它相对于服务器而不是应用程序解析了类路径。如何使ant相对于服务器相对于应用程序和服务器位置解析应用程序位置。我不知道它是如何在蚂蚁文档中完成的。你能用更简单的方式解释一下吗?代码片段稍微澄清一下问题。

App 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:\work\test\ant-test2\server\build.xml

app.compile:
 [echo] Compiling to D:\work\test\ant-test2\server\build

compile:
 [echo] Compiling server to D:\work\test\ant-test2\server\build using classpath:  D:\work\test\ant-test2\server\build

BUILD SUCCESSFUL
Total time: 0 seconds

期望的输出:

Buildfile: D:\work\test\ant-test2\server\build.xml

app.compile:
 [echo] Compiling to D:\work\test\ant-test2\app\build

compile:
 [echo] Compiling server to D:\work\test\ant-test2\server\build using classpath:  D:\work\test\ant-test2\app\build

BUILD SUCCESSFUL
Total time: 0 seconds

2 个答案:

答案 0 :(得分:2)

多模块构建很难,因为没有标准,每个构建作者都有自己的方法来解决这个问题。

我个人的偏好是模仿Maven如何做到这一点。每个模块都创建一个jar文件并将其发布到“本地”存储库。然后,此jar文件是使用其类的其他模块的依赖项。这种方法可以在模块之间创建清晰的分离,这意味着您在处理一个子模块时无需构建整个项目。

那么如何使用ANT完成?那么你需要拥抱另一个Maven概念,依赖管理。 ivy plugin为ANT提供此功能。

实施例

我的虚拟项目。一个名为“app”的模块,它是“服务器”模块的依赖

├── 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存储库管理器超出了本问题的范围。

通过build运行此示例后,将生成以下显式版本化文件:

~/.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将缓存下载的第三方依赖项。 “全干净”任务对于确保构建清洁干净非常有用: - )

应用程序/的ivy.xml

列出模块具有的第三方依赖项。这是一个非常有用的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>

注意:

  • 常春藤配置用于对依赖项进行分类和分组。稍后用于填充类路径

应用程序/ build.xml中

非常标准的构建过程。代码经过编译测试和打包。请注意如何使用常春藤配置来控制类路径。

“发布”目标值得特别注意,它会将构建的jar推送到local location,以便其他模块构建可以将其拾取。

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

注意:

  • 常春藤buildnumber任务对于确保每次运行构建时正确递增构建号非常有用。它查看以前发布的文件。

服务器/的ivy.xml

此模块对“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文件。值得注意的是它使用了常春藤retrieve任务。它将拉出“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>

答案 1 :(得分:0)

一个简单的方法如下:在app的build.xml中而不是

<property name="app.build.dir" location="build"/>

使用

<property name="app.build.dir" location="../app/build"/>

如果按位置指定属性(并使用相对路径),则ant会解析相对于当前项目的路径。使用这种表示法,ant首先上升到目录级别,然后下到app dir,这两个项目都是正确的。

更好的方法是将两个构建脚本使用的设置放在一个单独的属性文件中,并在两个构建中包含此文件。