Top 15 Ant Best Practices

Before Ant, building and deploying Java applications required a hodgepodge of platform-specific scripts, makefiles, proprietary IDEs, or manual processes. Now, nearly every open source Java project uses Ant. A great number of companies use Ant for internal projects as well. The widespread use of Ant in these projects has naturally led to an increased need for a set of well-established best practices.

This article summarizes several of my favorite Ant tips or best practices. Many were inspired by mistakes made on previous projects, or from horror stories relayed to me from other developers. One person told me of a project where XDoclet-generated code was placed into a version- control tool that locks files. When a developer changes a source file, they must remember to manually check out and lock all of the files that will be regenerated. They must then manually run the code generator, and only then can they tell Ant to compile the code. Here are some problems with this approach:

  • Generated code should not be stored in version control.
  • Ant (or XDoclet, in this case) should automatically determine which files will be affected by the next build. Programmers should not have to figure this out manually.
  • The Ant buildfile should define correct target dependencies so that programmers do not have to invoke targets in a particular order in order to get a good build.

Related Reading

Top 15 Ant Best Practices - 87426628 - 小鱼的博客

Java Extreme Programming Cookbook
By Eric M. BurkeBrian M. Coyner

When I start any new project, I begin by creating the Ant buildfile. Ant defines the build process and is used by every programmer on the team throughout the day. All of the tips in this article assume the Ant buildfile is an important artifact that must be written with care, maintained in version control, and refactored periodically. So here now are my top 15 Ant best practices.

1. Adopt Consistent Style Conventions

Ant users either love the XML buildfile syntax or hate it. Rather than jump into the middle of this fascinating debate, let's look at a few simple ways to keep the XML buildfile clean.

First and foremost, spend time formatting your XML so it looks visually appealing. Ant works with ugly or pretty XML, but ugly XML is hard to read. Provided you leave a blank line between targets, indent consistently, and avoid exceeding 90 or so columns of text, XML is surprisingly readable. Throw in a good editor or IDE that syntax highlights the XML, and you should not have any trouble getting by.

Also pick meaningful, human-readable names for targets and properties. For example, dir.reports is a better name than rpts. The specific naming convention is not important -- just come up with something and stick to it.

2. Put build.xml in the Project Root Directory

The Ant buildfile can reside anywhere, but putting build.xml in the top-level project directory keeps things simple and clean. This is the most common convention, and programmers expect to find build.xml in this location. Having the buildfile at the top directory also makes it conceptually easy to see how relative paths point to different directories in your project tree. Here is a typical project layout:

[root dir]
  | build.xml
  +--src
  +--lib (contains 3rd party JARs)
  +--build (generated by the build)
  +--dist (generated by the build)

When build.xml is in the top directory, you can compile code from the command line without changing your working directory, provided you are somewhere within the project directory tree. Just type this: ant -find compile. The -find argument tells Ant to search ancestor directories until it locates the buildfile.

3. Prefer a Single Buildfile

Some people prefer to break up large projects into several small buildfiles, each of which is responsible for a small portion of the overall build. This is strictly a matter of opinion, but beware that breaking up the build often makes it harder to wrap your head around the overall process. Be careful not to over-engineer a clever hierarchy of buildfiles when a single file can usually do the job.

Even if your project is divided into many different buildfiles, programmers expect to find a master build.xml in the project root directory. Make sure this buildfile is available, even if it only delegates actual work to subordinate builds.

4. Provide Good Help

Strive to make the buildfile self-documenting. Adding target descriptions is the easiest way to accomplish this. When you type ant -projecthelp, you see a listing of each target containing a description. For example, you might define a target like this:

<target name="compile" 
  description="Compiles code, output goes to the build dir.">

The simple rule is to include descriptions for all of the targets you wish programmers to invoke from the command line. Internal targets should not include description attributes. Internal targets may include targets that perform intermediate processing, such as generating code or creating output directories.

Another way to provide help is to include XML comments in the buildfile. Or, define a target named help that prints detailed usage information when programmers type ant help.

<target name="help" 
        description="Display detailed usage information">
  <echo>Detailed help...</echo>
</target>

5. Provide a Clean Target

Every buildfile should include a target that removes all generated files and directories, bringing everything back to its original, pristine state. All files remaining after a clean should be those found in version control. For example:

<target name="clean" 
    description="Destroys all generated files and dirs.">
  <delete dir="${dir.build}"/>
  <delete dir="${dir.dist}"/>
