编译工具之cmake

简介

这儿我们援引自cmake官网(https://cmake.org/overview/)来初步认识cmake是什么东西。
CMake是一个操作系统平台无关和编译器无关的用于管理编译(构建)过程的一个可扩展、开源系统。不像大多数跨平台系统,CMake可以与本地构建环境协同作业。将简单的配置文件放置在源码路径(CMakeLists.txt)用来生成标准的编译文件(Unix系统中生成makefile, windows MSVC环境生成相应工程)。CMake支持in-place和out-of-place这两种构建模式,in-place构建过程的中间文件与源码在同一目录,out-of-place的中间文件是在一个单独的构建目录。
使用CMake可以简单的生成动态库,静态库和可执行文件。因为其配置简单而得到广泛使用,目前大部分开源项目都已支持CMake方式构建。不过CMake目前仅支持C/C++、Java项目。
以上简单介绍了CMake的基本特征,但是对于这种类似的工具没接触过的人可能还是不明白这是个什么东西。正如上面讲的,它是一个系统,系统包含的工具,编译,库等元素可以说它都有,接下来我们一起来一一体会它是个什么东西。

入门

CMake获取

如何获取CMake,这儿介绍两种方式,安装包与源代码。

https://cmake.org/download/

从以上地址可以获取Windows与Unix平台的安装包与对应源代码。对于安装包的安装过程这儿就不详述,仅对源码编译说明一下。
本地系统已经安装了cmake:
$ cd <cmake-src>
$ mkdir build
$ cmake ..
$ make -j8
$ sudo make install
本地系统未安装cmake:
$ cd <cmake-src>
$ mkdir build
$ ../configure
$ make -j8
$ sudo make install
经过以上步骤cmake就安装到了你的系统中。windows平台的直接将工程导入VS,利用VS编译即可。

CMake的使用

以下都以Unix平台为例做说明。
建立一个简单工程
$ cd <test-project-dir>
$ echo -e "#include <stdint.h>\nint main()\n{\n\tprintf(\"Hello World\!\\\n\");\n\treturn 0;\n}" > test.c
$ touch CMakeLists.txt
...
$ mkdir build && cd build
$ cmake ..
$ make
以上创建CMakeLists.txt文件后在开始先对其编辑,编辑完后在接着执行后面命令,CMakeLists.txt文件内容如下:
# ---[ Test project
project(Test C)
set(TEST_FILE "${CMAKE_SOURCE_DIR}/test.c")
add_executable(test ${TEST_FILE})
如上所示,配置三行就可以编译test.c了,当然,如果只是为了编译一个文件大可不必使用cmake,但是如果这儿的工程是代码量十分多,目录结构也很复杂,依赖关系等也不是简单的一两行就能够配置完成的呢。在编译那种较大的工程时,cmake就体现出了其配置简单与管理方便的特点。
完成了上面的所有操作,那么在编译目录(build)下我们就应该可以获得一个test可执行文件,但是大家应该疑问也是更多了。比如为什么要创建叫做CMakeLists.txt的文件?为什么CMakeLists.txt文件要那么写?为什么要执行cmake这个命令?为什么执行make?等等...
首先CMakeLists.txt是CMake的标准配置文件命名,在cmake命令执行时,它只去找有没有叫CMakeLists.txt这个名字的文件,所以CMake的配置文件必须叫那个名。而里面如project, set, add_executable等这样的关键字是什么?这些可以说是CMake提供的API,就像c标准库实现的各种库函数类似,我们需要利用这些API来写配置文件,而写配置文件的过程也就相当与编程了,只不过这儿所要完成的事是设计编译流程罢了,其中也有各种赋值,条件判断,宏定义,函数调用等,因此要熟练使用cmake来进行复杂工程的配置也不是一蹴而就的事儿,需要慢慢积累。
在Unix下,最终cmake通过配置文件输出的其实是Makefile文件,而Makefile文件是Make的配置文件,Make也是Unix下基本的构建系统之一,所以我们获得Makefile文件后,剩下的工作就完全交给make来完成了。

授之以渔

学习CMake其实也是一个学习一门新的编程语言的过程,在配置文件里使用的所有API都可以通过cmake --help-commands命令获得,包括if等条件判断语句也是,所以学习的过程也是一个查文档的过程。网上有很多中文博客等也有对其的简单介绍或者相关问题的解决方案,但是总有网上的人没有遇到的问题,在那时是需要我们拥有自己依赖这些基础的功底自己去解决问题的。所以学会自己查文档,学会自己思考才是最总要的。
如下列举获取相关帮助的方法:
    man cmake——获取cmake命令的使用方法
    cmake --help-commands ——获取所有可用的API
    cmake --help-command cmd ——查询cmd命令的使用方法
    cmake --help-modules ——获取所有可用的功能模块
    cmake --help-variables ——获取所有可用的预定义变量
    https://cmake.org/ ——CMake官网

例子

以下给出一个构建一个工程的常用的配置文件,里面配置适合大部分工程的构建,也包含了许多CMake的常用特性,以此为模块可以更快速的编写新的工程的配置文件。
以下配置文件对应的模板工程的项目地址:https://code.csdn.net/yxhlfx/cmake_test/tree/master
其中包含了一些常用的编译库,可执行文件,添加依赖项等的方式,同时还提供了一种**编译汇编项目**的方法和编译**汇编与C结合的项目**的方法。
# Set the cmake version of minimum limits, we can 
# know details from cmake --help-command cmake_minimum_required
# FATAL_ERROR is a pre-defined constant
cmake_minimum_required(VERSION 2.8.12.2 FATAL_ERROR)

# In some specia situation, we need set a cmake policy to
# control cmake action, this is a example for this.
if(POLICY CMP0025)
  cmake_policy(SET CMP0025 NEW)
endif()

# We need set a name for a project
project(Examples)

# If we need judge a variable and do something
if(NOT EXAMPLES_EXTERNAL_SOURCES)
  set(EXAMPLES_EXTERNAL_SOURCES "${CMAKE_SOURCE_DIR}/test.c")
endif()

# receive a variable from a commandline, such as
# "cmake -DPRINT_MESSAGE" and switch to define a macro
#
if("${PRINT_MESSAGE}" STREQUAL "ERROR")
    macro(MESSAGE_ERROR _TEXT)
        message(FATAL_ERROR "${_TEXT}")
    endmacro()
else()
    macro(MESSAGE_WARN _TEXT)
        message(STATUS "${_TEXT}")
    endmacro()
endif()

# contain of the header file
include_directories(include) # The relative paths are interpreted as relative to
                             # the current source directory

# if you have some sub-CMakeLists file, you need include
# but here we have not.
# add_subdirectory(src)

# Use the cmake pre-defined module, we also use of more modules,
# we can get more modules details from "cmake --help-module-list"
include(CheckIncludeFileCXX)
CHECK_INCLUDE_FILE_CXX(test.h HAVE_TEST_H)
if(NOT HAVE_TEST_H)
    message_warn("Don't found the Header file test.h")
else()
    message_warn("Found the Header file test.h ${HAVE_TEST_H}")
endif()

# ---[ Config generation
# we can use the *.in file generate the config header file and other is
# *.pc file. details from "cmake --help-command configure_file"
configure_file(${CMAKE_SOURCE_DIR}/config.h.in "${PROJECT_BINARY_DIR}/config.h")

# we build a executable
# details from "cmake --help-command add_executable"
add_executable(example_exe ${EXAMPLES_EXTERNAL_SOURCES})

# we build a share library
# details from "cmake --help-command add_library"
add_library(example_sso SHARED ${EXAMPLES_EXTERNAL_SOURCES})

# we build a static library
# details from "cmake --help-command add_library"
add_library(example_slib STATIC ${EXAMPLES_EXTERNAL_SOURCES})

# we build a Java Project
# java project like the c project, just the input source file
# is *.java

# we build a Assembly Project
# because of cmake don't support the assembly, so we must custom the rules
# to compile the assembly file
if(NOT ASSEMBLY_SOURCE_FILE)
    file(GLOB ASSEMBLY_SOURCE_FILE pure/*.S)
endif()

foreach(GENERATE_OBJ ${ASSEMBLY_SOURCE_FILE})
    list(APPEND obj_list_pure GENERATE_OBJ)
    list(LENGTH obj_list_pure Index)
    set(Mide_Obj "PURE_${Index}")

    add_custom_command(OUTPUT  ${Mide_Obj}.o
        COMMAND gcc -c ${GENERATE_OBJ} -o ${Mide_Obj}.o
        WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
        COMMENT  "compile the assembly file")
    set(OBJ_LIST_pure "${OBJ_LIST_pure} ${CMAKE_CURRENT_BINARY_DIR}/${Mide_Obj}.o")
endforeach()
string(STRIP ${OBJ_LIST_pure} OBJ_LIST_pure)
add_custom_command(OUTPUT Assemble_Example
  COMMAND ld ${OBJ_LIST_pure} -o Pure_Assemble_Example -T ${CMAKE_SOURCE_DIR}/pure/pure_as_test.lds
  COMMENT "Linking Assembly executable Assemble_Example")
add_custom_target(PRE_Pure_Assemble ALL DEPENDS ${OBJ_LIST_pure})
add_custom_target(Pure_Assemble_Example ALL DEPENDS Assemble_Example PRE_Pure_Assemble)

# we build a Assembly and a C Project
if(NOT MIXED_AS_SOURCE_FILE)
    set(MIXED_AS_SOURCE_FILE "${CMAKE_SOURCE_DIR}/pure/pure_as_test.S")
endif()
if(NOT MIXED_C_SOURCE_FILE)
    file(GLOB MIXED_C_SOURCE_FILE *.c)
endif()

foreach(GENERATE_OBJ ${MIXED_AS_SOURCE_FILE})
    list(APPEND obj_list_mixed GENERATE_OBJ)
    list(LENGTH obj_list_mixed Index)
    set(Mide_Obj "MIXED_${Index}")

    add_custom_command(OUTPUT  ${Mide_Obj}.o
        COMMAND gcc -c ${GENERATE_OBJ} -o ${Mide_Obj}.o
        WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
        COMMENT  "compile the assembly file")
    set(OBJ_LIST_mixed "${OBJ_LIST_mixed} ${CMAKE_CURRENT_BINARY_DIR}/${Mide_Obj}.o")
endforeach()
string(STRIP "${OBJ_LIST_mixed}" OBJ_LIST_mixed)
add_custom_command(TARGET PRE_Mixed_Assemble
  PRE_LINK
  COMMENT "Linking Assembly executable Assemble_Example")
add_custom_target(PRE_Mixed_Assemble ALL DEPENDS ${OBJ_LIST_mixed})
add_executable(Mixed_Example ${MIXED_C_SOURCE_FILE})
add_dependencies(Mixed_Example PRE_Mixed_Assemble)
set_target_properties(Mixed_Example PROPERTIES
    LINK_FLAGS "${OBJ_LIST_mixed}")
target_link_libraries(Mixed_Example pthread)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值