ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CMake上手:学习和练习案例素材

2020-12-13 20:33:06  阅读:208  来源: 互联网

标签:cmake target library 素材 version 上手 CMake include hello


CMake上手:学习和练习案例素材
git clone https://github.com/ttroy50/cmake-examples.git

素材分类目录

准备

开始之前,检查一下自己的cmake版本是不错的选择,毕竟我们要编写的CMakeists文件都是要用cmake来执行的,而不同版本的cmake支持的语法可能是不同的。

$ cmake --version
cmake version 3.16.3

CMake suite maintained and supported by Kitware (kitware.com/cmake).

另外,使用CMake编译一个工程的简要过程:

mkdir build   #在工程文件夹里创建一个build文件夹并进入
cd build      
cmake ..    #对工程目录中的CMakeLists.txt文件,用cmake生成编译需要的材料
make      #编译连接生成可执行文件

然后,就可以运行可执行文件:

./XXXX 

01-basic

A-hello-cmake

任何CMakeLists.txt文件,都要在开头声明对cmake版本的要求。
对于只包含一个main函数文件的简单工程,
(1)定义工程名称
(2)为工程添加可执行文件。

# Set the minimum version of CMake that can be used
# To find the cmake version run
# $ cmake --version
cmake_minimum_required(VERSION 3.5)

# Set the project name
project (hello_cmake)

# Add an executable
add_executable(hello_cmake main.cpp)

B-hello-headers

对于有include和src目录的工程结构(也是通常的应用程序工程),源码文件可能不止一个(其中有一个包含main入口函数),因此,
(1)定义SOURCES变量来列出所有的源码文件,并在为工程添加可执行文件时,用SOURCES变量代替。
(2)include文件夹里的头文件,是需要包含的内容,使用target_include_directories语法指明要包含的头文件所在目录。

# Set the minimum version of CMake that can be used
# To find the cmake version run
# $ cmake --version
cmake_minimum_required(VERSION 3.5)

# Set the project name
project (hello_headers)

# Create a sources variable with a link to all cpp files to compile
set(SOURCES
    src/Hello.cpp
    src/main.cpp
)

# Add an executable with the above sources
add_executable(hello_headers ${SOURCES})

# Set the directories that should be included in the build command for this target
# when running g++ these will be included as -I/directory/path/
target_include_directories(hello_headers
    PRIVATE 
        ${PROJECT_SOURCE_DIR}/include
)

C-static-library

对于想要生成静态连接库的工程,
(1)把相应类的源码文件add_library为STATIC,并使用target_include_directories标明其所需要的头文件就可以了(注意是PUBLIC),这部分最后编译出来的就是*.a文件(在windows系统上就是我们常见的*.lib),是工程发布给别人的交付件。
(2)为了对这个静态文件进行测试,一个工程的main入口文件可以生成可执行文件,并将对应的静态连接库作为类似于外部依赖进行连接,以进行必要的测试,使用target_link_libraries声明可执行文件对于静态连接库的依赖。如果只是要制作和发布静态库,最终这部分可以不要,把.a和头文件发布就可以了,用户程序通过文档参考来使用静态库即可。

cmake_minimum_required(VERSION 3.5)
project(hello_library)
############################################################
# Create a library
############################################################
#Generate the static library from the library sources
add_library(hello_library STATIC 
    src/Hello.cpp
)
target_include_directories(hello_library
    PUBLIC 
        ${PROJECT_SOURCE_DIR}/include
)
############################################################
# Create an executable
############################################################
# Add an executable with the above sources
add_executable(hello_binary 
    src/main.cpp
)
# link the new hello_library target with the hello_binary target
target_link_libraries( hello_binary
    PRIVATE 
        hello_library
)

D-shared-library