</target>

Do not automatically invoke clean, unless perhaps you have some special target for generating a full release. When programmers are merely compiling or performing other tasks, they do not want the buildfile to perform a full cleanup before proceeding. This is both annoying and counterproductive. Trust programmers to decide when they are ready to clean all files.

6. Manage Dependencies Using Ant

Suppose your application consists of a Swing GUI, a web interface, an EJB tier, and shared utility code. In large systems, you need to clearly define which Java packages belong to which layer of the system. Otherwise, you end up being forced to compile hundreds or thousands of files each time you change something. Poor dependency management leads to overly complex, brittle systems. Changing the layout of a GUI panel should not cause you to recompile your servlets and EJBs.

As systems get bigger, it is easy to inadvertently introduce server-side code that depends on client-side code, or vice versa. This is because the typical IDE project compiles everything using a monolithic classpath. Ant lets you control the build more effectively.

Design your Ant buildfile to compile large projects in stages. First, compile shared utility code. Place the results into a JAR file. Then, compile a higher level portion of the project. When you compile higher-level code, compile against the JAR file(s) created in the first step. Repeat this process until you reach the highest level of the system.

Building in stages enforces dependency management. If you are working on a low-level framework Java class and accidentally refer to a higher-level GUI panel, the code will not compile. This is due to the fact that when the buildfile compiles the low-level framework, it does not include the high-level GUI panel code in the source path.

7. Define and Reuse Paths

A buildfile is often easier to understand if paths are defined once in a central location, and then reused throughout the buildfile. Here is an example that shows this in action.

<project name="sample" default="compile" basedir=".">
  <path id="classpath.common">
    <pathelement location="${jdom.jar.withpath}"/>
    ...etc
  </path>
  <path id="classpath.client">
    <pathelement location="${guistuff.jar.withpath}"/>
    <pathelement location="${another.jar.withpath}"/>
    <!-- reuse the common classpath -->
    <path refid="classpath.common"/>
  </path>
  <target name="compile.common" depends="prepare">
    <javac destdir="${dir.build}" srcdir="${dir.src}">
          <classpath refid="classpath.common"/>
          <include name="com/oreilly/common/**"/>
    </javac>
  </target>
</project>

Techniques like this always gain more value as the project grows and the build gets progressively more complex. You will probably have to define different paths to compile each tier of the application, as well as paths to run unit tests, run the application, run XDoclet, generate JavaDocs, etc. This modular path approach is preferable to a gigantic path for everything. Failure to modularize makes it easy to lose track of dependencies.

8. Define Proper Target Dependencies

Suppose the dist target depends on the jar target, which depends on compile, which depends on prepare. Ant buildfiles ultimately define a dependency graph, which must be carefully defined and maintained.

Periodically review the dependencies to ensure that your builds do the right amount of work. Large buildfiles tend to degrade over time as more targets are added, so you end up with unnecessary dependencies that cause your builds to work too hard. For example, you might find yourself regenerating the EJB code when the programmer actually only wanted to compile some GUI code that does not use EJB at all.

Omitting dependencies in an effort to "optimize" the build is another common mistake. This is a mistake because it forces programmers to remember to invoke a series of targets in a particular order in order to get a decent build. A better solution exists: provide public targets (those with descriptions) that contain correct dependencies, and another set of "expert" targets that let you manually execute individual build steps. These steps do not guarantee a complete build, but let expert users bypass steps during quick and dirty coding sessions.

9. Use Properties for Configurability

Any piece of information that needs to be configured, or that might change, should be defined as an Ant property. The same is true for values that are used in more than one place in the buildfile. Properties should be defined either at the top of a buildfile or in a standalone properties file for maximum flexibility. Here is how properties look when defined in the buildfile:

<project name="sample" default="compile" basedir=".">
  <property name="dir.build" value="build"/>
  <property name="dir.src" value="src"/>
  <property name="jdom.home" value="../java-tools/jdom-b8"/>
  <property name="jdom.jar" value="jdom.jar"/>
  <property name="jdom.jar.withpath" 
                    value="${jdom.home}/build/${jdom.jar}"/>
    etc...
</project>

Or, you can use a properties file:

<project name="sample" default="compile" basedir=".">
  <property file="sample.properties"/>
   etc...
</project>

And in sample.properties:

