标签归档:cmake

用cmake和astyle自动格式化代码

以前开发中经常忘了格式化代码,导致代码交流传递中非常不”雅“;而不同的人使用不同的格式化工具或者配置又回导致所有源码相关的文件特别多的格式变动,每次svn/git上提交一大堆跟逻辑无关的变动。其实在开发框架中cmake和astyle已经完整的帮我们解决了这个问题,业务代码里面借鉴即可。

  1. 选择一项格式化工具(astyle),统一格式化配置


     
  2. 将格式化任务hook到项目的编译工程中来
    首先查询astyle命令是否存在,如果存在将格式化任务挂到目标的编译过程中来,PRE_BUILD指定编译器前格式化,然后递归的处理指定工作目录下的c和h文件。


     

cpack生成框架安装包

团队业务框架的自动部署工具已经比较完善,默认从源码编译安装最新的tag并且会自动更新(也可以手动指定版本),但是前段时间遇到一个特别弱的开发机,编译时间长并且偶尔中断,所以我们诞生了直接生库安装包的诉求来弥补某些特殊场景,而cmake其实就内置cpack来干这件事情。

  1. 打包文件的整理
    install指令把需要用到的target/file/directory指定好,cpack_generator能够直接生效。
  2. cpack参数设置
    包类型,包文件名,版本号等的设置。如果不需要source_generator可以不用设置这一项。

  3. cmake依赖处理
    框架中各种库的依赖关系,环境数据的注入还得依赖cmake的配置,所以这里还需要做配置。这样业务使用的时候,只需要inclue(xxx-${XXX_VERSION_MAJOR}.${XXX_VERSION_MINOR}.${XXX_VERSION_PATCH}-config.cmake)即可。

  4. 生成包
    最后,make package生成二进制包,make package_source生成源码包。假定版本号是3.1.3,项目名是test,最终build目录下就会生成test-3.1.3.tar.gz和test-3.1.3_src.tar.gz。

in-source builds or out-source builds