对于想要生成动态链接库的工程,
(1)add_library的时候就要声明为SHARED方式,然后,把头文件声明出来(注意和静态连接库一样是PUBLIC),两句最后就可以编译出来*.so文件(在windows系统上就是我们常见的*.dll),这是动态链接库工程的交付件。
(2)也是为了能够进行必要测试,工程可以同时编译出带main入口函数的文件,生成可执行文件。
【备注】这里编译动态连接库中,有一句add_library(hello::library ALIAS hello_library),并非必要,只是对库的名称进行了重命名,所以,后面编译可执行文件时,target_link_libraries里连接的依赖库名称,就可以写成hello::library或者原来的名字hello_library都行,如果没有这句重命名,那么连接的依赖库名称,只能使用原来的工程名称hello_library(由于这是在一个工程里面,动态链接库和可执行文件都是这个名称,有时候可能让人迷惑,如果进行专门的测试,另外建立一个可执行工程,那么就不会容易迷惑了)

cmake_minimum_required(VERSION 3.5)
project(hello_library)
############################################################
# Create a library
############################################################
#Generate the shared library from the library sources
add_library(hello_library SHARED 
    src/Hello.cpp
)
add_library(hello::library ALIAS hello_library)
target_include_directories(hello_library
    PUBLIC 
        ${PROJECT_SOURCE_DIR}/include
)
############################################################
# Create an executable
############################################################
# Add an executable with the above sources
add_executable(hello_binary
    src/main.cpp
)
# link the new hello_library target with the hello_binary target
target_link_libraries( hello_binary
    PRIVATE 
        hello::library
)

E-installing

可执行文件和静态/动态连接库工程,都是在本地工程目录进行编译安装,生成可执行文件或连接库文件。可安装的工程,就是要把这些东西编译出来,而且能够安装到系统目录去。
(1)编译一个动态连接库,给个单独的库名称,叫做cmake_examples_inst。
(2)编译一个可执行文件,给个单独的名称,叫做cmake_examples_inst_bin,而且这个可执行文件,依赖于动态链接库cmake_examples_inst。

cmake_minimum_required(VERSION 3.5)
project(cmake_examples_install)
############################################################
# Create a library
############################################################
#Generate the shared library from the library sources
add_library(cmake_examples_inst SHARED
    src/Hello.cpp
)
target_include_directories(cmake_examples_inst
    PUBLIC 
        ${PROJECT_SOURCE_DIR}/include
)
############################################################
# Create an executable
############################################################
# Add an executable with the above sources
add_executable(cmake_examples_inst_bin
    src/main.cpp
)
# link the new hello_library target with the hello_binary target
target_link_libraries( cmake_examples_inst_bin
    PRIVATE 
        cmake_examples_inst
)
############################################################
# Install
############################################################
# Binaries
install (TARGETS cmake_examples_inst_bin
    DESTINATION bin)
# Library
# Note: may not work on windows
install (TARGETS cmake_examples_inst
    LIBRARY DESTINATION lib)
# Header files
install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/ 
    DESTINATION include)
# Config
install (FILES cmake-examples.conf
    DESTINATION etc)