dir.build=build
dir.src=src
jdom.home=../java-tools/jdom-b8
jdom.jar=jdom.jar
jdom.jar.withpath=${jdom.home}/build/${jdom.jar}

Having a separate file for properties is beneficial because it explicitly defines the configurable portion of the build. You can provide a different version of this properties file for different platforms, or for developers working on different operating systems.

10. Keep the Build Process Self-Contained

To the greatest extent possible, do not refer to external paths and libraries. Above all, do not rely on the programmer's CLASSPATH setting. Instead, use relative paths throughout your buildfile and define your own paths. If you refer to an explicit path such as C:\java\tools, other developers will not be able to use your buildfile because they are highly unlikely to use the same directory structure.

If you are deploying an open source project, provide a distribution that includes all JAR files necessary to compile your code, subject to licensing restrictions, of course. For internal projects, dependent JAR files should be managed under version control and checked out to a well-known location.

When you do have to refer to external paths, define the paths as properties. This lets programmers override those settings to conform to their own machines. You can also refer to environment variables using this syntax:

<property environment="env"/>
<property name="dir.jboss" value="${env.JBOSS_HOME}"/>

Related Reading

Top 15 Ant Best Practices - 87426628 - 小鱼的博客

Ant: The Definitive Guide
By Jesse E. TillyEric M. Burke

11. Use Version Control

The buildfile is an important artifact that should be versioned, just like source code. When you tag or label your code, apply the same tag or label to your buildfile. This lets you go back to a previous release and build the software using the buildfile as it was back then.

In addition to the buildfile, you should maintain third-party JAR files in version control. Again, this makes it possible to recreate previous releases of your software. This also makes it easier to ensure that all developers have the same JAR files, because they can check them out of version control to a path relative to the buildfile.

Generally, avoid storing build output in version control. Provided that your source code is versioned properly, you should be able to recreate any previous release through the build process.

12. Use Ant as the Least Common Denominator

Suppose your team uses an IDE. Why bother with Ant when programmers can click the lightning bolt icon to rebuild the whole application?

The problem with IDEs is one of consistency and reproducibility across a team of members. IDEs are almost always designed for individual programmer productivity, not for consistent builds across a team of developers. Typical IDEs require each programmer to define his or her own project file. Programmers may have different directory structures, may use different versions of various libraries, or may be working on different platforms. This leads to situations where code that compiles fine for Bob may not build properly for Sally.

Regardless of what IDE your team uses, set up an Ant buildfile that all programmers use. Make a rule that programmers perform an Ant build before checking new code into version control. This ensures that code is always built from the same Ant buildfile. When problems arise, perform a clean build using the project-standard Ant buildfile, not someone's particular IDE.

Programmers should be free to use whatever IDE or editor they are comfortable with. Use Ant as a common baseline to ensure the code is always buildable.

13. Use zipfileset

People often use Ant to create WAR, JAR, ZIP, and EAR files. These files generally require a particular internal directory structure, one that usually does not match the directory structure of your source code and build environment.

An extremely common practice is to write an Ant target that copies a bunch of files to a temporary holding area using the desired directory structure, and then create the archive from there. This is not the most efficient approach. Using a zipfileset is a better solution. This lets you select files from any location and place them in the archive file using a different directory structure. Here is a small example:

<ear earfile="${dir.dist.server}/payroll.ear"
    appxml="${dir.resources}/application.xml">
  <fileset dir="${dir.build}" includes="commonServer.jar"/>
  <fileset dir="${dir.build}">
    <include name="payroll-ejb.jar"/>
  </fileset>
  <zipfileset dir="${dir.build}" prefix="lib">
    <include name="hr.jar"/>
    <include name="billing.jar"/>
  </zipfileset>
  <fileset dir=".">
    <include name="lib/jdom.jar"/>
    <include name="lib/log4j.jar"/>
    <include name="lib/ojdbc14.jar"/>
  </fileset>
  <zipfileset dir="${dir.generated.src}" prefix="META-INF">
    <include name="jboss-app.xml"/>
  </zipfileset>
</ear>

In this example, all JAR files are placed in the lib directory of the EAR. The hr.jar and billing.jar files are copied from our build directory, therefore we use zipfileset to move them to the lib directory inside of the EAR. The prefix attribute specifies the destination directory in the EAR.

14. Perform the Clean Build Test

Assuming your buildfile has clean and compile targets, perform the following test. First, type ant clean. Second, type ant compile. Third, type ant compile again. The third step should do absolutely nothing. If files compile a second time, something is wrong with your buildfile.