最近一段时间在跟进解决一系列的跟编译相关的事情,发现大家特别喜欢在源码目录直接编译代码,功能角度上这没有什么问题,但从源码管理角度来看这种方式还是存在很多不好的地方,最直观的感受就是一堆凌乱的编译中间文件。那么为什么我们应该选择out-source builds而不是in-source builds呢?(这篇文章列得很详细http://voices.canonical.com/jussi.pakkanen/2013/04/16/why-you-should-consider-using-separate-build-directories/)

1. 易于清理
这个是最容易理解也最显而易见的,一堆中间临时文件,干扰你阅读源码,干扰你打包源码tree,干扰你清理编译中间文件。

2. 可以针对不同的编译选项设立独立的编译目录
这样的好处显而易见,可能做测试,可能给不同类型的用户使用,需要多套不同的编译选项,这样in-source builds就不能满足要求了。

3. 可以对源码目录和编译目录做特定控制或优化
比如源码不允许修改(只读),或者需要加速编译,可能会把编译目录放到更快速的存储设备上。

向目标文件注入编译环境信息

【背景】
前段时间一个离职同事几年前的老模块需要交接,由于长时间无人维护,一时找不到代码路径,只得从整个后台中心的代码库里面“人肉“。记得曾经还出现过类似的情况,找不到老的编译环境(现有的系统,机器都升级了),被迫重构了老代码。工作时间越长,这样的问题也就会越来越频繁,老是这么死板的处理也不是个办法,费时费力,苦不堪言。如果可以将server部署前的编译环境信息集成到目标文件中,是否就可以根据线上运行的目标文件定位到相关的信息呢?目前部门的开发方式都是依赖于框架,所以只得在框架上下功夫。定好2个核心原则:对开发者透明,能便捷的获取信息。

【实现】
整个框架的编译体系是通过cmake维系的,通过其丰富的能力可以实现一些特殊的功效。框架使用gflags来维护命令行参数,本已经集成一些基本的参数,比如threads, conf,pid_file等,所以直接通过这种方式来获取编译环境信息对开发者更加熟悉和便捷。
1. cmake获取编译环境信息。

2. cmake将信息string以宏的方式透传给框架头文件。

3. 框架头文件使用宏来区分框架编译和业务编译。

4. 运行效果。

 

 

控制cmake查询路径

【背景】
部门依赖特定框架开发代码,框架中已经集成了protobuf,但是用户却有可能在开发机上胡乱安装protobuf。而业务链接到的版本是框架的protobuf,但cmake中find_package(Protobuf)默认的是用系统protobuf中的protoc编译描述文件,当系统中pb和框架pb版本不同时,就会造成编译错误。

【构思】
研究了下Cmake中的FindProtobuf.cmake文件,_protobuf_find_libraries|_protobuf_find_libraries|_protobuf_find_libraries中查找lib,include,bin均是通过find_path,find_program,find_library来完成的,只要控制查找路径即可。

于是,框架的xxx-config.cmake中添加一下两行解决问题。业务依赖框架时find_pakcage(xxx)就会自动设置cmake的路径变量,最终达到控制查询结果的目的。

 

cmake为什么好用

好东西绝总是有让你爱不释手的魔力,cmake就是其中一个。

1. cmake管理外部库
如果要使用很多库,而这些库又都没有安装到系统中,使用makefile的工作量真是难以想象,而CMake却能很轻松的处理这种场景。

  • 库的CMakeLists.txt中添加
    #导出目标
    export(PACKAGE config_parse)
    #指定要生成的导出文件
    export(TARGETS config_parse FILE config_parse-exports.cmake)
    #指定供find_package使用的配置文件
    configure_file(config_parse-config.cmake.in ${CMAKE_BINARY_DIR}/config_parse-config.cmake)
  • 库工程根目录添加config_parse-config.cmake.in文件
    include_directories(${CMAKE_SOURCE_DIR}/src) #指定库所需要的头文件目录
    include(“${CMAKE_BINARY_DIR}/config_parse-exports.cmake”) #指定库导出文件

接下来,编译该库之后,将在用户目录的.cmake目录下生成该库的信息文件。

  • 需要使用该库时,只需添加find_package(目标名),就可以按常规使用方法使用库了。
    find_package(config_parse REQUIRED)
    find_package会在用户目录的.cmake目录下找到对应的库编译路径,然后加载路径下的配置文件。

2. cmake来添加编译前/后的任务
cmake告诉我们编译管理系统有的不仅仅是编译。想格式化源代码,生成doxygen代码文档等操作都可以集成到编译管理系统中来。截一段

  • add_custom_command(TARGET config_parse
    POST_BUILD
    COMMENT “[ generate doc ]”
    COMMAND ${DOXYGEN_EXECUTABLE} 2>/dev/null
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    #VERBATIM
    )
  • add_custom_command(TARGET config_parse
    PRE_BUILD
    COMMENT “[ astyle code ]”
    COMMAND astyle src/*.c src/*.h 1>/dev/null 2>/dev/null
    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
    #VERBATIM
    )

3. 查找需要的库/命令
如果安装前缀是/usr的话,/usr/share/cmake/Modules下就是CMake提供的所有模块了,加载模块之后,就拥有了很多变量,不仅可以用来测试系统是否包含所查找的东西,同时提供很多。

  • 命令(Doxygen)
    CMakeLists.txt中添加include(FindDoxygen)之后就可以直接使用${DOXYGEN_EXECUTABLE}来代替Doxygen二进制文件了。
  • 库(Gtest)
    CMakeLists.txt中添加include(FindGTest)之后就可以直接使用${GTEST_LIBRARIES}和${GTEST_INCLUDE_DIRS},免去了查找库和头文件位置的麻烦。

4. cmake生成安装包
cpack应该是cmake中最强大的模块了,而且可以独立运行。用它来生成源码包和二进制包,也就是传说中的安装包。通过CPACK模块可以看出,它支持
DEB/RPM/NSIS/TGZ/ZIP/TBZ2/STGZ/ZIP等,需要注意的是cpack依赖于install指令,install中必须使用相对路径才能使cpack可用(CMAKE_INSTALL_PREFIX表示安装的根路径),同时tgz这类generator默认会忽略空目录。

SET(CPACK_GENERATOR RPM)
SET(CPACK_SOURCE_GENERATOR TGZ)
INCLUDE(CPack)

通过make package_source 可以生成源码包
通过make package 可以生成二进制包

5. cmake生成项目依赖图
添加cmake –graphviz来编译,就会生出graphviz识别的文件,然后调用dot命令,
例如dot graphviz文件 -Tpng则生成png文件

6. cmake外部编译
编译的中间文件总是使人烦乱,cmake推荐外部编译,这就是你为什么会通常会看到一个build文件夹的原因。官方目前仍未提供限制内部编译的指令,so只能委婉的禁用内部编译
IF(“${PROJECT_SOURCE_DIR}” STREQUAL “${PROJECT_BINARY_DIR}”)
MESSAGE(FATAL_ERROR “in-source builds are not allowed.”)
ENDIF(“${PROJECT_SOURCE_DIR}” STREQUAL “${PROJECT_BINARY_DIR}”)