UML软件工程组织

 

 

Eclipse 插件自动构建介绍
 
2008-03-10 作者: 晏斐 出处:primeton.com
 

1.基本信息

摘要:

Eclipse 提供了在Eclipse IDE外自动构建的能力。Eclipse本身也是采用这种方式构建的。本文将分享Eclipse自动构建的一些经验,详细介绍Eclipse插件开发的自动编译过程。
分类:开发技术->Eclipse
标签:编码 集成编译 插件
作者:晏斐 创建于2007-5-21 12:00:00
环境要求:本文是基于Eclipse 3.0环境进行描述的。如果你使用的是其他版本的Eclipse,在试验时你可能需要对一脚本进行修改。

2.Ant介绍

Ant是一个平台无关的Java项目构建工具。使用Ant可以方便的将您开发的项目的构建流程有效的管理起来,可以方便的提供给用户可发布的软件产品。使用Ant来做项目构建工具的优点被越来越多的人认可,目前开源的Java项目几乎都使用Ant做构建工具。现在还出现了Dot Net平台上的Ant构建工具。Ant已经成为了Java项目构建的事实上的标准。

Ant通过执行构建脚本来生成可发布的软件包。构建脚本为XML格式。每一个Ant脚本中包含一个Project, Project下又包含许多的Target, Target由一个或多个Ant Task组成。Ant的Task允许用户自己进行扩展,当Ant提供的Task不能满足你的需要时可以自己开发一个Task。一个Ant脚本可以调用另外一个Ant脚本,所以可以设计出精巧的脚本回调结构, 使您开发的Ant脚本更易于维护和扩展。

Eclipse将Ant集成到了它的IDE中,开发完一个插件项目,你可以方便的生成插件项目的构建脚本。但是Eclipse的插件项目一般比较依赖Eclipse的开发环境,如果直接使用Ant来执行Eclipse生成的构建脚本则需要做许多额外的环境设置的工作。Eclipse的开发者考虑到了这一点,在Eclipse中提供了一个Ant的Eclipse Application。你可以使用命令行的方式执行它,像下边这样:

3 Eclipse插件自动构件过程

3.1准备工作

1.全面分析你需要构建的项目

在编写Ant脚本前,需要对将要构建的项目有一个全面的考虑。 列出你的项目有多少个子项目需要进行构建。各个子项目之间的依赖关系如何,这关系到在脚本中对各个子项目的构建先后顺序的安排。其次,列出项目的中间提交物清单。也就是构建的结果是什么。对于一个小项目构建的结果也许就是一个jar文件,但是对于一个几十人的开发团队开发的项目,构建结果可能包含有服务端的构建结果,客户端的构建结果,还有Web程序。中间提交物清单应该包括构建结果的文件名和存放路径以及将来部署时在未来系统中所处的逻辑位置。

2.列出构建的工作流程

也就是说在你的构建过程中先做什么,后做什么。例如通常的构建过程是:从cvs服务器获取最新版本的代码,然后对代码进行编译,之后打包部署。这个过程如果一下子想不清楚,可以先列出最顶层的过程,然后不断细化。

3.了解构建的依赖环境

一般项目都是在IDE中开发,项目的配置信息都通过IDE的设置界面进行设置。这些设置项比较分散。如果要在使用Ant成功构建项目,则需要对IDE中的设置导出,否则构建通常会失败。另外需要了解项目使用了那些第三方的开发包,在编写构建脚本时需要考虑。Eclipse的插件项目可以方便的导出Ant构建脚本,供其他顶层脚本调用。

3.2自动构建脚本编写

在对你要构建的项目有了一个通盘的考虑之后,可以开始编写构建脚本了。最直接的方法当然是自己从头开始编写构建脚本。或者是使用一个顶层脚本按照子项目的依赖关系依次调用各个插件生成的构建脚本build.xml文件。这样能为我们节省很大一部分时间。但是实践证明,即使是这种方式工作量还是很大。例如我们目前的项目的插件数目已经有几十个,依次调用一遍各个插件的build.xml文件依然是一件很累和乏味的事情。实际上我们也是这样做的。有没有更方便的方法呢?

原来,Eclipse本身提供了一个插件构建的脚本框架,我们只需要像填空一样编写很少的脚本代码就可以完成整个复杂的构建过程。

3.3Eclipse自动构建框架介绍