A buildfile should perform work only when input files change with respect to corresponding output files. A build process that compiles, copies, or performs some other work when it is not necessary to perform the work is inefficient. Even small inefficiencies become big problems when a project grows in size.

15. Avoid Platform-Specific Ant Wrappers

For whatever reason, some people like to ship their products with simple batch files or scripts called something like compile. Look inside of the script and you will find the following:

ant compile

Developers are familiar with Ant and are perfectly capable of typing ant compile. Do not include a platform-specific script that does nothing but invoke Ant. Your script becomes one more little annoyance for people to study and understand when they are looking at your tool for the first time. It is also likely that you will fail to provide scripts for every operating system out there, which will really annoy some users.

Summary

Too many companies rely on manual processes and ad hoc procedures for compiling code and creating software distributions. Teams that do not use a defined build process with Ant or similar tools spend an amazing amount of time tracking down problems when code compiles for some developers but fails for others.

Creating and maintaining build scripts is not glamorous work, but is necessary. A well-crafted Ant buildfile lets you focus on what you enjoy most -- writing code!

References

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: CMake是一个跨平台的生成代码工具,用于管理项目构建过程,它可以自动化编译过程中的各种任务,如编译、链接和打包等。在使用CMake时,有一些最佳实践需要遵循,以确保项目构建的正确性和高校。CMake Best Practices PDF是一份指南,旨在帮助用户了解如何使用CMake,并通过最佳实践来管理和构建项目。 该指南的主要内容包括使用现代CMake方法编写清晰、可读和可维护的CMake脚本,按功能分割项目并将其组织成库、模块和应用程序,并基于目标架构编写通用CMake文件,以确保跨平台的兼容性。此外,该指南还提供了一些实用技巧和工具,如依赖管理、安装和打包,以帮助开发人员进行更好的构建管理和项目发布。 总的来说,CMake Best Practices PDF是一份非常有价值的指南,旨在帮助开发人员遵循最佳实践来编写和管理CMake项目,以提高项目构建的高校和可维护性。 ### 回答2: 《CMake最佳实践(CMake Best Practices)》是一份技术文档,主要介绍了使用CMake构建C++项目的最佳实践。该文档由Daniel Pfeifer和其它CMake社区成员合作编写,为开发人员提供了一系列关于如何使用CMake构建现代C++应用程序的经验和建议。 该文档详细介绍了CMake的基本工作原理,并提供了使用CMake构建项目的步骤。同时,该文档还着重强调了在编写CMakeLists.txt文件时应该注意的细节,并提供了一些令人信服的理由。 例如,该文档建议编写简洁、模块化的CMakeLists.txt文件,避免使用过多的IF语句和过多的变量。此外,该文档也强调了对目标平台进行适当的测试、生成合适的安装脚本以及使用包管理器等最佳实践。 通过该文档的指导,开发人员可以更好地理解CMake的使用,确保其具有良好的可移植性、可扩展性和可维护性。该文档是CMake社区的重要资源之一,为C++开发人员提供了优秀的参考资料。 ### 回答3: CMake是一款功能强大的构建工具,可用于生成跨平台的软件代码。为了确保项目的顺利构建和维护,需要遵循一些最佳实践。这些实践包括: 1. 确保每个项目都有自己的CMakeLists.txt文件,以便更好地组织项目的结构和依赖关系。同时,应根据需要使用外部项目和库。 2. 编写可移植的CMake代码。这可以帮助确保代码在不同的平台上能够正确地构建和运行。 3. 使用版本控制工具管理代码,并确保将CMake配置文件包含在版本控制系统中。这可以确保在不同的开发环境之间保持一致。 4. 将构建和安装相关的命令分离到不同的CMake文件中,以便更好地管理项目。可以使用类似于“build.sh”或“install.sh”的脚本来组织这些文件。 5. 使用CMake变量来拆分项目配置,以便更好地管理和维护代码。 6. 如果有必要,可以使用预处理器语句来动态配置CMake文件。这可以帮助实现更高级的构建过程。 7. 编写清晰、易于理解的CMake文件。必要时可以添加注释或文档,以便其他开发人员能够理解和维护代码。 总之,CMake是一款非常有用的构建工具,可以帮助简化复杂的项目结构和依赖关系。但是,为了确保项目的顺利构建和维护,需要遵循一些最佳实践,并编写清晰、易于理解的CMake文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值