(3)install部分主要包括四个部分的拷贝:
1)把可执行文件,拷贝安装到目标bin目录,也就是系统的/usr/local/bin目录
2)把库文件,拷贝安装到目标lib目录,也就是系统的/usr/local/lib目录
3)把头文件,拷贝到目标include目录,这对应的是系统的/usr/local/include目录,因为工程的头文件在工程本地的目录安排是installing/*.h,因此按照${PROJECT_SOURCE_DIR}/include/的拷贝规则,就把这个文件夹整体放在了/usr/local/include下面。
4)把配置文件,拷贝安装到目标etc目录,也就是/usr/local/etc目录,这个配置文件可以根据工程需要进行一些环境变量的定义,本例中就是个空文件。
最后,我们通过编译以后,执行安装的过程就可以一目了然:

~/project/cmake-examples/01-basic/E-installing/build$ sudo make install
[ 50%] Built target cmake_examples_inst
[100%] Built target cmake_examples_inst_bin
Install the project...
-- Install configuration: ""
-- Installing: /usr/local/bin/cmake_examples_inst_bin
-- Set runtime path of "/usr/local/bin/cmake_examples_inst_bin" to ""
-- Installing: /usr/local/lib/libcmake_examples_inst.so
-- Up-to-date: /usr/local/include
-- Installing: /usr/local/include/installing
-- Installing: /usr/local/include/installing/Hello.h
-- Installing: /usr/local/etc/cmake-examples.conf

并且,ldconfig刷新一下系统相关的环境变量以后,输入可执行文件的开始字母就可以tab联想出对应的完整命令,并且可以执行我们安装到系统中的这个可执行文件了:

$ cmake
cmake                    cmake_examples_inst_bin  
$ cmake_examples_inst_bin 
Hello Install!

F-build-type

如果说前面都是在用默认的方式把最简单的事情先走通一遍,那么,这里的build选项的设置,是编译一个项目过程中,灵活性的体现。
以下例子,判断在没有定义BUILD_TYPE和CONFIGURATION_TYPES的情况下,
(1)强制设置CMAKE_BUILD_TYPE为RelWithDebInfo类型,
(2)为cmake-gui提供四种编译选项的显示

# Set the minimum version of CMake that can be used
# To find the cmake version run
# $ cmake --version
cmake_minimum_required(VERSION 3.5)

# Set a default build type if none was specified
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  message("Setting build type to 'RelWithDebInfo' as none was specified.")
  set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Choose the type of build." FORCE)
  # Set the possible values of build type for cmake-gui
  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release"
    "MinSizeRel" "RelWithDebInfo")
endif()

# Set the project name
project (build_type)
# Add an executable
add_executable(cmake_examples_build_type main.cpp)

我们可以在cmake这个工程后查看到,他的安装配置项是RelWithDebInfo

~/project/cmake-examples/01-basic/F-build-type/build$ cmake --install .
-- Install configuration: "RelWithDebInfo"

G-compile-flags

编译选项也是编译过程中的一种灵活性设置,通常是各种优化开关,下面这个例子就是指定编译的时候,添加 -D EX2 选项,并在编译可执行文件的时候,target_compile_definitions指定EX3选项。

cmake_minimum_required(VERSION 3.5)

# Set a default C++ compile flag
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DEX2" CACHE STRING "Set C++ Compiler Flags" FORCE)

# Set the project name
project (compile_flags)

# Add an executable
add_executable(cmake_examples_compile_flags main.cpp)

target_compile_definitions(cmake_examples_compile_flags 
    PRIVATE EX3
)

H-third-party-library

在这个例子中,使用find_package查找工程中用到的第三方库,并打印是否找到。然后在编译可执行文件的时候,target_link_libraries对应的库,就像我们自己的工程中需要依赖的静态连接库和动态链接库一样。

cmake_minimum_required(VERSION 3.5)
# Set the project name
project (third_party_include)
# find a boost install with the libraries filesystem and system
find_package(Boost 1.46.1 REQUIRED COMPONENTS filesystem system)
# check if boost was found
if(Boost_FOUND)
    message ("boost found")
else()
    message (FATAL_ERROR "Cannot find Boost")
endif()

# Add an executable
add_executable(third_party_include main.cpp)
# link against the boost libraries
target_link_libraries( third_party_include
    PRIVATE
        Boost::filesystem
)

在这个文件写法中,target_link_libraries并没有对应加入头文件包含,这是Boost::filesystem这种写法的优势,而且居然就只链接了Boost::filesystem,源码中其实是还会依赖Boost::system库的,find_package那一句也说明了要依赖两个库:

#include <boost/shared_ptr.hpp>
#include <boost/filesystem.hpp>

那为什么就链接一个也行呢?因为filesystem同时也依赖于system,如果链接了filesystem,那么filesystem所依赖的所有库都会自动被链接进来。因此,target_link_libraries这一句,也可以使用另外一种写法来替代,看上去会更直观清晰,但是比较繁琐一些:

# Include the boost headers
target_include_directories( third_party_include
    PRIVATE ${Boost_INCLUDE_DIRS}
)

## link against the boost libraries
target_link_libraries( third_party_include
    PRIVATE
    ${Boost_SYSTEM_LIBRARY}
    ${Boost_FILESYSTEM_LIBRARY}
)

当然,这个是真正外部依赖库,不像上面自己的连接库那样,在工程中刚编译出来,除非库有问题没编过,否则库肯定是应该存在的。但是,真正的外部依赖库,环境上就并不一定真的存在了,如果不存在,那么在连接的时候就会出现连接错误。本例中这样,还会提前打印一条message提示信息。

CMake Error at /usr/share/cmake-3.16/Modules/FindPackageHandleStandardArgs.cmake:146 (message):
  Could NOT find Boost (missing: Boost_INCLUDE_DIR filesystem system)
  (Required is at least version "1.46.1")
Call Stack (most recent call first):
  /usr/share/cmake-3.16/Modules/FindPackageHandleStandardArgs.cmake:393 (_FPHSA_FAILURE_MESSAGE)
  /usr/share/cmake-3.16/Modules/FindBoost.cmake:2179 (find_package_handle_standard_args)
  CMakeLists.txt:8 (find_package)

由于我本地环境上确实没有这第三方库,所以,我必须安装这库才能cmake这个案例。
以下安装以后,就可以了。

sudo apt-get install libboost-filesystem-dev
..................
获取:1 http://cn.archive.ubuntu.com/ubuntu focal/main amd64 libboost1.71-dev amd64 1.71.0-6ubuntu6 [9,068 kB]
获取:2 http://cn.archive.ubuntu.com/ubuntu focal/main amd64 libboost-filesystem1.71.0 amd64 1.71.0-6ubuntu6 [242 kB]                                            
获取:3 http://cn.archive.ubuntu.com/ubuntu focal/main amd64 libboost-system1.71.0 amd64 1.71.0-6ubuntu6 [205 kB]                                                
获取:4 http://cn.archive.ubuntu.com/ubuntu focal/main amd64 libboost-system1.71-dev amd64 1.71.0-6ubuntu6 [205 kB]                                              
获取:5 http://cn.archive.ubuntu.com/ubuntu focal/main amd64 libboost-filesystem1.71-dev amd64 1.71.0-6ubuntu6 [258 kB]                                          
获取:6 http://cn.archive.ubuntu.com/ubuntu focal/main amd64 libboost-filesystem-dev amd64 1.71.0.0ubuntu2 [3,420 B]  
...................
~/project/cmake-examples/01-basic/H-third-party-library/build$ cmake ..
-- Found Boost: /usr/lib/x86_64-linux-gnu/cmake/Boost-1.71.0/BoostConfig.cmake (found suitable version "1.71.0", minimum required is "1.46.1") found components: filesystem system 
boost found
-- Configuring done
-- Generating done
-- Build files have been written to: /home/wkli/project/cmake-examples/01-basic/H-third-party-library/build
~/project/cmake-examples/01-basic/H-third-party-library/build$ make
Scanning dependencies of target third_party_include
[ 50%] Building CXX object CMakeFiles/third_party_include.dir/main.cpp.o
[100%] Linking CXX executable third_party_include
[100%] Built target third_party_include
~/project/cmake-examples/01-basic/H-third-party-library/build$ ls
CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile  third_party_include
~/project/cmake-examples/01-basic/H-third-party-library/build$ ./third_party_include
Hello Third Party Include!
Path is not relative

I-compiling-with-clang

clang和clang++是另一款轻量级C/C++编译器,这个案例说明我们在执行cmake的时候可以显式指定编译器,否则,就生成使用默认的GCC/G++编译器的Makefile工程。

#!/bin/bash
# Ubuntu supports multiple versions of clang to be installed at the same time.
# The tests need to determine the clang binary before calling cmake
clang_bin=`which clang`
clang_xx_bin=`which clang++`

if [ -z $clang_bin ]; then
    clang_ver=`dpkg --get-selections | grep clang | grep -v -m1 libclang | cut -f1 | cut -d '-' -f2`
    clang_bin="clang-$clang_ver"
    clang_xx_bin="clang++-$clang_ver"
fi

echo "Will use clang [$clang_bin] and clang++ [$clang_xx_bin]"

mkdir -p build.clang && cd build.clang && \
    cmake .. -DCMAKE_C_COMPILER=$clang_bin -DCMAKE_CXX_COMPILER=$clang_xx_bin && make

J-building-with-ninja

Ninja是一款极速构建工具,这个案例说明在执行cmake的时候可以显示指定 -G 参数,构建Ninja工程,之后使用ninja进行增量构建。

#!/bin/bash
# Travis-ci cmake version doesn't support ninja, so first check if it's supported
ninja_supported=`cmake --help | grep Ninja` 

if [ -z $ninja_supported ]; then   # *****report param too much error*****
    echo "Ninja not supported"
    exit
fi
mkdir -p build.ninja && cd build.ninja && \
    cmake .. -G Ninja && ninja

K-imported-targets

CMakeLists还是上面第三方依赖的文件,只是写了个shell脚本来进行自动执行cmake编译过程。没觉得这个案例想要新讲什么东西,倒是判断cmake version的表达式那句写的不知道啥意思,不注释掉的话,脚本明显直接在那句exit 0了。

#!/bin/bash
# Make sure we have the minimum cmake version
cmake_version=`cmake --version | grep version | cut -d" " -f3`
#[[ "$cmake_version"  =~ ([3-9][.][5-18.][.][0-9]) ]] || exit 0  # *****will auto exit*****
echo "correct version of cmake"
mkdir -p build && cd build && cmake .. && make
if [ $? -ne 0 ]; then
    echo "Error running example"
    exit 1
fi

L-cpp-standard

几种条件编译的cmake写法。

i-common-method

主要是进行条件编译,判断是否支持C++11标准,支持的话就用,比较直来直去的思路,但是繁琐的写法。

# Set the minimum version of CMake that can be used
# To find the cmake version run
# $ cmake --version
cmake_minimum_required(VERSION 2.8)
# Set the project name
project (hello_cpp11)
# try conditional compilation
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
# check results and add flag
if(COMPILER_SUPPORTS_CXX11)#
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
elseif(COMPILER_SUPPORTS_CXX0X)#
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
else()
    message(STATUS "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()
# Add an executable
add_executable(hello_cpp11 main.cpp)

ii-cxx-standard

直接设置CMAKE_CXX_STANDARD变量,指定编译器执行的C++标准

# Set the minimum version of CMake that can be used
# To find the cmake version run
# $ cmake --version
cmake_minimum_required(VERSION 3.1)
# Set the project name
project (hello_cpp11)
# set the C++ standard to C++ 11
set(CMAKE_CXX_STANDARD 11)
# Add an executable
add_executable(hello_cpp11 main.cpp)

iii-compile-features

使用target_compile_features函数,自动选择编译器执行的C++标准

# Set the minimum version of CMake that can be used
# To find the cmake version run
# $ cmake --version
cmake_minimum_required(VERSION 3.1)
# Set the project name
project (hello_cpp11)
# Add an executable
add_executable(hello_cpp11 main.cpp)
# set the C++ standard to the appropriate standard for using auto
target_compile_features(hello_cpp11 PUBLIC cxx_auto_type)
# Print the list of known compile features for this version of CMake
message("List of compile features: ${CMAKE_CXX_COMPILE_FEATURES}")

02-sub-projects

03-code-generation

04-static-analysis

05-unit-testing

06-installer

07-package-management

(实践记录中,未完待续)

标签:cmake,target,library,素材,version,上手,CMake,include,hello
来源: https://blog.csdn.net/weixin_44252417/article/details/111106353

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有