扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者:builder.com.cn 来源:来源网站 2007年11月23日
关键字:
我手头上有一个 Java 项目,在过去几年的时间里,我小心翼翼地为之维护一个 Ant 构建脚本。Ant 能够执行大量任务,这一点我十分欣赏;然而,我常常发现,Ant 脚本的 XML 语法编写起来有些麻烦。而且,在 可表达性 方面,Ant 的 XML 部分还存在限制。实际上,当我发现自己需要更高程度的灵活性(例如在条件逻辑方面)时,我常常不得不在 Ant 的 script
任务中编写(例如使用 Groovy)定制任务或嵌入式逻辑。
数年前,当我在寻找可表达性更强的软件构建工具时,我听说过 Rake。Rake 是用于使用功能全面的 Ruby 编程语言构建软件的一种域特定语言。通过 Rake,我可以轻松获得正在寻找的可表达性。例如,我不必编写定制任务,因为 Rake 构建脚本是 Ruby(而不是 XML);因此,如果我需要某种条件逻辑,那么很容易在需要的地方编写这种逻辑。而且,Rake 采用基于依赖的任务系统,这类似于 Ant 及其前辈 make 中的任务系统。例如,如果输入 rake deploy
,Rake 将调用之前设置的依赖任务,例如编译,运行测试等等。而且 Rake 非常智能,它只运行一次依赖性任务(就像 Ant 一样)。
对于大多数 Java 项目来说,Rake 本身并不能提供多大帮助;然而,一个相对较新的项目 Raven 将 Rake 的功能和可表达性与用于构建 Java 应用程序的特定任务结合起来。有了 Raven,Java 开发人员就有了一个真正可行的构建平台,该平台可提供特定于 Java 的特性,例如使用 javac
进行编译以及使用 jar
和 war
归档二进制文件,同时还拥有 Ruby 编程语言的丰富的可表达性。
|
在将 Raven 用于 Java 项目之前,理解 Raven 与 Rake 之间的关系会有所帮助。图 1 显示了 Raven 的一个逻辑图,可以看到,Raven 使用 Rake、RubyGems 和 Ruby。相应地,在使用 Raven 时,可以使用本地 Rake 任务。而且,还可以在 Raven 脚本中使用 Ruby。RubyGems 是类似于 CPAN、RPM 或 APT 的一个包管理器,用于下载必需的 Ruby 包(以及 Java jar)和任何依赖包。
虽然还不确定 Raven 是不是构建 Java 项目的最佳 平台,但是可以发现,在转向更自然的编程语言方面,Raven 提供了一个简单而强大的组合。要是让我来说,单凭不必依赖 XML 就可构建 Java 项目这一点,足以使 Raven 获得更多关注。
|
有两种方法可安装和配置 Raven,第一种方法是 Ruby 方法,第二种方法则更倾向于 Java,即使用 JRuby。我推荐 Java 方法,因为这样更容易配置。安装 Raven 和使之运行需要以下步骤:
JRUBY_HOME
。JRUBY_HOME/bin
添加到 PATH
环境变量中。jruby -version
,以检查安装情况。如果完全安装成功,那么在命令窗口中应该看到类似于 ruby 1.8.5 (0) [java]
的内容。 当然,下载和安装 Raven 只是小菜一碟。真正有趣的是在创建构建脚本时 Raven 具有的灵活性。在 Raven 术语中,构建文件被称作 Rakefiles,它们有一个简单的格式。实际上,如果您用 Ruby 编写过程序(或者见过 Ruby 代码),那么这些文件看上去会很熟悉。这正是 Raben 的美妙之处:它就是 Ruby — 只不过在其中添加了一些特殊的词。
为了尽快使用 Raven ,毫无疑问,首先我要执行最基本(但是也很重要)的任务,即编译。图 2 中的目录结构表示了我的 Java 项目的布局。如果知道第三方库和源代码所在的位置,就能更快上手了。
注意图 2 中的 lib 结构,第三方 JAR 就在其中。在开始编译之前,必须创建一个路径,使用 Raven 做这件事再容易不过了,因为它支持通过它的 dependency
任务(实际上是 Raven 提供的一个名为 dependency
的 Ruby 方法)创建类路径,如清单 1 所示:
require 'raven' require 'rake' dependency 'deps' do | task | task.libs = Dir.glob('lib/**/*.*') end |
根据清单 1 中的定义,deps
依赖任务使用 Dir
类和 glob()
方法包括 lib 目录中的所有库。还可以使用这个依赖任务(通过 RubyGems)从一个储存库中下载 JAR(这类似于 Maven 的依赖管理机制)。
在清单 1 中,我特意在路径中包括了 lib 目录中的所有东西。我将这个任务命名为 deps
,在尝试编译任何文件之前,都可以显式地执行该任务,如清单 2 所示:
javac 'compile' => 'deps' do |task|
task.build_path << "src"
end
|
可以看到,我通过 Raven 的 javac
任务执行编译。该任务名为 compile
,它对清单 1 中的 deps
任务存在依赖。符号 =>
的意思是,Raven 将在当前任务之前 运行该操作符右边的任务(这很像 Ant 中 target
元素的 depends
属性。
魔力就发生在 javac
任务的 do
和 end
之间。默认情况下,javac
任务使用 "src/main/java"
作为 build_path
的默认值。但是,由于我的 Java 项目并没有遵循这个惯例(还记得 图 2 吗?),我需要修改 build_path
属性,在这里它应该为 src
,因为源代码就在其中。
|
为了测试 Raven 脚本,需要打开一个命令提示符,然后输入:rake compile
。编译后会生成 .class 文件,这些文件被放到一个名为 target/classes 的目录中,该目录是由 javac
Raven 任务自动生成的。
这里要弄清楚一点。我不必编写很多代码就可以完成编译,因为 Raven 在幕后处理了一些事情,例如创建 target/classes 目录并将生成的类文件复制到这个位置。实际上,您可能已经忘了 清单 1 和 清单 2 中对应的 Ant 脚本是什么样子。这里提示一下,清单 3 显示了一个典型的使用 Ant 构建脚本的编译:
<?xml version="1.0" encoding="iso-8859-1"?> <project name="compile-code" default="all" basedir="."> ... <target name="compile-src"> <mkdir dir="${classes.dir}"/> <javac destdir="${classes.dir}" debug="true"> <src path="${src.dir}" /> <classpath refid="project.class.path"/> </javac> </target> <project> |
将清单 3 与 清单 1 和 清单 2 比较,可以看出一些明显的不同。Raven 不但废除了 XML,而且还假定一些基本的属性,使您在定义任务时需要指定的东西更少。而且,如清单 2 所示,如果有必要,Raven 允许重写假定的属性。
虽然从比较的角度来看清单 1 和清单 2 确实比较有趣,但编译只是构建脚本的有趣之处的一个开始。让我们来看一个更有趣的特性:打包 Java Web 应用程序。
|
Web 归档(Web Archive,WAR)文件用于打包 Web 应用程序,其中包含诸如 servlet、第三方库和图像之类的各种资源。这些归档文件使用标准的目录命名模式,并且只接受某些文件,例如 web.xml(该文件映射 servlet 名称和其它配置方面),放置在 WEB-INF 目录中。
Raven 使 WAR 文件的创建变得非常简单。在清单 4 中,我以 'brewery.war'
作为名称使用 war
任务,默认情况下,该任务创建一个具有相同名称的 WAR 文件。webapp_dir
的默认位置是 src/main/webapp,但是由于我的目录结构不同于 Raven 的假定,因此需要通过 task.webapp_dir = 'src/web'
行对此进行修改。
war 'brewery.war' => ['clean', 'compile', 'java-doc'] do |task| task.webapp_dir = 'src/web' end |
注意,在创建 WAR 文件之前,Raven 必须首先运行 clean
任务,接着运行 compile
任务,然后再运行一个任务来创建一些文档。在这些任务成功运行之后,Raven 可以创建一个归档文件,您将看到,这里需要输入的东西很少。实际上,如果我的项目遵循 Raven 假定的默认目录布局的话,那么除了那些依赖任务之外,不再需要编写任务东西(也就是说,不需要 do
和 end
之间的代码!)。
不管您信不信,Raven 就这么简单 — Raven 的 war
任务处理目录的创建,文件的放置,以及 war 文件的生成。非常高效,不是吗?
|
生成项目文档对于向团队成员宣传有用信息通常会有所帮助。如果别人计划使用您正在编写的框架代码或实用程序,那么您最终需要发布 Javadoc,这些 Javadoc 是描述相关源代码中的类、方法和公共实例变量的一些 HTML 文件。
通过 Raven,可以很快地利用 Javadoc 机制生成文档,如清单 5 所示:
javadoc 'java-doc' => 'deps' do |task| task.build_path << "src" end |
请再次注意,由于我的项目没有遵循 Raven 假定的模型,因此必须修改 javadoc
任务的 build_path
属性。此外,该任务依赖于 deps
任务(在 清单 1 中)来包括所有类引用。
至此,您大概已经意识到,如果项目遵循 Raven 假定的布局,那么构建脚本甚至比我的还要简单!
|
虽然我只谈到了 Raven 中提供的一部分特性,但是还可以用这个创新的构建平台做更多的事情。特别地,Raven 为使用 RubyGems 机制处理构建依赖(即第三方库)提供了广泛的支持,RubyGems 在构建时自动下载版本化的二进制文件,而不是引用共享驱动。RubyGems 以类似于 Maven 或 Ivy 的方式处理第三方库。
除了 RubyGems 之外,Raven 还可以运行 JUnit 测试,构建一般的 JAR 文件,并且还可以清理目录。表 1 列出了 Raven 中一些较流行的、可方便构建 Java 项目的任务:
Raven 任务 | 描述 |
---|---|
javac |
编译 Java 源文件,默认情况下将生成的类文件放入 target/classes 目录中。 |
jar |
生成一个 JAR 文件,该文件通常是 .class 文件的一个集合。 |
war |
生成一个可部署到 Java Web 容器的 Web 应用程序归档文件。 |
javadoc |
根据源代码生成 Javadoc。 |
junit |
执行 JUnit 测试。 |
jar_source |
根据 Java 源文件创建一个 JAR 文件。 |
gem_wrap_inst |
将 JAR 文件转换到 RubyGem,然后将该文件安装到一个存储库中。 |
随着 Raven 的成熟,表 1 中的列表会进一步扩大。此外,如果 Raven 中还缺少某种您需要的功能,您完全可以自己编写它!
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者