下面我们用一个例子来说明整个的构建过程。我们在下面的例子中将会编写sdk.examples的构建脚本。sdk.examples是eclipse的示例代码,你可以在eclipse的cvs里得到。

在开始之前你需要下载cvs.exe和unzip.exe,并将他们拷贝到系统目录中,或者放置在环境变量%PATH%可以找到的地方。Cvs.exe和unzip.exe可以从以下地方下载:
CVS client version 1.10 or higher on system path.
Info-Zip zip and unzip executables on system path.
第一步先获得org.eclipse.releng.eclipsebuilder。org.eclipse.releng.eclipsebuilder是eclipse的发布引擎。

D:\build>cvs -d :pserver:anonymous@dev.eclipse.org:/home/eclipse export -r R3_0_2 org.eclipse.releng.eclipsebuilder

org.eclipse.releng.eclipsebuilder中的readme.html描述了如何构建一个eclipse的组件。

在默认的情况下,为了获取3.0.0版本的sdk.examples,我们需要修改org.eclipse.releng.eclipsebuilder/sdk.examples/build.properties文件。
mapVersionTag=R3_0_0

现在我们可以在控制台的命令行中开始构建过程。

  1. D:\build\org.eclipse.releng.eclipsebuilder>set ECLIPSE_HOME=D:\eclipse\eclipse-3.0.0
  2. D:\build\org.eclipse.releng.eclipsebuilder>java -cp %ECLIPSE_HOME%\startup.jar org.eclipse.core.launcher.Main
  3. -application org.eclipse.ant.core.antRunner -buildfile build.xml
  4. -Dcomponent=sdk.examples -Dconfigs="*,*,*" -Dbaseos=win32 -Dbasews=win32 -Dbasearch=x86 -Djavacfailonerror=true
  5. -Dpde.build.scripts=%ECLIPSE_HOME%/plugins/org.eclipse.pde.build_3.0.0/scripts -DbaseLocation=%ECLIPSE_HOME%

命令行中的compoent属性是你将要构建的组件名称。也就是你在d:\build\org.eclipse.releng.eclipsebuilder目录下的一个子目录的名称。在这个子目录下有build.properties和customTargets.xml文件。
Build脚本将会使用d:\build\org.eclipse.releng.eclipsebuilder\src目录作为默认的build目录。你也可以将-DbuildDirectory=${basedir}/newDirectory加入到命令行中指定你自己的build目录。

构建完成之后,装配完成的可分发的软件包和构建日志位于build目录下的一个子目录,这个子目录的名字由buildLabel属性定义。默认的这个目录的名称由”I-”开始,并包含构建时刻的时间戳。

命令行中的baseLocation属性用户指定eclipse启动的目录。同时,构建过程中如果在build目录中找不到一个plugin,会到baseLocation目录去查找。因此,在你自己的构建过程中的第三方的插件也应该位于%baseLocation%/plugins目录下,才能保证构建过程成功。BaseLocation目录下不能包含任何你将要构建的插件,如果baseLocation目录下包含了sdk.examples,构建过程将会出错。

3.4构建过程分析

在下面的部分我们将为你分析构建过程中的一些细节。首先我们先熟悉一下和构建有关的几个重要文件和他们的位置:
build.xml
位于 org.eclipse.pde.build_<version>/scripts 目录下。该文件提供了一个从prebuild到postbuild的过程的一个骨架。

genericTargets.xml
位于org.eclipse.pde.build_<version>/scripts目录下。包含了像fetchElement, generateScript, processElement, assembleElement等Ant Task。

customTargets.xml
位于org.eclipse.releng.eclipsebuilder目录的子目录下。如sdk.examples.customTargets.xml文件将会被org.eclipse.releng.eclipsebuilder目录下的build.xml文件调用。在customTargets.xml文件中将会调用genericTargets.xml文件中的target。CustomTargets.xml文件的主要职责是定义将要构建的features和从那里得到构建过程中需要的map文件。关于map文件将会在后边介绍。

4. build.properties

位于org.eclipse.releng.eclipsebuilder目录的子目录下。Build.properties属性文件中定义了一些构建过程中build脚本使用的一些属性。

下面我们来看一下整个构建过程的序列图如下:

上面的序列图给出了整个构建过程的一个概览。

