基于 Ant 构建的 Java 项目布局最佳实践
我有点震惊,(如果?)这个精确的问题没有被问到,但是 15 分钟的扫描并没有找到完全匹配的结果。 (如果我错了,请指出正确的方法并投票关闭...)
问题 1:
在 Ant 构建系统下布局 Java 项目的最佳实践是什么? 出于我们的目的,我们有以下上下文(也许其中大部分是无关紧要的):
- 大多数开发人员都在使用 Eclipse(不是全部)
- 项目是在 subversion 中维护的
- 项目构建最近已迁移到 Hudson,我们希望在其中使用发布插件管理版本和一些自定义脚本来处理自动化部署
- 该项目是一个“传统”应用程序,一种“生产原型”,用户池非常有限,但它们位于具有气隙分离的远程站点,因此提供版本化、可跟踪的易于安装、手动数据收集/恢复和远程诊断的工件非常重要。
- SVN 存储库中包含一些依赖项(JAR);如有必要,可以在构建时通过 ant 脚本获取其他内容。还没有像 Ivy 那样奇特的东西(请不要告诉我切换到 Maven3...我知道,如果/当适当的时间到来时我们会这样做。)
- 构建包括 JUnit、FindBugs、CheckStyle、PMD、JavaDoc、一些自定义文档生成
- 两个或三个主要 JAR 工件(一个主应用程序工件加上几个最小的 API JAR,用于包含在一些耦合应用程序中)
- 想要分发以下分发工件:
- “完整”源代码 + bin 压缩包,其中包含已解决的所有依赖项、预构建的 jar 和 JavaDoc
- 一个 bin tarball,仅包含文档和 JavaDoc、jar 和辅助包装脚本等
- “合作伙伴”源代码+bin,其中包含合作伙伴开发人员可能查看的“共享”源代码以及关联的测试用例
当前结构如下所示
project-root/ project-root/source project-root/source/java // main application (depends on -icd) project-root/source/java-icd // distributable interface code project-root/source/test // JUnit test sources project-root/etc // config/data stuff project-root/doc // pre-formatted docs (release notes, howtos, etc) project-root/lib // where SVN-managed or Ant-retrieved Jars go project-root/bin // scripts, etc...
在构建时,它扩展为包括:
build/classes // Compiled classes build/classes-icd build/classes-test build/javadoc build/javadoc-icd build/lib // Compiled JAR artifacts build/reports // PMD, FindBugs, JUnit, etc... output goes here build/dist // tarballs, zipfiles, doc.jar/src.jar type things, etc.. build/java // Autogenerated .java products build/build.properties // build and release numbering, etc...
问题 2:
如何在开发树中保持修订控制之间的严格分离项目和构建时工件同时产生如上所述的连贯分布并且允许我在开发和测试期间将开发树视为操作/分布?特别是,我讨厌让我的
任务将 .jar 文件放入顶级 lib
目录中——开发人员树中的该目录是不可侵犯的SVN领地。但是使用 build/lib/*.jar
分发一些供公众使用的东西是一件令人困惑的烦恼。对于文档和其他构建的工件也是如此,我们希望它们出现在发行版中一致的位置,但不希望开发人员和用户使用完全不同的目录结构。
将所有生成的产品放在单独的 build/
目录中对于开发时间来说非常好,但分发时却是一个烦人的工件。出于分发目的,我宁愿将所有 JAR 放在一个 lib
位置,事实上,像下面这样的结构最有意义。目前,我们通过在构建 .tar.gz 和 .zip 工件时进行一些复杂的路径操作,使用 ant dist 动态构建此结构。
我认为 dist 应该是这样的:
project-root/ project-root/source // present in only some dists project-root/etc // same as in development tree project-root/doc // same as in development tree project-root/doc/javadoc // from build project-root/lib // all dependency and built JAR files project-root/bin // same as in development tree build.properties // build and release numbering, etc...
这个问题狭义上是关于“如何维护清洁开发和分发项目布局?”正如我上面所问的;还收集有关 Java/Ant 项目布局的一般信息,以及对我们特定方法的批评。 (是的,如果您认为它应该是一个社区 Wiki,我会这样做......)
I'm a little shocked that (if?) this precise questions hasn't been asked, but 15 minutes of scanning SO didn't turn up an exact match. (If I'm wrong, point me the right way and vote to close...)
Question 1:
What are the best practices for laying out Java projects under an Ant build system?
For our purposes, we have the following context (perhaps most of which is irrelevant):
- Most developers are using Eclipse (not all)
- Project is maintained in subversion
- Project builds have recently migrated to Hudson, in which we want to use the release plugin to manage releases, and some custom scripts to handle automated deployment
- This project is a "conventional" application, a sort of "production prototype" with a very limited pool of users, but they are at remote sites with airgap separation, so delivering versioned, traceable artifacts for easy installation, manual data collection/recovery, and remote diagnosis is important.
- Some dependencies (JARs) are included in the SVN repo; others may be fetched via the ant script at build time, if necessary. Nothing fancy like Ivy yet (and please don't tell me to switch to Maven3... I know, and we'll do so if/when the appropriate time comes.)
- Build includes JUnit, FindBugs, CheckStyle, PMD, JavaDoc, a bit of custom documentation generation
- Two or three primary JAR artifacts (a main application artifact plus a couple of minimal API JARs for inclusion in a few coupled applications)
- Desire to distribute the following distribution artifacts:
- a "Full" source+bin tarball with all dependencies resolved, jars and JavaDoc prebuilt
- a bin tarball, with just the docs and JavaDoc, jars, and ancillary wrapper scripts etc
- a "Partner" source+bin, which has the "shared" source that partner developers are likely to look at, and associated testcases
Current structure looks like this
project-root/ project-root/source project-root/source/java // main application (depends on -icd) project-root/source/java-icd // distributable interface code project-root/source/test // JUnit test sources project-root/etc // config/data stuff project-root/doc // pre-formatted docs (release notes, howtos, etc) project-root/lib // where SVN-managed or Ant-retrieved Jars go project-root/bin // scripts, etc...
At build time, it expands to include:
build/classes // Compiled classes build/classes-icd build/classes-test build/javadoc build/javadoc-icd build/lib // Compiled JAR artifacts build/reports // PMD, FindBugs, JUnit, etc... output goes here build/dist // tarballs, zipfiles, doc.jar/src.jar type things, etc.. build/java // Autogenerated .java products build/build.properties // build and release numbering, etc...
Question 2:
How can I maintain strict separation in the development tree between revision-controlled items and build-time artifacts WHILE producing a coherent distribution as above AND allowing me to treat a development tree as a operational/distribution during development and testing? In particular, I'm loathe to have my <jar>
task drop .jar files in the top-level lib
directory -- that directory in the developers' trees is inviolable SVN territory. But distributing something for public use with build/lib/*.jar
is a confusing annoyance. The same is true of documentation and other built artifacts that we want to appear in a consistent place in the distribution, but don't want to have developers and users use completely different directory structures.
Having all the generated products in a separate build/
directory is very nice for development-time, but it's an annoying artifact to distribute. For distribution purposes I'd rather have all the JARs sitting in a single lib
location, in fact, a structure like the below makes the most sense. Currently, we build this structure on the fly with ant dist
by doing some intricate path manipulations as .tar.gz and .zip artifacts are built.
What I think the dist should look like:
project-root/ project-root/source // present in only some dists project-root/etc // same as in development tree project-root/doc // same as in development tree project-root/doc/javadoc // from build project-root/lib // all dependency and built JAR files project-root/bin // same as in development tree build.properties // build and release numbering, etc...
This question is narrowly about the "how do I maintain clean development and distribution project layouts?" as I asked above; but also to collect info about Java/Ant project layouts in general, and critiques of our particular approach. (Yes, if you think it should be a Community Wiki I'll make it so...)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
我的一个建议是,您分发的目录树不应该是 CVS 中的目录树。有一个脚本,将 build 下的 dist 目录放在一起,然后将其压缩。该脚本可以将源代码控制的文件和派生文件与其核心内容结合起来。它还可以执行诸如清除您不想分发的 SVN 目录之类的操作。如果您希望能够以相同的方式处理开发树和分布式树,只需确保 dist 的布局与开发项目的布局相同 - 最简单的方法是复制除 build 子目录之外的所有内容(还有 CVS 目录,也许还有 Eclipse .project 和 .classpath 之类的东西)。
我怀疑你不会喜欢这个建议。您可能认为分布式文件只是您的开发环境的可移植版本 - 但我认为事实并非如此,永远不可能,也不需要如此。如果你能接受这个想法,你可能会觉得我的建议很不错。
编辑:我对此进行了更多思考,并查看了我使用的一些脚本。我认为在这种情况下我会做的是即使在开发中也构建一个单独的树;将执行环境指向project-root/build/app(或者可能是project-root/build,如果可以的话)而不是project-root,然后符号链接(或者如果没有符号链接则复制)所有必需的(无论是静态的) ,从项目根目录中,或从构建中派生)到其中。构建一个发行版可能就像压缩该树一样简单(当然,使用解析符号链接的工具)。这样做的好处是它允许执行树的结构非常干净 - 它不会包含源目录、IDE 文件、构建脚本或项目内部的其他支持文件等。如果您使用 Subversion ,它仍将包含从静态区域符号链接的任何内容内的 .svn 目录;如果您使用 Mercurial,它不会包含任何 .hg 内容。
My one suggestion would be that the directory tree you distribute should not be the one in CVS. Have a script which puts together a dist directory under build, then zips that up. That script can combine source-controlled and derived files to its heart's content. It can also do things like scrub out SVN directories, which you don't want to distribute. If you want to be able to treat development and distributed trees in the same way, simply ensure that the layout of dist is the same as the layout of the development project - the easiest way to do that would be to copy everything except the build subdirectory (and CVS directories, and perhaps things like the Eclipse .project and .classpath).
I suspect you won't like this suggestion. It may be that you are attached to the idea that the distributed file is simply a portable version of your development environment - but i think it's the case that it isn't, it can never be, and it doesn't need to be. If you can accept that idea, you might find my suggestion agreeable.
EDIT: I thought about this a bit more, and looked at some of the scripts i use. I think what i'd do in this situation is to build a separate tree even in development; point the execution environment at project-root/build/app (or perhaps project-root/build if you can) rather than project-root, and then symlink (or copy if you don't have symlinks) all the necessaries (whether static, from in the project root, or derived, from in build) into that. Building a distribution may then be as simple as zipping up that tree (with a tool that resolves symlinks, of course). The nice thing about this is it allows the structure of the executed tree to be very clean - it won't contain source directories, IDE files, build scripts, or other supporting files from inside the project, etc. If you're using Subversion, it will still contain .svn directories inside anything symlinked from the static areas; if you were using Mercurial, it wouldn't contain any .hg stuff.
在布局方面,我们使用的东西已经演变成非常接近 Maven 布局的东西(参见此处)。这是一个非常实用的布局,已经被很多人使用过。而且,如果您想稍后切换到 Maven,那么一切都已准备就绪。我们有几个变体,其中最重要的是我们将自动化单元测试和集成测试分开。
就混合源和构建工件而言 - 我当然会建议反对。正如您所看到的,它会扰乱 IDE 索引和版本控制,通常会让生活变得困难。
据我所知,您要么必须接受这种混合,要么将依赖项复制为构建的一部分,并将输出视为一个单独的项目 - 如果您需要的话,也许会不断在另一个 IDE 窗口中打开。无论如何,将发布包的“作为用户”与“作为制作者”混合在一起的想法听起来会令人困惑。
Layout-wise, we use something which has evolved into something very close to a Maven layout (see here). This is a very functional layout which has been used by a lot of people. And, if you want to switch to Maven later, you're all set. We have a couple of variations, the most important of which is that we separate automated unit- and integration-tests.
In terms of mingling sources and build artefacts - I would certainly recommend against it. As you've seen, it messes with IDE indexing and version control and generally makes life difficult.
As far as I can tell you either have to accept this mingling, or copy your dependencies as part of the build and treat the output as a separate project - perhaps constantly open in another IDE window if you need it. The idea of mixing your work 'as a user' versus 'as a producer' of your release package sounds like it would be confusing, anyway.
http://ant.apache.org/ant_in_anger.html
该项目包含子目录
http://ant.apache.org/ant_in_anger.html
The project contains sub directories
sun/oracle 还提供了一些(可能有点过时的)关于项目布局的一般建议,您可能想看看:
端到端 Java 应用程序的指南、模式和代码
There are also (maybe a bit outdated) general recommendations from sun/oracle for a project layout you maybe want to take a look at:
Guidelines, Patterns, and Code for End-to-End Java Applications