软件开发中的持续集成与原生代码构建
持续集成工具介绍
在软件开发中,持续集成是应对日益复杂开发环境的必要步骤。随着项目复杂度增加、时间尺度缩短、团队分布化等问题的出现,持续集成能帮助我们更好地控制开发过程。这里介绍几种常见的持续集成工具。
Gump工具
Gump是开源Java世界的得力工具,它不仅能显示构建失败情况,还能表明项目是否因前置依赖失败而无法构建。构建失败时可触发邮件提醒,但需安装Perl来使用此功能。
不过,Gump也存在一些不足:
- 目前仅支持CVS。
- 配置、执行和自动化操作有一定难度,需要深入理解其架构和配置。
- 非Java方面的内容可能会让很多开发者望而却步,例如存在自动化生成其他脚本的脚本,使用Perl进行失败邮件通知(但非运行Gump的必需组件)。
但Gump也有其突出特点:
- 支持项目间依赖,能在项目构建前使用最新代码库构建依赖库,提前预警API变化或确保一切正常。
- 输出和用户界面出色,所有项目都与依赖构建超链接,一键可查看CVS更新日志,项目交叉引用有助于可视化复杂的互联项目集。
持续集成工具对比
选择持续集成工具时,SCM支持是主要决定因素。若不使用CVS,有两种选择:将SCM集成脚本化并利用操作系统调度功能自动化,或使用CruiseControl。Anthill和Gump也可适配其他SCM系统,但可能需自行进行底层实现。
以下是几种持续集成工具的特性对比:
| 特性 | CruiseControl | Anthill | Gump |
| — | — | — | — |
| 安装/配置难度 | 中等难度 | 简单 | 困难 |
| 是否需修改项目构建文件 | 是 | 否 | 否 |
| 多项目依赖支持 | 否 | 否 | 是 |
| SCM支持 | CVS、VSS、ClearCase、MKS、Perforce、PVCS、StarTeam和文件系统 | 仅CVS | 仅CVS |
| 是否自行控制SCM | 否(需手动在构建文件中编码) | 是 | 是 |
| 支持多项目构建的流程 | 设置运行器应用的另一个实例,并使用CruiseControl钩子配置每个项目的构建文件 | 通过Web界面添加另一个项目定义 | 将另一个项目添加到配置文件或工作区配置中并重新生成/运行脚本 |
| 版本标记 | 提供默认和自定义标记,SCM不打标签 | 提供默认和自定义标记,成功构建后自动为SCM打标签 | 构建有时间戳,无其他标记支持,SCM不打标签 |
持续集成工具总结
不同的持续集成工具各有优缺点:
- 自定义shell脚本构建自动化:快速简单,高度可定制。
- CruiseControl:出色的报告功能,默认支持报告单元测试结果。
- Anthill:安装简单,可通过Web应用完全控制。
- Gump:若需要项目间依赖和交叉引用报告,它是不二之选。
虽然安装和配置这些工具需要一定努力,但最终是值得的。自动化构建并带有失败邮件通知是非常实用的功能,强烈建议在项目中使用持续集成。
原生代码构建挑战与现有工具
在Java软件项目中,经常会遇到原生代码,如访问Java未直接支持的操作系统功能的原生库、CORBA组件或COM对象,甚至是独立的原生可执行文件。因此,需要从Ant中编译原生代码。
现有构建工具的问题
Ant是Java程序的优秀构建工具,但在C或C++开发方面较弱。传统的Makefiles和IDEs是编译和链接原生代码的标准工具,但它们存在一些问题:
-
可移植性差
:这些工具不具备良好的可移植性,难以在不同系统上使用。
-
使用困难
:操作复杂,在部署以及与Java编译器和JUnit测试集成方面表现不佳。
委托给IDE
若将原生代码构建委托给IDE,可简化大型原生项目的开发。例如,使用Microsoft Visual Studio 6进行构建时,可使用以下代码:
<target name="msdev" depends="headers">
<exec
executable="msdev.exe"
failonerror="true" >
<arg file="CpuInfo.dsw" />
<arg value="/MAKE"/>
<arg value=""CpuInfo - Release""/>
</exec>
</target>
但这种方式存在稳定性和可移植性问题,新的IDE版本可能需要重新编写整个目标。例如,支持Microsoft Visual Studio.Net时,代码需要大幅修改:
<target name="devenv" depends="headers">
<exec
executable="devenv.exe"
failonerror="true" >
<arg file="CpuInfo.sln" />
<arg value="/build"/>
<arg value="Release"/>
</exec>
</target>
此外,IDE构建通常难以在不同系统上运行,需要锁定系统环境,否则可能导致构建失败。
使用Make
使用Make可以实现一个可在同事间共享的构建过程,也可与自动化持续集成流程集成。运行Make时,可使用以下代码:
<target name="make" depends="headers">
<exec
executable="make"
failonerror="true" >
<arg value="-f"/>
<arg file="CpuInfo" />
<arg value="release"/>
</exec>
</target>
但Make的依赖规范过程复杂,需要大量GNU或Unix命令才能正常工作。为实现跨平台可移植性,可能需要使用autoconf配置Makefile,这会使构建过程变得更加复杂。
引入 任务
Ant目前的1.5版本中没有C和C++编译及链接任务,但未来版本可能会有。目前,SourceForge上的Ant - Contrib项目提供了 任务,可用于编写单个构建文件来编译多平台的C++代码并链接成相应的可执行文件。
安装任务
首先,检查在线文档,看任务是否已包含在Ant中。若未包含,从SourceForge项目主页下载cpp - tasks存档(当前使用版本为1.0a)。将cpp - tasks.jar文件添加到项目的lib目录,并在项目中声明 任务及其四个支持的数据类型:
<path id="cc.classpath">
<pathelement location="lib/cpptasks.jar"/>
</path>
<taskdef resource="cpptasks.tasks"
classpathref="cc.classpath"
loaderRef="cctasks"/>
<typedef resource="cpptasks.types"
classpathref="cc.classpath"
loaderRef="cctasks"/>
若任务成为Ant的一部分,构建文件中将无需这些声明。为确保一切正常工作,数据类型和任务必须在同一类加载器中加载。
添加编译器
任务需要编译器来执行实际工作,支持的编译器如下表所示:
| 编译器 | 描述 |
| — | — |
| aCC | HP aCC编译器和链接器 |
| bcc | Borland C++(仅适用于Windows,可从borland.com免费获取) |
| brc | Borland Windows资源编译器 |
| CC | Sun Forte C++编译器 |
| df | Compaq Fortran编译器 |
| gcc | GNU C++编译器和链接器 |
| icl | Intel 32位Windows编译器 |
| icc | Intel 32位Linux编译器 |
| ecl | Intel 64位Windows编译器 |
| ecc | Intel 64位Linux编译器 |
| midl | Microsoft IDL编译器 |
| msvc | Microsoft C++编译器 |
| msrc | Microsoft RC资源编译器 |
| os390 | IBM大型机编译器和链接器 |
| xlC | IBM Visual Age AIX编译器 |
无论使用哪种编译器,都需确保其已安装并能从命令行正常工作,即编译器命令在路径中,且相关环境变量(如INCLUDE和LIB)配置正确。
任务简介
任务的核心特点是作为多平台构建原生C和C++程序各阶段的包装器,具体功能如下:
- 编译后缀为.c、.cc、.cxx、.cpp或.c++的C和C++源文件。
- 创建中间目标文件、可执行文件、共享库和静态库。
- 只需指定文件集(如src/*
/
.cpp)即可构建文件。
- 解析文件以确定依赖关系。
- 支持多平台和工具链。
该任务试图将 的体验引入C和C++世界,通过自动包含目录树中的所有源文件和依赖检查,避免了Makefile中列出依赖的繁琐操作,同时实现快速编译。
然而,跨平台支持仍然是一个复杂的问题,不同平台和工具的构建过程差异很大。使用 任务时,需要在所有目标平台上进行编码和测试,并且对于非基本的编译器选项,需要为每个支持的编译器和链接器确定具体选项。
以下是一个 任务的示例:
<cc debug="false"
link="executable"
outfile="dist/application"
objdir="build/objects"
multithreaded="true"
exceptions="true" >
<compiler name="msvc" if="use-msvc"/>
<compiler name="gcc" if="use-gcc"/>
<fileset dir="src/cpp" includes="*.cpp"/>
<linker name="msvc" if="use-msvc"/>
<linker name="gcc" if="use-gcc"/>
<syslibset libs="kernel32,user32"/>
</cc>
这个任务声明将使用GNU和Microsoft工具套件构建一个应用程序,启用多线程和异常处理,并生成发布版本(debug=”false”)。
构建JNI库
Java通过JNI调用原生库,这是一个复杂而强大的机制。这里不深入探讨其细节,仅探索如何在Ant中构建和测试JNI代码。后续将进一步研究 任务在JNI项目中的具体应用。
软件开发中的持续集成与原生代码构建
构建JNI库的步骤
在Ant中构建JNI库,可按以下步骤进行:
1. 生成头文件
JNI开发通常需要根据Java类生成对应的C/C++头文件。可以使用
javah
工具来完成这一任务。在Ant中,可以通过
<exec>
任务调用
javah
:
<target name="generate-jni-headers" depends="compile-java">
<exec executable="javah" failonerror="true">
<arg value="-classpath"/>
<arg path="${build.dir}"/>
<arg value="-d"/>
<arg path="${jni.headers.dir}"/>
<arg value="com.example.MyNativeClass"/>
</exec>
</target>
上述代码中,
${build.dir}
是Java类文件的输出目录,
${jni.headers.dir}
是生成的JNI头文件的输出目录,
com.example.MyNativeClass
是包含本地方法的Java类。
2. 编译C/C++代码
使用前面介绍的
<cc>
任务编译C/C++代码:
<target name="compile-jni" depends="generate-jni-headers">
<cc debug="false"
link="sharedlib"
outfile="${jni.lib.dir}/libMyNativeLibrary.so"
objdir="${jni.objects.dir}"
multithreaded="true"
exceptions="true" >
<compiler name="gcc" if="use-gcc"/>
<fileset dir="${jni.src.dir}" includes="*.cpp"/>
<linker name="gcc" if="use-gcc"/>
<syslibset libs="jvm"/>
</cc>
</target>
这里,
link="sharedlib"
表示构建共享库,
outfile
指定生成的共享库的路径,
fileset
指定要编译的C/C++源文件所在的目录。
3. 运行测试
编写Java测试类来调用JNI库中的本地方法,并使用JUnit进行测试。在Ant中,可以使用
<junit>
任务运行测试:
<target name="test-jni" depends="compile-jni">
<junit printsummary="yes" haltonfailure="true">
<classpath>
<pathelement path="${build.dir}"/>
<pathelement path="${junit.jar}"/>
</classpath>
<test name="com.example.MyNativeClassTest"/>
</junit>
</target>
上述代码中,
${junit.jar}
是JUnit库的路径,
com.example.MyNativeClassTest
是测试类的名称。
跨平台构建考虑
在进行跨平台构建时,需要考虑不同操作系统和编译器的差异。以下是一些建议:
1. 编译器选择
根据目标平台选择合适的编译器。例如,在Windows上可以使用
msvc
,在Linux上可以使用
gcc
。可以通过条件判断来选择编译器:
<cc debug="false"
link="executable"
outfile="dist/application"
objdir="build/objects"
multithreaded="true"
exceptions="true" >
<compiler name="msvc" if="use-msvc"/>
<compiler name="gcc" if="use-gcc"/>
<fileset dir="src/cpp" includes="*.cpp"/>
<linker name="msvc" if="use-msvc"/>
<linker name="gcc" if="use-gcc"/>
<syslibset libs="kernel32,user32"/>
</cc>
2. 环境变量配置
不同平台的环境变量可能不同,需要确保编译器和链接器所需的环境变量正确配置。例如,在Windows上需要配置
INCLUDE
和
LIB
环境变量,在Linux上需要配置
LD_LIBRARY_PATH
环境变量。
3. 文件路径处理
不同操作系统的文件路径分隔符不同,在编写构建脚本时需要注意。可以使用Ant的属性和函数来处理文件路径,确保脚本在不同平台上都能正常工作。
原生库分发
构建好的原生库需要进行分发,以下是一些常见的分发方式:
1. 打包到应用程序中
将原生库打包到应用程序的安装包中,确保用户在安装应用程序时同时安装原生库。可以使用Ant的
<zip>
或
<tar>
任务来创建安装包:
<target name="package-app" depends="compile-jni">
<zip destfile="dist/MyApp.zip">
<fileset dir="${build.dir}" includes="**/*.class"/>
<fileset dir="${jni.lib.dir}" includes="*.so"/>
</zip>
</target>
2. 提供独立的下载链接
将原生库作为独立的文件提供下载链接,用户可以根据自己的需求下载并安装。可以使用Web服务器来提供下载服务。
总结
在软件开发中,持续集成和原生代码构建是两个重要的方面。持续集成工具如Gump、CruiseControl、Anthill等各有优缺点,开发者可以根据项目需求选择合适的工具。在原生代码构建方面,Ant的
<cc>
任务为多平台构建原生C和C++程序提供了便利,但跨平台支持仍然是一个挑战,需要开发者在不同平台上进行测试和调整。同时,在构建和分发JNI库时,需要遵循一定的步骤和考虑不同平台的差异。通过合理使用这些工具和技术,可以提高软件开发的效率和质量。
以下是一个简单的流程图,展示了在Ant中构建JNI库的主要步骤:
graph LR
A[生成JNI头文件] --> B[编译C/C++代码]
B --> C[运行测试]
C --> D[打包应用程序]
通过以上步骤和方法,开发者可以更好地应对软件开发中的持续集成和原生代码构建问题,提高项目的可维护性和可扩展性。
持续集成与原生代码构建实践
超级会员免费看
2万+

被折叠的 条评论
为什么被折叠?



