UML软件工程组织

 

 

Eclipse 插件项目的自动化集成与构建

2008-09-09 作者:山 崺颋, Gu Yi 来源:IBM

 
本文内容包括:
Eclipse 平台、Eclipse 插件及 Eclipse Rich Client Platform 技术在业界已经得到了非常广泛的应用,使用 Eclipse 技术构建的工具,产品无论在开发人员的日常工作,抑或大型企业的生产运作上都发挥了巨大的作用。而和每个具有完备流程的软件工程一样,一个优秀的 Eclipse 插件 RCP 项目也缺少不了自动构建这一环。本文详细地介绍了 Eclipse 插件 RCP 项目的自动化构建的过程。

自动构建简介

构建( build 在软件工程中是指将源文件及资源编译、打包成可在计算机上运行的软件。而自动化构建在软件行业已有非常悠久的历史,我们平时用的 Make 脚本, Ant 脚本都是常见的自动化构建的应用。而 Eclipse 插件系统的核心模块 PDE 同样也提供了完整的面向 Ant 的自动构建体系来编译和打包 Eclipse 插件 RCP 项目以用于发布。

在下面的部分中,详细讲述了进行 Eclipse 自动化构建的方法。文中示例适用于 Eclipse 3.2 和3.3。

使用 Eclipse 工作平台导出 Eclipse 插件项目

Eclipse 平台提供了最简易的手动导出方式,您可以在 Eclipse IDE 的菜单中中选择 File->Export 命令,在弹出对话框中选择 Plug-in Development->Deployable features 导出 Eclipse plug-in feature 或选择 Deployable plug-ins and fragments 导出 Eclipse plug-in 或 fragment :

图 1. 导出 Feature 项目
导出 Feature 项目

在本例中,我们选择了导出一个 Eclipse plug-in feature ,在选择 Next 以后您可以看到如下的页面:

图 2. 选择项目和路径
选择项目和路径

在此页面中,您的 Eclipse 工作台中现存的 Eclipse Feature 项目将被列出于 Available Features 的选项之中。

在我们的例子中已有项目为 org.eclipse.test ,如果您的 Workbench 中存在其他 Feature 项目,也会被相应地列出。此后,您可以在 Directory或者 Archive file 中选择将此 Feature 项目导出为文件夹或压缩文件。

点选 Finish , Eclipse 将自动导出 org.eclipse.test 项目并打包为 C:\org.eclipse.test.zip 文件。

图 3. 导出项目的结构
导出项目的结构

可以看到, Eclipse 平台提供的一键式导出非常方便,已经可以满足普通插件开发者的需要。但它的可配置项较少,缺少自动加入版本号,时间戳等功能。而对于一个大型的项目,往往涉及多个模块联合编译构建;部分项目需要定期构建(例如 nightly build ),往往需要一个定时脚本在无人干预的情况下自动进行构建。此时 Eclipse 平台的手工一键导出已经不能完成这些要求,必须通过脚本的方式运行。为此 Eclipse 系统也提供了另一种基于 Ant 脚本的构建方式—— PDE headless-build ,我们在下面将详细讲述。

PDE headless-build 基础

PDE (Plug-in Development Environment) headless-build 是一种基于 Ant 脚本的构建方式,它主要适用于 Eclipse plug-in 与 Eclipse RCP 项目的导出。

headless-build 所需安装的软件

在您开始 headless-build 之前需要安装如下软件:

  • Eclipse SDK (必需)
  • Ant (可选,在命令行模式中必须)
  • CVS (可选,在自动获取 CVS 源控制的项目中必须)

由于 headless-build 是特定于 Eclipse 插件的构建平台,而 Eclipse 插件的编译构建都离不开Eclipse 本身的类库与资源,因而运行 headless-build 之前,您必须已经安装好带有 PDE 环境的 Eclipse SDK 。同时,如果您需要在纯命令行模式中启动构建或者其他必须使用外部 Ant 启动的模式,那您还必须安装 Ant 程序。

最后如果您的项目存储于 CVS 源控制中, headless-build 也提供了自动获取 CVS 源控制中代码和资源的功能。而在使用此项功能之前您必须安装有 CVS 客户端,并确保 cvs 命令已在环境变量中被定义。

headless-build 的启动和配置

由于 headless-build 必须基于 Eclipse 环境,因而我们必须通过启动 Eclipse 中相应的 application 来完成 build 过程。以下是一条简单的 build 启动命令。

java -jar plugins/org.eclipse.equinox.launcher_1.0.0.v20070606.jar \
-application org.eclipse.ant.core.antRunner -buildfile \
E:\soft\eclipse330\plugins\org.eclipse.pde.build_3.3.0.v20070612\scripts\build.xml  \
–Dbuilder=”C:\Documents and Settings\YiTing\workspace33\pluginBuildExample\cfg”

这条命令和平时我们使用 java 命令启动 Eclipse IDE 环境类似,但是它的启动参数 –application 设置为 org.eclipse.ant.core.antRunner ,这将启动 Eclipse 的内置 Ant 运行器,而之后的参数 -buildfile build.xml 则类似于通常 Ant 启动参数中的脚本参数,指定了将被运行的 Ant 脚本。

在启动命令中,我们也可以设置 –D 参数,例如使用 –DbuildLabel 可更改最终构建完成的插件的后缀, -DcompilerArg 可设置编译 plug-in 中 Java 代码的编译参数,但通常情况下我们不必在启动参数中设置所有的可配置参数,因为 headless-build 已为此提供了一组基于 properties 和 xml 的配置文件:

allElements.xml

用于设定哪些 feature 或 plug-in 项目将会被 build 。 allElements.xml 的文件内容往往较为简单例如:

<project name="allElements Delegator">
  <target name="allElementsDelegator">
 	<ant antfile="${genericTargets}" target="${target}">
	 	<property name="type" value="feature" />
	 	<property name="id" value="org.eclipse.test" />
 		</ant>
  </target>
 
  <target name="assemble.org.eclipse.test">
 	<ant antfile="${assembleScriptName}" dir="${buildDirectory}"/>
  </target>
</project>

在此样例中第一个 target 项指定了名为 org.eclipse.test 的 feature 项目将会被 build 。而第二个 target 则允许用户对不同环境下的插件设置不同的参数,例如:

<target name="assemble.org.eclipse.pde.build.container.feature.linux.motif.x86">
 <ant antfile="${assembleScriptName}" dir="${buildDirectory}">			
 	<property name="archiveName" value="${archiveNamePrefix}-linux.motif.x86.zip"/>
	</ant>
 </target>

设定了 build 结果中应用于 linux motif x86 环境的 plug-in 的输出将以 linux.motif.x86.zip 为后缀。

build.properties

定义了大量的控制参数,用户可以通过改变此些参数控制整个 build 流程中7个方面的具体细节:基本设置,基础控制,输出控制,签名和 JNLP 控制,版本控制,获取源码控制,编译控制。每个部分中参数的具体含义可以参考 PDE 文档(参考1)。

在这里我们列举并解释其中最常用的控制项:


表 1. build.properties 中的常用参数
 
参数 功能
baseLocation 用于设定您的插件的目标Eclipse平台。例如,您所build的插件将会被安装于Eclipse 3.2.2平台上,那您必须为build过程准备一份Eclipse 3.2.2平台的本地拷贝。而baseLocation则设置为此平台的eclipse目录的路径。
buildDirectory 整个build过程的工作目录,在这个目录下将存放有build过程中生成的中间脚本、配置、log记录和最终输出的二进制文件。
buildType 构建的种类,例如I、N、M。在Eclipse插件的版本设置中一般使用单个大写字母表示每个版本的种类,I表示集成版(integration build),N表示每日版本(nightly build),M表示维护版本(maintenance build)。
buildLabel 完整的版本标签,一般您不需要改变此项设置,默认情况下buildLabel = ${buildType}${buildId}。
buildId 此项未在build.properties的文档中涉及,因为一般我们不在build.properties中设置buildId,而是通过headless-build启动参数加入,这样用户就可以在自定义的Ant脚本中任意生成符合项目需求的buildId。
compilerArg 设定编译插件中Java代码时使用的编译器参数。
configs 控制目标插件的平台信息。通常的格式为:
configs = win32, win32, x86 & linux, gtk, ppc
表示输出的插件将有一份为win32系统、win32窗口和x86架构下的版本,而另一份则是linux系统,gtk窗口库和ppc架构下的版本。在build过程中之所以需要控制最终输出的结果集是因为Eclipse系统中部分核心模块如SWT对操作系统、窗口系统和硬件平台有特定的依赖,因而您所生成的插件也需要对此有区别的对待,以保证链接到正确的平台。
forceContextQualifier 用于替换feature和plug-in的version number中的qualifier。
pluginPath 指定存放库插件的目录。您的插件一般总对其他插件有所依赖,因而您还必须制定所依赖的插件的存放目录。
skipFetch 是否选择从cvs源控制中下载代码和资源。由于Eclipse系统和cvs的集成相对较为紧密,因而在headless-build构架中特有对cvs源控制的支持,如果skipMaps未设为true,系统将会试图从指定的cvs源中下载代码。关于headless-build与cvs的集成,我们在之后的章节中会介绍。

在构建的过程中,如果需要改变 version 中的 build 号,一般可以通过指定forceContextQualifier 来完成。 如在 feature 和 plug-in 中指定版本为 0.8.0.qualifier。然后在 build 的时候可以通过修改 forceContextQualifier 来实现 build 号的自增长。

customTargets.xml:

customTargets.xml 为用户在构建过程中的每个步骤中加入控制提供了可能。它的机制类似于我们通常编程中使用的回调函数。例如他提供了 preFetch 和 postFetch 两个 Ant target:

	<target name="preFetch">
	</target>

	<target name="postFetch">
	</target>

通常用户可直接修改这两个 target 中的内容,以定制在获取代码前后的特殊行为。

allElements.xml、build.properties、customTargets.xml 是配置 headless-build 总体过程的3个支柱文件,用户需要负责编写它们,并将其放入同一被称为配置目录的文件夹,并将此目录的路径作为 –Dbuilder 参数值设定于 build 启动命令(例如前面样例命令中的 –Dbuilder=”C:\Documents and Settings\YiTing\workspace33\pluginBuildExample\cfg”)。

headless-build 的核心库文件与资源

正如前面所说,PDE headless-build 属于 Eclipse Plug-in Development Environment 的一部分。因而他的核心脚本文件都存储于 Eclipse 的 PDE 插件中。以作者目前的环境为例,PDE build 插件位于 plugins 目录下的 org.eclipse.pde.build 插件目录中。在此目录的 lib 文件夹中包含有 pde build 库的核心类文件,其中包括了绝大多数和 PDE build 相关的 Ant task 的实现,有兴趣的读者可以在 org.eclipse.pde.source 源代码插件中看到相应的代码。

图 4. 导出项目的结构
导出项目的结构

而对于我们的 build 任务,PDE headless-build 的核心脚本文件位于 scripts 和 templates 两个目录下。

scripts 文件夹中提供了 headless-build 的基础控制脚本。事实上无论是使用 headless-build 还是IDE 导出,我们都间接地通过执行这些脚本完成了一系列的过程。其中的 build.xml 文件是 build 过程的主脚本文件,在之前的启动命令中的 –buildfile 后的值就是此 build.xml 文件的路径。而 genericTargets.xml 则定义了在 build 过程的各个子过程的行为。

templates 文件夹中则提供了一系列的模板库。您可在 templates 下的 headless-build 目录中找到前面所说的 allElements.xml、build.properties、customTargets.xml 文件的模板。您可以方便地将其拷贝和修改,用作您自己的构建配置文件。

headless-build 的工作流程和通知机制

在之前的介绍中我们提到了 customTargets.xml 提供了对 headless-build 流程的多个节点的控制。而事实上 PDE 将 headless-build 分为多个子过程。customTargets.xml 正是针对了此些子过程提供了回调接口,以通知和触发用户定制的脚本被执行。

以下是 headless-build 整体流程的简图。

图 5. headless build 的工作流程
headless build 的工作流程

在其中,您需要提供的是最初的引入脚本(Ant 文件,批处理文件或命令行指令),之后 Eclipse 宿主会被启动并调用您指定的通常位于 PDE 插件 scripts 目录下的 build.xml,而此文件则会相应找到您在配置目录中定义的 customTargets.xml 文件,执行用户设定的回调脚本,并通过 customTargets.xml 调用 PDE 插件 scripts 下的 genericTargets.xml 启动 PDE 内部的各项子过程的执行代码。而在 genericTargets 中则定义了 headless-build 的4个子过程fetch、generate、process 和 assemble,下面我们对他们做简单的介绍。

表 2. build的四个子过程
 
子过程 描述
fetch 负责从源控制中下载所需构建的插件项目的源文件
generate 为每个插件分别生成自身的构建脚本。
process 通过运行 generate 中生成的脚本,完成对每个插件的构建
assemble 将 process 过程中生成的插件打包

使用 ant 自动获取 cvs 源控制中的代码与资源

我们知道,Eclipse 和 Ant 对于 cvs 源控制的支持非常优秀。同样,PDE 环境对 cvs 的集成度也相当好,用户可以在 fetch子 过程中自动下载存储于 cvs 源控制中的代码。而为了顺利运行 cvs,您必须在本地安装 cvs 客户端,并且保证 cvs 目录被设置于系统路径变量中:

图 6. cvs 命令示例
cvs 命令示例

为了从源控制中正确找到相应的项目文件,PDE 还需要用户创建 map 文件,并在其中指定每个插件、功能部件等在 cvs 源控制中的位置,以下是一个 map 文件的样例:

feature@org.eclipse.test=CVS,HEAD,:pserver: anonymous   \
          @dev.eclipse.org:/cvsroot/eclipse,,org.eclipse.test-feature,
fragment@org.eclipse.ant.optional.junit=CVS,HEAD,:pserver: anonymous   \
@dev.eclipse.org:/cvsroot/eclipse,,org.eclipse.ant.optional.junit,
plugin@org.eclipse.test=CVS,HEAD,:pserver: anonymous   \
   @dev.eclipse.org:/cvsroot/eclipse,,org.eclipse.test,
...

其中定义了我们所需要构建的 org.eclipse.test 功能部件,及其相关的组件在 eclipse.org 的 cvs 源控制中的位置,tag,和密码等信息。之后 PDE 将根据这些信息执行 fetch 子过程,将源控制中代码下载到本地构建目录( buildDirectory 中。

关于详细的 map 文件的语法格式请参见 PDE 详细说明(参考2)。

具体实例——一个样例插件的构建

最后我们将用实例演示如何使用 PDE headless-build 架构来构建 Eclipse 的一个子功能部件 org.eclipse.test。该功能部件的源代码可在 eclispe.org 的 cvs 源控制 /cvsroot/eclipse 中找到。

在完成这个例子的过程中,我们使用了 eclipse3.3.0、Ant 1.7.0 和 CVSNT 2.5.03 的环境,您可能需要根据个人环境的不同做少许调整。完整的示例可以在下面的附件一下载。

如果观察 org.eclipse.test 功能部件的定义文件 feature.xml 您可以发现它由org.eclipse.ant.optional.junit org.eclipse.test org.eclipse.test.performance 和 org.eclipse.test.performance.win32 org.junit org.junit4 六个子插件组成(注:org.eclipse.test.source 并非原生功能插件,而是衍生的源代码插件)

图 7. org.eclipse.test 的子插件
org.eclipse.test 的子插件

首先我们先建立一个 eclipse 项目 pluginBuildExample。

由于这些代码资源全部位于 eclipse.org 的 CVS 源控制中,所以,为了获取他们,我们首先需要创建 map 文件 build.map。

图 8. build.map
build.map

内容为:

feature@org.eclipse.test=CVS,HEAD,:pserver:$cvsuser$@dev.eclipse.org:/cvsroot/eclipse,  \
                             $cvspass$,org.eclipse.test-feature,
fragment@org.eclipse.ant.optional.junit=CVS,HEAD,:pserver:$cvsuser$\
      @dev.eclipse.org:/cvsroot/eclipse,$cvspass$,org.eclipse.ant.optional.junit,
plugin@org.eclipse.test=CVS,HEAD,:pserver:$cvsuser$\
@dev.eclipse.org:/cvsroot/eclipse,$cvspass$,org.eclipse.test,
plugin@org.eclipse.test.performance=CVS,HEAD,:pserver:$cvsuser$\
@dev.eclipse.org:/cvsroot/eclipse,$cvspass$,org.eclipse.test.performance,
fragment@org.eclipse.test.performance.win32=CVS,HEAD,:pserver:$cvsuser$\
@dev.eclipse.org:/cvsroot/eclipse,$cvspass$,org.eclipse.test.performance.win32,
#plugin@org.junit=CVS,HEAD,:pserver:$cvsuser$\
@dev.eclipse.org:/cvsroot/eclipse,$cvspass$,org.junit,
#plugin@org.junit4=CVS,HEAD,:pserver:$cvsuser$\
@dev.eclipse.org:/cvsroot/eclipse,$cvspass$,org.junit4,

注意到 org.junit 和 org.junit4 在此 map 文件中被注释。因为我们之后使用的目标 eclipse 平台已经带有这两个插件,所以为了减少网络下载量,我们将其注释。

$cvsuser$$cvspass$是两个可替换字段,在最终的 fetch 过程开始前,我们将会用真实的用户名和密码代替他们。

将 org.eclipse.pde.build_3.3.0.v20070612\templates\headless-build 下的3个文件拷贝至项目中的cfg 文件夹中:

图 9. cfg 文件夹
cfg 文件夹
 

修改 allElements.xml 文件使其内容为:

<project name="allElements Delegator">
  <target name="allElementsDelegator">
    <ant antfile="${genericTargets}" target="${target}">
		<property name="type" value="feature" />
		<property name="id" value="org.eclipse.test" />
 		</ant>
  </target>
 
  <target name="assemble.org.eclipse.test">
    <ant antfile="${assembleScriptName}" dir="${buildDirectory}"/>
  </target>
</project>

注意到我们使用了 org.eclipse.test 替换了原先的 element.id,以告知 PDE 构建系统我们所需要构建的功能部件。

而 build.properties 和 customTargets.xml 则不需要做任何修改。

之后,我们创建 Ant 启动脚本 build.xml

图 10. build.xml
build.xml
 

在这个脚本中我们定义了两个重要的 Ant target:copyRequisites externBuild

copyRequisites 将我们所需要的 map 文件拷贝至最终的工作目录,并在拷贝完毕后将 map 文件中的用户名和密码替换为真实的 cvs 用户和密码

<target name="copyRequisites">
  <copy todir="${build.dir}/maps" overwrite="true">
    <fileset dir="./map">
    </fileset>
  </copy>

  <replace dir="${build.dir}/maps" token="$cvsuser$" value="${cvs.user}">
  </replace>
  <replace dir="${build.dir}/maps" token="$cvspass$" value="${cvs.pass}">
  </replace>
</target>

而 externBuild 则通过创建新的 eclipse 进程启动了 PDE 构建过程。

<target name="externBuild" depends="init">
	<java jar="${startup.jar}" fork="true">
		<arg value="-application" />
		<arg value="org.eclipse.ant.core.antRunner" />
		<arg value="-DbuildDirectory=${build.dir}" />
		<arg value="-Dbuilder=${cfg.dir}" />
		<arg value="-DpluginPath=${plugin.path}" />
		<arg value="-DbuildType=${build.type}" />
		<arg value="-DbuildId=${build.time}" />
		<arg value="-DbaseLocation=${sdk.dir}" />
		<arg value="-buildfile" />
		<arg path="${build.scripts}" />
	</java>
</target>

在传入给此进程的参数中 buildDirectory 为用于构建功能部件的工作目录,builder 为此前所创建的配置目录 cfg。pluginPath 指定了已有插件(如 org.junit)的搜索路径,而 buildType、buildId、baseLocation 则复写了 build.properties 中的相应项。

一般而言,我们的构建脚本的内容较为固定,这样更适合存储于共享的源控制之中,以便于团队开发。而每台最终用于构建项目的计算机往往都有不同的环境和配置(例如,不同的 Eclipse 安装路径,不同的工作目录、CVS 帐号等等),因而,最后我们还需要创建一个本地配置文件 local.properties,每台构建用计算机都需要适当修改此文件的本地副本,以适合自身的环境。以下是作者在运行样例时所设置的本地环境。

#CORRECT THEM LOCALLY, DO NOT COMMIT THEM BACK TO CVS
#settings for the folders on your local build machine

# the temporary build space, source files will be copied to 
# this place, and build occurs here 
build.dir=E:/buildDir
# path to plugins will is needed by the build
plugin.path=E:/soft/eclipse330
# the location of your eclipse, you don't need to set this 
# if you start the build from an eclipse enviroment
sdk.dir=E:/soft/eclipse330
# the startup.jar file of your eclipse, you don't need to set this 
# if you start the build from an eclipse enviroment
startup.jar=E:/soft/eclipse330/plugins/org.eclipse.equinox.launcher_1.0.0.v20070606.jar
# the build script file
build.scripts=E:/soft/eclipse330/plugins/org.eclipse.pde.build_3.3.0.v20070612/\
scripts/build.xml

# the user account you are using to access cvs
cvs.user=anonymous
# the password corresponding to your cvs user account
cvs.pass=

结束语

过去,开发人员使用手工的方式集成与部署他们的项目,这样的过程存在太多的人为因素而极易导致错误的发生,因而手工模式在当今的系统开发过程中已很难再被沿用。而 Eclipse PDE 提供的自动化集成和构建架构则正是顺应此潮流,为规范化 Eclipse 插件开发的流程提供了诸多可能。

下载

描述 名字 大小 下载方法
样例代码
pluginBuildExample.zip
49KB
 

参考资料

 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号