cmake_minimum_required(VERSION 3.8)
project(Calculator LANGUAGES CXX)
add_library(calclib STATIC src/calclib.cpp include/calc/lib.hpp)
target_include_directories(calclib PUBLIC include)
target_compile_features(calclib PUBLIC cxx_std_11)
add_executable(calc apps/calc.cpp)
target_link_libraries(calc PUBLIC calclib)
if(variable)
# If variable is `ON`, `YES`, `TRUE`, `Y`, or non zero number
else()
# If variable is `0`, `OFF`, `NO`, `FALSE`, `N`, `IGNORE`, `NOTFOUND`, `""`, or ends in `-NOTFOUND`
endif()
if("${variable}")
# True if variable is not false-like
else()
# Note that undefined variables would be `""` thus false
endif()
宏和函数
function(SIMPLE REQUIRED_ARG)
message(STATUS "Simple arguments: ${REQUIRED_ARG}, followed by ${ARGV}")
set(${REQUIRED_ARG} "From SIMPLE" PARENT_SCOPE)
endfunction()
simple(This)
message("Output: ${This}")
# Assuming the canonical version is listed in a single line
# This would be in several parts if picking up from MAJOR, MINOR, etc.
set(VERSION_REGEX "#define MY_VERSION[ \t]+\"(.+)\"")
# Read in the line containing the version
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/My/Version.hpp"
VERSION_STRING REGEX ${VERSION_REGEX})
# Pick out just the version
string(REGEX REPLACE ${VERSION_REGEX} "\\1" VERSION_STRING "${VERSION_STRING}")
# Automatically getting PROJECT_VERSION_MAJOR, My_VERSION_MAJOR, etc.
project(My LANGUAGES CXX VERSION ${VERSION_STRING})
title: CMake简明教程 date: 2018-08-12 17:39:00 tags:
CMake
前言
主要最近的换工作,完全在Linux下开发,虽然以前都接触过CMake,不过体系也是零散的,遂做了一个简短的CMake教程,以供后续快速入门。
另外,好久也没有写文章了,这份工作还是有一定的技术性,之前的那家公司是开发/维护,大部分工作都是维护,没有什么写文章的激情。
所以,今天是硬凑一篇文章。
正文
CMake
CMake是跨平台的元构建系统,也就是说,它不实际产生构建行为,它只是生成给其他构建系统使用的文件,比如Makefile,MSBuild的solution file。
CMake根据读取名为CMakeLists.txt的文件,然后生成平台特定的构建文件,但是一个很大的问题是,CMake官方提供的教程特别复杂,对于新手的个坑,很难快速入门。
这个教程会通过例子来学习怎么用CMake。以下我们提供几个C++源代码供例子使用:
那么描述构建的CMakeLists.txt内容会是以下:
以上代码很简单,但是第一个问题是,它是不可移植的,因为它没有任何逻辑判断就设置了GCC/Clang的特定编译参数。
第二个问题是,它全局改变了include的搜索路径。
CMake也要有好的书写习惯,采用更加现代的方式来写CMake文件:
注意了,以上代码有几点改变了:
然后在构建过程中,你会发现没有警告,因为CMake不会设定编译器的警告级别,需要你根据不同平台来指定相应的编译器警告参数:
如果就采用上述的CMake文件,那么它生成的工程文件并不好,没有预期,你会发现如果生成VS的solution,你打开工程,你会发现没有包含头文件(vector.h array.h)。因为CMake不理解C++语言,它只是构建工具。
所以CMake文件中要改变下:
当然,也可以通过CMake的source_group命令给文件归类:
这样VS工程下就可以看到对C++源文件分类的文件夹图标了。
Tests
CMake是一堆工具的集合,所以它有一个test runner,叫CTest。
要使用它,你需要显式指定它:
测试返回0表示成功,返回其他值表示失败。
还可以自定义,通过set_tests_properties来设置其相关属性。
对于我们的例子工程,我们仅仅是运行bin文件,并不做额外检查:
COMMAND后面的表达式是generator-expression。
最后,我们的CMakeLists.txt的内容会是:
libraries
之前的教程都是很简单的例子,但是现实中的项目往往要拆分模块,链接外部的第三方库或者链接工程内的库。
如果不想阅读此章节,可以参考JetBrain的CLion提供的一个简明的CMake教程。
里面记录如何包含链接外部的library。
构建生成一个library的cmake指令如下:
下面我们来写一个计算器程序的cmake,bin文件依赖了lib文件,这样看起来就像一个小工程了:
变量和缓存变量
为什么要说这个?因为如果cmake维护一个很大的工程,会有各种编译策略参数,这个就需要逻辑判断,变量这些就随之诞生了。
给一个局部变量设置一个值:
当然,cmake也有list类型的变量,有2种表达方式:
以上变量如果离开作用域就无效了。
下面来说下缓存变量,如果你要从命令行来设置cmake的变量,那么就需要声明缓存变量。类似于CMAKE_BUILD_TYPE这样的变量都是缓存变量:
但是以上不会替换已经存在的值,需要按照以下这样:
环境变量
cmake可以访问环境变量:
属性
cmake也会在属性中保存一些信息,这些属性有点像变量,但是这些属性的作用一般是针对一个target或者目录什么的。cmake的属性变量是以CMAKE_打头的,类似CMAKE_BUILD_TYPE,CMAKE_CXX_STANDARD这些。
比如使用CXX标准可以用设置属性的方式办到,有两种表达:
当然既然有set,当然有get:
Cmake编程
控制流
宏和函数
与源码文件“通信”
Cmake允许源代码访问cmake的变量,这个指令就是configure_file。
这样的功能在版本管理上经常使用:
Version.h.in
可以从上面的配置看出来,本质上cmake只是把后缀in的文件,进行变量标记替换,然后再拷贝到指定目录的文件名。没有什么神奇的。
当然,cmake也可以反过来,cmake代码访问源码文件的内容:
怎样规划你的工程结构
基本如下:
运行其他的程序
编译的时候要做很多事情,比如你构建完毕要用nsis进行打包等等,有些时候不得不调用其他命令行。
配置时运行一个命令
编译时运行一个命令
Cmake其他的一些特性
CMAKE_BUILD_TYPE变量默认既不是Debug也不是Release。必须指定。
关于C++ 11等一些新标准,有以下配置:
查找库