Apache Ant,是一个将软件编译、测试、部署等步骤联系在一起加以自动化的一个工具,在Java开发环境中应用非常广泛。
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake,在c/c++跨平台开发中应用非常广泛 。
有的时候,我们项目是java/c/c++混合代码,这时可能就需要同时应用上述两种工具。
在项目初始阶段,java和c/c++代码可以分别用ant和cmake写脚本进行编译,但当项目越来越成形,越来越复杂的时候,再分别手工编译就非常容易出错。
如果能统一用一个工具(ant或cmake)进行编译,就可以提高工作效率减少编译环节出错的机率。
很久以前写过一个博客《ant调用make实现Makefile编译》,可以通过shell脚本方式,让ant执行Makefile编译,可以实现用ant统一编译项目代码的需求。
反过来,cmake也可以生成可以执行ant脚本的Makefile,以用cmake统一编译项目代码的需求。
cmake实现在Makefile中执行ant脚本是通过add_custom_command
和add_custom_target
命令来完成的。add_custom_command
允许用户将任何一个可执行文件定义成类似cmake-command的custom_command语句。而add_custom_target
可以定义一个依赖于custom_command的target。
好吧,说起来好绕口,还是用例证来说明吧。
下面这段代码是cmake脚本中的片段,用于在cmake脚本中执行ant脚本编译java代码。
# 定义一个是否编译java代码的开关,默认不编译
option(BUILD_JAVA "Build the cassdk_jni jar (Java)" OFF)
if(BUILD_JAVA)
# 执行find_program 命令在 环境变量(PATH)中查找ant可执行程序
find_program (ANT_CMD ant)
if(NOT ANT_CMD)
// 如果没有找到ant.exe则报错退出
message(FATAL_ERROR "NOT FOUND ant")
endif()
# 执行file命令生成java编译依赖文件列表,当列表中的java代码文件被修改时,就会执行ant脚本编译java代码
# GLOB_RECURSE指定递归搜索子目录
file(GLOB_RECURSE java_srcs "${CMAKE_CURRENT_SOURCE_DIR}/src/*.java")
# 定义输出的jar包名及位置
set ( CASSDK_JAR ${CMAKE_CURRENT_SOURCE_DIR}/build/jar/cassdk_jni.jar)
# 执行 ant jar命令 (当然 ant脚本build.xml中要有名为'jar'的target)
# 编译 java 代码,并将java代码编译并生成jar包
add_custom_command(
OUTPUT ${CASSDK_JAR}
COMMAND ${ANT_CMD} jar
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${java_srcs} # 依赖的java源码改变时会执行此命令
COMMENT "run ant jar -> cassdk_jni.jar"
)
# 创建自定义target
add_custom_target(CASSDKJavaJar ALL
DEPENDS ${CASSDK_JAR} # 依赖add_custom_command输出的jar包
COMMENT "building cassdk_jni.jar"
)
# 安装jar包到 jar目录下
install(FILES ${CASSDK_JAR}
DESTINATION jar COMPONENT JavaModule
)
endif(BUILD_JAVA)
上面代码中使用ant脚本比较简单,也一并贴出来
build.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project basedir="." default="build" name="cassdk_jni">
<property environment="env"/>
<property name="debuglevel" value="source,lines,vars"/>
<property name="target" value="1.6"/>
<property name="source" value="1.6"/>
<property name="build.dir" value="${basedir}/build" />
<property name="jar.dir" value="${build.dir}/jar" />
<property name="release.dir" value="${basedir}/../release/jar" />
<property name="jarname" value="${ant.project.name}.jar" />
<path id="cassdk_jni.classpath">
<pathelement location="build/classes"/>
</path>
<target name="init">
<mkdir dir="build/classes"/>
<copy includeemptydirs="false" todir="build/classes">
<fileset dir="src">
<exclude name="**/*.launch"/>
<exclude name="**/*.java"/>
</fileset>
</copy>
<copy includeemptydirs="false" todir="build/classes">
<fileset dir="test">
<exclude name="**/*.launch"/>
<exclude name="**/*.java"/>
</fileset>
</copy>
</target>
<target name="clean" description="清除生成的文件">
<delete dir="${build.dir}"/>
</target>
<target depends="clean" name="cleanall" description="清除所有生成的文件(包含release)">
<delete dir="${release.dir}"/>
</target>
<target depends="init" name="build" description="编译代码">
<echo message="${ant.project.name}: ${ant.file}"/>
<javac debug="true" debuglevel="${debuglevel}" destdir="build/classes" includeantruntime="false" source="${source}" target="${target}" encoding="utf-8">
<src path="src"/>
<classpath refid="cassdk_jni.classpath"/>
</javac>
</target>
<target depends="clean,build" name="rebuild" description="重新编译代码"/>
<target name="jar" depends="build" description="生成jar包">
<echo message="生成${jar.dir}/${jarname}" />
<jar destfile="${jar.dir}/${jarname}">
<fileset dir="${build.dir}/classes" />
</jar>
</target>
<target name="release" depends="clean,jar" description="复制jar包到release目录">
<copy includeemptydirs="false" todir="${release.dir}">
<fileset dir="${jar.dir}">
<include name="**/*.jar"/>
</fileset>
</copy>
</target>
</project>
参考资料:
https://cmake.org/cmake/help/v3.1/command/add_custom_command.html
https://cmake.org/cmake/help/v3.1/command/add_custom_target.html