1.D:\build\org.eclipse.releng.eclipsebuilder目录下的build.xml首先会设置一些与构建过程有关的环境设置变量,然后调用$ECLIPSE_HOME\plugins\org.eclipse.pde.build_3.0.0\scripts下的build.xml文件。

2.$ECLIPSE_HOME\plugins\org.eclipse.pde.build_3.0.0\scripts下的build.xml文件会一次调用自身的preBuild,fetch,generate,process,assemble,postBuild的几个target。在这几个target中又会一次调用customTargets.xml文件。所以说build.xml是整个构建过程的骨架,它定义了构建过程中的“构建准备”,“fetch构建所需资源”,“生成插件构建所需build.xml文件”,“编译”,“组装”,“构建完成后的相关工作”等阶段。

3.PreBuild过程中要做的一件重要事情就是回调customTargets.xml文件的getMapFiles Target获取map文件。Map文件定义了将要构建的所有插件在cvs中的位置。Map文件的格式如下:
feature|fragment|plugin@elementId=<cvs tag>,<access method>:<cvsuser>@<cvs repository>,<cvs password>[,<repository path> (no starting slash) ]
Map文件从cvs获得后会放在buildDirectory目录下的map子目录下。Map文件可以是有多个的,脚本会将map目录下的所有map文件连接为${buildDirectory}/directory.txt文件。

4.fetch过程同样会调用customTargets.xml文件中的allElements target。AllElements Target又将调用$ECLIPSE_HOME\plugins\org.eclipse.pde.build_3.0.0\scripts下的genericTargets.xml文件的fetchElement target。FetchElement target中会调用eclipse内置的Ant Task “eclipse.fetch”。“eclipse.fetch”会自动生成一个fetch_${plugin_id}.xml文件。它生成的原理是分析feature.xml文件,获取一个feature内包含的所有插件id, 然后在map文件中查找插件在cvs中的位置并生成fetch_$(plugin_id).xml文件。然后会执行该fetch文件将插件代码获取到本地构建机器上。

5.Generate过程同样会回调customTargets.xml文件的allElements target。AllElements target又将调用$ECLIPSE_HOME\plugins\org.eclipse.pde.build_3.0.0\scripts下的genericTargets.xml文件的generateScript target。GenerateScript Target中会调用eclipse内置的Ant Task “eclipse.buildScript”。“eclipse.buildScript”的作用是根据插件下的plugin.xml文件生成构建插件所需的build.xml文件。大家注意到在脚本中将这个过程自动化了。同样的我们可以在ide环境中的插件开发透视图中选中一个plugin.xml文件,执行右键菜单中的pde tools->Create Ant Build File命令生成build.xml文件。如下图所

但是,在开发过程中经常会添加对新的插件的引用而没有重新生成build.xml文件,这是导致构建过程失败的主要原因。另外一个缺点是如果使用在ide环境中生成的build.xml文件,构建环境必须和ide环境设置一样。开发环境和构建环境设置不一样也是导致构建过程失败的最常见的原因。

在构建时刻生成build.xml文件可以避免以上两个缺点。

6.Process过程同样也会回调customTargets.xml文件的allElements target。AllElements target又将调用$ECLIPSE_HOME\plugins\org.eclipse.pde.build_3.0.0\scripts下的genericTargets.xml文件的processElement Target。 ProcessElement Target调用自动生成的插件build.xml文件,完成对插件的构建。

7.Assemble过程将完成插件的组装过程。

8.PostBuild过程回调customTargets.xml的postBuild target。

3.5构建你自己的插件

前面我们构建了sdk.examples,并且分析了整个自动构建的全过程。采用上面介绍的构建方法有一个限制就是你必须有一个feature项目。如果没有就先建立一个。菜单路径是File->New->Project...->Plug-in Development->Feature Project。在打开的向导中输入feature项目的名称,然后选择你要构建的插件并且包含到feature项目中。如果你不想将你要发布的plugin部署为一个feature,你只需要修改feature项目中的build.properties文件,删除掉bin.includes属性就可以了。

构建你自己的插件,你需要准备四个文件:

  1. a map file for fetching the sources ,map文件一般情况下是存放在cvs中,在构建开始时从cvs下载。
  2. customTargets.xml ,customTargets.xml文件位于位于d:\build\org.eclipse.releng.eclipsebuilder目录的子目录下。如sdk.examples。org.eclipse.releng.eclipsebuilder是eclipse的release engine。可以从eclipse的cvs中获得。
  3. build.properties ,build.properties文家定义了构建过程中使用的环境变量。你可以根据自己的情况修改。
  4. a shell script, which starts an Eclipse instance as AntRunner。运行Eclipse实例的脚本。在本文的前面已经有过介绍。

