cmake:在Makefile中运行ant脚本

时间:2022-01-08 12:41:35

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_commandadd_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