下面是一个Map文件片断的示例,你可以根据你自己的情况修改用户名,密码,package名称,和CVS分支名称。

  1. feature@org.rubypeople.rdt=HEAD,:ext:cvs.sf.net:/cvsroot/rubyeclipse,,org.rubypeople.rdt-feature
  2. plugin@org.kxml2=HEAD,:ext:cvs.sf.net:/cvsroot/rubyeclipse,
  3. plugin@org.rubypeople.rdt=HEAD,:ext:cvs.sf.net:/cvsroot/rubyeclipse,
  4. plugin@org.rubypeople.rdt.core=HEAD,:ext:cvs.sf.net:/cvsroot/rubyeclipse,

CustomeTargets.xml提供了构建过程中回调的钩子。CustemerTargets.xml文件的模板在org.eclipse.pde.build插件的script目录下可以找到。下面是customerTargets.xml文件的一个片断。allElements target中定义了你要构建的feature id。这里的feature id不是feature manifest文件中定义的feature,而是用于在map文件中查找cvs信息时使用的id。getMapFiles target会将map文件下载到本地的${buildDirectory}目录下。Assemble target会调用生成的assemble脚本文件对编译结果进行组装。

  1. <target name="allElements">
  2. <ant antfile="${genericTargets}" target="${target}" >
  3. <property name="type" value="feature" />
  4. <property name="id" value="org.rubypeople.rdt" />
  5. </ant>
  6. </target>
  7. <target name="getMapFiles">
  8. <property name="cvsRoot" value=":ext:cvs.sf.net:/cvsroot/rubyeclipse" />
  9. <cvs cvsroot="${cvsRoot}"
  10. dest="${buildDirectory}/maps"
  11. command="export -r ${mapVersionTag} org.rubypeople.rdt.build/map"/>
  12. </target>
  13. <target name="assemble.org.rubypeople.rdt">
  14. <ant antfile="${assembleScriptName}" dir="${buildDirectory}">
  15. <property name="zipargs" value="" />
  16. </ant>
  17. </target>

3.6结论

回顾一下整个构建过程,在customTargets.xml文件中我们定义了每一构建过程中供$ECLIPSE_HOME\plugins\org.eclipse.pde.build_3.0.0\scripts下的build.xml文件回调的target。对于一些复杂的通用功能如生成获取cvs中插件代码的fetch.xml文件、生成build.xml文件等由genericTargets.xml来完成。这样一种结构最大限度的保证了customTargets.xml文件的简洁性、可维护性、可扩充性。Eclipse提供了这样一个优雅的构建脚本框架,用户只需要编写自己的customTargets.xml文件。CustomTargets.xml文件的模板可以在$ECLIPSE_HOME\plugins\org.eclipse.pde.build_3.0.0\scripts找到。我们只需要拷贝一个,然后稍加修改就可以完成该项工作。

CustomTargets.xml文件中预定义了很多和构建过程对应的target。这些target都会被$ECLIPSE_HOME\plugins\org.eclipse.pde.build_3.0.0\scripts调用。一个例子可以很好的说明这种回调的方便性。当我们构建一个包含有help文件的插件时,一个预先要做的工作就是先要将help文件打包成zip文件。我们可以修改customTargets.xml中的preProcess Target:

  1. feature@org.rubypeople.rdt=HEAD,:ext:cvs.sf.net:/cvsroot/rubyeclipse,,org.rubypeople.rdt-feature
  2. plugin@org.kxml2=HEAD,:ext:cvs.sf.net:/cvsroot/rubyeclipse,
  3. plugin@org.rubypeople.rdt=HEAD,:ext:cvs.sf.net:/cvsroot/rubyeclipse,
  4. plugin@org.rubypeople.rdt.core=HEAD,:ext:cvs.sf.net:/cvsroot/rubyeclipse,

3.7参考资料

http://www.eclipse.org/articles/Article-PDE-Automation/automation.html ,标题是“Build and Test Automation for plug-ins and features”

版权所有(C)2007 上海普元信息技术有限公司

 

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

京公海网安备110108001071号