【cmake】CMakeList添加库|添加头文件|添加路径|add_executable、add_library、target_link_libraries|添加编译选项|宏开关

目录

官网查阅

开胃菜例子

CMakeLists生成和添加依赖库

CMakeLists更多小例子

生成.so共享库文件

调用.so共享库文件

生成一个可执行程序的 CMakeList

生成一个.so动态库的 CMakeList 

CMAKE 添加编译选项|-g编译参数/选项

包含文件的的目录include_directories

优化项|优化等级

Cmake设置优化等级| cmake 生成 debug和 release 版

设置默认构建类型

CMake设置编译参数/选项

如何在cmakelists中加入-ldl编译选项

CMake指定gcc,g++版本编译

CMake 关闭警告的方法

关闭编译器优化

CMakeLists 实现动态宏开关

去掉编译优化

CMake--List用法

CmakeLists.txt单行注释和多行注释 

CMakeList 通配符

其他未归类

add_custom_target  自定义命令

add_dependencies 

Cmake条件判断指令|if 判断优先级

设置编译时和程序运行时去哪个目录找动态库

#指定运行时动态库的加载路径

#指定链接时动态库的路径

cmake install 和打包设定

CMakeLists.txt单行注释和多行注释


作者:bandaoyu,持续更新,链接:https://blog.csdn.net/bandaoyu/article/details/115165199

官网查阅

CMake 3.22.0-rc1文档:https://cmake.org/cmake/help/latest/search.html?q=add_library

https://cmake.org/cmake/help/v3.23/manual/cmake-commands.7.html

 CMake Cookbook:《CMake菜谱(CMake Cookbook中文版)》 :https://www.bookstack.cn/read/CMake-Cookbook/content-preface-preface-chinese.md

开胃菜例子

CMakeLists生成和添加依赖库

原文;cmake之生成动态库:https://www.cnblogs.com/pandamohist/p/13408455.html

1、目录结构

│  CMakeLists.txt
│  index.txt
│  
├─build
├─include
│      hello.h
│      hi.h
│      
└─src
        hello.cxx
        hi.cxx

2、CMakeLists.txt

cmake_minimum_required(VERSION 3.1)

#项目名
project(libhello)

# 1、指定库的目录变量
set(libhello_src src/hello.cxx)
# 指定头文件搜索路径
include_directories("${PROJECT_SOURCE_DIR}/include")



# 2、添加库(对应的两个项目)
add_library( hello_shared SHARED ${libhello_src})
add_library( hello_static STATIC ${libhello_src})
#  按照一般的习惯,静态库名字跟动态库名字应该是一致的,只是扩展名不同;
# 即:静态库名为 libhello.a; 动态库名为libhello.so ;
# 所以,希望 "hello_static" 在输出时,不是"hello_static",而是以"hello"的名字显示,故设置如下
# SET_TARGET_PROPERTIES (hello_static PROPERTIES OUTPUT_NAME "hello")


# 3、cmake在构建一个新的target时,会尝试清理掉其他使用这个名字的库,
# 因此,在构建libhello.a时,就会清理掉libhello.so.
# 为了回避这个问题,比如再次使用SET_TARGET_PROPERTIES定义 CLEAN_DIRECT_OUTPUT属性。
SET_TARGET_PROPERTIES (hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
SET_TARGET_PROPERTIES (hello_shared PROPERTIES CLEAN_DIRECT_OUTPUT 1)


# 4、按照规则,动态库是应该包含一个版本号的,
# VERSION指代动态库版本,SOVERSION指代API版本。
SET_TARGET_PROPERTIES (hello_static PROPERTIES VERSION 1.1 SOVERSION 1)
SET_TARGET_PROPERTIES (hello_shared PROPERTIES VERSION 1.1 SOVERSION 1)


# 5、若将libhello.a, libhello.so.x以及hello.h安装到系统目录,才能真正让其他人开发使用,
# 本例中,将hello的共享库安装到<prefix>/lib目录;
# 将hello.h安装<prefix>/include/hello目录。
#INSTALL (TARGETS hello hello_shared LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
#INSTALL (TARGETS hello hello_static LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
#INSTALL (FILES hello.h DESTINATION include/hello)

3、configure and generate

xxx/to/path

cd build
cmake ..

4、其他设置

  若需要指定输出路径,尝试下面的示例指令:

# 设置VS会自动新建Debug和Release文件夹
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Bin)

# 设置分别设置Debug和Release输出目录
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/../../build/Debug)

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/Bin)

CMakeLists更多小例子

生成.so共享库文件


下面是我的几个文件:

1hello.cpp

//hello.cpp
 int Calculate_sum_Of_Two_Number(int x,int y)
{
   int z=0;
   z=x+y;
   return (z);
}


2hello.hpp

//hello.hpp
#ifndef     __HELLO_H
#define     __HELLO_H
int Calculate_sum_Of_Two_Number(int x,int y);
#endif


3 main.cpp

//main.cpp
#include "hello.hpp"
#include <stdio.h>
int main(void)
{
   int a=0,b=0,c=0;
   printf("please input two parameter:");
   scanf("%d",&a);
   scanf("%d",&b);
   c=Calculate_sum_Of_Two_Number(a,b);
   printf("the sum is : %d",c);
   return 0;
}


4 CMakeLists.txt

#要求的Cmake最低版本
CMAKE_MINIMUM_REQUIRED( VERSION 2.8)
 
#工程名称
PROJECT(main)
 
#设置编译器编译模式:
set( CMAKE_BUILD_TYPE "Debug" )
 
#生成共享库
#get the shared package
#here needs no .hpp
add_library(calculate_shared SHARED  hello.cpp)
 
#生成可以执行的文件
add_executable(main main.cpp)
 
#连接共享库
target_link_libraries(main calculate_shared)


上面CmakeLists.txt里面, 共享库的名称是calculate_shared,这个是我们可以自己更改的。生成的可执行文件是main, 这个名称也是可以更改的。

不过需要注意的是,hello.cpp里面不用在包含hello.hpp 了。(汗,因为这个导致出错,提示说是重复定义函数了);

编译生成:

mkdir build
cd    build
cmake ..
make
我们就可以看到build生成了 如下的文件:

CMakeCache.txt  cmake_install.cmake     main
CMakeFiles      libcalculate_shared.so  Makefile

 libcalculate_shared.so就是生成的共享库文件。

他们的路径是:/home/fan/dev/cmake/4-exer/

下面有build文件夹,以及main.cpp, hello.cpp, hello.hpp, 

build文件夹下面有共享库 libcalculate_shared.so.so

调用共享库文件


所有的外部依赖库都是这样的,比如opencv ,openni, eigen等等,原理是一样的,只不过他们已经安装在系统里面了,可以查找,而这个则是需要我们自己去配置。

即我上面生成的共享库文件本质上和opencv的库是相同的。只不过这个共享库需要自己手动配置。

比如我又新建了一个工程,需要调用上面的共享库 libcalculate_shared.so。

main.cpp如下:

//main.cpp
#include <stdio.h>
#include <iostream>
#include "hello.hpp"
using namespace std;
int main(void)
{
   int x=2,y=3;
   int z=0;
   z=Calculate_sum_Of_Two_Number(x,y);
   cout<<"the result is:"<<z<<endl;
   return 0;
}


那么在CMakeLists.txt里面,我需要告诉CMake, 这个头文件可以在哪里找到,头文件所定义的函数又可以在哪里找到。

上面hello.hpp的路径是:/home/fan/dev/cmake/4-exer/hello.hpp

libcalculate_shared.so的路径是/home/fan/dev/cmake/4-exer/build/libcalculate_shared.so

则CMakeLists.txt如下:

CMAKE_MINIMUM_REQUIRED( VERSION 2.8)
 
PROJECT(main)
#设置编译器编译模式:
SET( CMAKE_BUILD_TYPE "Debug" )
 
SET(HELLO_INCLUE 
    /home/fan/dev/cmake/4-exer/)
 
SET(HELLO_SO 
    /home/fan/dev/cmake/4-exer/build/libcalculate_shared.so)
 
INCLUDE_DIRECTORIES(${HELLO_INCLUE})
 
add_executable(main main.cpp)
 
target_link_libraries(main ${HELLO_SO})


这里要注意一些细节(对于我这个渣渣来说的)

1、${   }这种形式代表一个变量,比如上面的,HELLO_INCLUE ,就是我自己定义的一个变量。

2、头文件包含到头文件所在的文件夹,即 /home/fan/dev/cmake/4-exer/

3、共享库要指明具体的共享库 ,精确到.so

其实主要的就是指明这个调用这个共享库的时候,使用的头文件,以及共享库本身所在的位置,然后包含链接就可以了。

安装过的共享库(例如opencv)就不用这么麻烦了,因为它的地址都放在了变量里面。

Opencv的依赖添加
比如Opencv, 它的头文件和.so文件都已经放在了系统变量里面,不用向上面自己定义了(上面例子里面的头文件和共享库文件的地址都是我自己设置的)

它的CMakeLists.txt如下:

find_package(OpenCV REQUIRED)

include_directories(${OPENCV_INCLUDE_DIRS})

target_link_libraries(MAIN ${OpenCV_LIBS})

只需要查找就可以了,OpenCV_LIBS  和  OPENCV_INCLUDE_DIRS  都是系统帮我们已经定义好的,所以比较容易

参考博客:

1、如何写自己的CmakeLists.txt   https://www.cnblogs.com/chaofn/p/10160555.html

2、 【OpenCV】使用CMake链接自己路径下面的OpenCV库 https://blog.csdn.net/twt520ly/article/details/81981473

原文链接:https://blog.csdn.net/qq_37761077/article/details/88750711

生成一个可执行程序的 CMakeList

#添加包含文件的的目录
include_directories(${cppzmq_INCLUDE_DIR})             

#用${SOURCE_FILES}指定的文件,生成可执行文件sample_project 
add_executable(sample_project ${SOURCE_FILES}) 

#生成可执行文件sample_project 需要连接 ${CMAKE_THREAD_LIBS_INIT}指定的库
target_link_libraries (sample_project  ${CMAKE_THREAD_LIBS_INIT}) 

生成一个.so动态库的 CMakeList 

#用${SRC_LISTS}指定的所有的源文件生成一个库,名字叫libsugan
add_library(libsugan ${SRC_LISTS})   

#生成libsugan库需要链接 ${OpenCV_LIBS}、 ${PROJECT_SOURCE_DIR}/lib/libCommonUtilities.so、${PROJECT_SOURCE_DIR}/lib/libInuStreams.so
target_link_libraries(libsugan                 
    ${OpenCV_LIBS}
    ${PROJECT_SOURCE_DIR}/lib/libCommonUtilities.so
    ${PROJECT_SOURCE_DIR}/lib/libInuStreams.so
)


原文链接:https://blog.csdn.net/bandaoyu/article/details/115165199

grep -nR  "common"  ./  --include=*.txt|grep -vE "src_bak|boost|erasure-code|doc|link.txt"

add_library(生成库),target_link_libraries(生成目标连接的库),set_target_properties

生成静态库:

add_library(libsugan ${SRC_LISTS})    #用${SRC_LISTS}生成静态库libsugan

ADD_LIBRARY(static_lib STATIC ${DIR_SUB_SRCS})

生成动态库(加SHARED  ):
add_library(libsugan  SHARED  ${SRC_LISTS})    #用${SRC_LISTS}生成动态库libsugan

target_link_libraries(libsugan      #生成静态库libsugan还需链接依赖库${OpenCV_LIBS}…
    ${OpenCV_LIBS}
    ${PROJECT_SOURCE_DIR}/lib/libCommonUtilities.so
    ${PROJECT_SOURCE_DIR}/lib/libInuStreams.so
)

#上面的配置生成名字为libsugan的静态库,但Linux下对库的存储格式是lib+name.a,所以库libsugan存储出来的结果就是liblibsugan.a,看着很别扭。用下面这句,保证了存储出来的静态库叫做libsugan.a:

set_target_properties(libsugan PROPERTIES OUTPUT_NAME "sugan")

#但是请千万注意,在整个CmakeLists.txt里

#如果想链接生成的这个库必须使用    “add_library(libsugan ${SRC_LISTS})”指明的名字。
set_target_properties(libsugan PROPERTIES OUTPUT_NAME "sugan")

add_executable(demo ./src/main.cpp)
target_link_libraries(demo libsugan)

连接库:

target_link_libraries(demo libsugan)

target_link_libraries(app libsort.a)  #生成app 链入 libsort.a静态库

TARGET_LINK_LIBRARIES(app libsort.a)

原例子:

add_library,target_link_libraries,set_target_properties,target_link_libraries使用联系:https://blog.csdn.net/michaelhan3/article/details/69568362

#工程名字
project(Camera_sugan)                  

#编译最低cmake版本
cmake_minimum_required(VERSION 2.6)    

#设置c++编译器
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11" )  

#在整个电脑上找opencv包
find_package(OpenCV REQUIRED)    

#包含头文件路径
include_directories(             
    ./include/inudev/
    ./src/
)

#将所有的源文件列为一个集合,集合名字叫做SRC_LISTS
set(SRC_LISTS                   
    ./src/inuitive.cpp
    ./src/runCamera_Qfeeltech.cpp
)

#将集合里的所有的源文件生成一个静态库,该静态库的名字libsugan,
注意,在整个CmakeLists里都要用libsugan这个
add_library(libsugan ${SRC_LISTS})   

#名字来代替之前那个集合生成的库。
target_link_libraries(libsugan    #链接静态库需要的依赖库
    ${OpenCV_LIBS}
    ${PROJECT_SOURCE_DIR}/lib/libCommonUtilities.so
    ${PROJECT_SOURCE_DIR}/lib/libInuStreams.so
)


原文链接:https://blog.csdn.net/michaelhan3/article/details/69568362

CMAKE 添加编译选项|-g编译参数/选项

add_definitions 和add_compile_options,二者添加的编译选项是针对所有编译器的(包括c和c++编译器)。

add_definitions 和add_compile_options的区别是:

add_definitions 可用于添加任何标志,但旨在添加预处理器定义。

此命令已被替代方案取代:
使用 add_compile_definitions() 添加预处理器定义。
使用 include_directories() 添加包含目录。
使用 add_compile_options() 添加其他选项。

add_definitions:https://cmake.org/cmake/help/latest/command/add_definitions.html

添加 -g编译参数/选项

方法一:add_definitions("-g")/ add_compile_options


在文件 CMakeLists.txt添加下面一条语句
add_definitions("-g")

添加其他编译参数/选项

例如下面的代码

#判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持

if(CMAKE_COMPILER_IS_GNUCXX)
    add_compile_options(-std=c++11)
    message(STATUS "optional:-std=c++11")   
endif(CMAKE_COMPILER_IS_GNUCXX)
 



使用add_compile_options添加-std=c++11选项,是想在编译c++代码时加上c++11支持选项。但是因为add_compile_options是针对所有类型编译器的,所以在编译c代码时,就会产生如下warning

J:\workspace\facecl.gcc>make b64
[ 50%] Building C object libb64/CMakeFiles/b64.dir/libb64-1.2.1/src/cdecode.c.obj
cc1.exe: warning: command line option ‘-std=c++11’ is valid for C++/ObjC++ but not for C
[100%] Building C object libb64/CMakeFiles/b64.dir/libb64-1.2.1/src/cencode.c.obj
cc1.exe: warning: command line option ‘-std=c++11’ is valid for C++/ObjC++ but not for C
Linking C static library libb64.a
[100%] Built target b64

虽然并不影响编译,但看着的确是不爽啊,要消除这个warning,就不能使用add_compile_options,而是只针对c++编译器添加这个option。

方法二:set

所以如下修改代码,则警告消除。

#判断编译器类型,如果是gcc编译器,则在编译选项中加入c++11支持
if(CMAKE_COMPILER_IS_GNUCXX)
    set(CMAKE_CXX_FLAGS "-std=c++11 ${CMAKE_CXX_FLAGS}")
    message(STATUS "optional:-std=c++11")   
endif(CMAKE_COMPILER_IS_GNUCXX)


原文链接:https://blog.csdn.net/qinglongzhan/article/details/80743731

包含文件的的目录include_directories

include_directories(${cppzmq_INCLUDE_DIR})  //添加包含文件的的目录

add_definitions 可用于添加任何标志,但旨在添加预处理器定义。

此命令已被替代方案取代:
使用 add_compile_definitions() 添加预处理器定义。
使用 include_directories() 添加包含目录。
使用 add_compile_options() 添加其他选项。

优化项|优化等级

更多和详细解释:https://blog.csdn.net/bandaoyu/article/details/123700034

-O0禁止编译器进行优化。默认为此项。
-O1尝试优化编译时间和可执行文件大小。

-O2更多的优化,会尝试几乎全部的优化功能,但不会进行“空间换时间”的优化方法。

-O3在 -O2 的基础上再打开一些优化选项:-finline-functions, -funswitch-loops 和 -fgcse-after-reload 。

-Os对生成文件大小进行优化。它会打开 -O2 开的除了会那些增加文件大小的全部选项。

可以通过下面的命令查找工程中设置优化项的地方:

grep -nR  "\-O"  ./  --include=*.txt

过滤掉含有 build、boost、erasure-code、doc、link.txt字样的结果

grep -nR  "\-O"  ./  --include=*.txt|grep  -vE  "build|src_bak|boost|erasure-code|doc|link.txt"

Cmake设置优化等级| cmake 生成 debug和 release 版

见文章:

https://blog.csdn.net/bandaoyu/article/details/123700034

    CMake 中有一个变量 CMAKE_BUILD_TYPE ,可以的取值是 Debug 、Release、 RelWithDebInfo 和 MinSizeRel。

当这个变量值为 Debug 的时候,CMake 会使用变量 CMAKE_CXX_FLAGS_DEBUG 和 CMAKE_C_FLAGS_DEBUG 中的字符串作为编译选项生成 Makefile ,

当这个变量值为 Release 的时候,工程会使用变量 CMAKE_CXX_FLAGS_RELEASE 和 CMAKE_C_FLAGS_RELEASE 选项生成 Makefile。

提供的级别为:

  • Release - Adds the -O3 -DNDEBUG flags to the compiler
  • Debug - Adds the -g flag
  • MinSizeRel - Adds -Os -DNDEBUG
  • RelWithDebInfo - Adds -O2 -g -DNDEBUG flags


链接:https://www.jianshu.com/p/d761232e8e90
 

CMakeCache.txt:89:CMAKE_ASM_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
CMakeCache.txt:92:CMAKE_ASM_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
CMakeCache.txt:95:CMAKE_ASM_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
CMakeCache.txt:123:CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
CMakeCache.txt:126:CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
CMakeCache.txt:129:CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
CMakeCache.txt:150:CMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
CMakeCache.txt:153:CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
CMakeCache.txt:156:CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG

 

cmake设置默认CMAKE_BUILD_TYPE

原文[CMake] Set default build type in CMakeLists.txt

CMakeLists.txt里写入

IF (NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE "Release" CACHE STRING
        "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
ENDIF()

设置默认构建类型

CMake提供的默认构建类型是不包含用于优化的编译器标志。对于某些项目,您可能需要设置默认生成类型,以便不必记住设置它。
为此,您可以将以下内容添加到CMakeLists.txt文件顶层

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()


作者:xingxingRealzyx
链接:https://www.jianshu.com/p/d761232e8e90

CMake设置编译参数/选项

而set命令设置CMAKE_C_FLAGS或CMAKE_CXX_FLAGS变量则是分别只针对c和c++编译器的

对c编译器的

set(CMAKE_C_FLAGS"-O3 -fopenmp -fPIC -Wno-deprecated -Wenum-compare -std=c++14")

针对c++编译器的

set(CMAKE_CXX_FLAGS "-O3 -fopenmp -fPIC -Wno-deprecated -Wenum-compare -std=c++14")

如何在cmakelists中加入-ldl编译选项

cmakelists.txt中,在增加可执行程序后增加TARGET_LINK_LIBRARIES
eg:
add_executable(xx ${ALL_F} ${WE_F})
TARGET_LINK_LIBRARIES(dl)
TARGET_LINK_LIBRARIES(m)

set(CMAKE_C_FLAGS "-ldl")

在add_executable(${PROJECT_NAME} "main.cpp")后面添加
target_link_libraries(${PROJECT_NAME} dl)

target_link_libraries(exe1 -Wl, - -whole-archive lib1 -Wl, -  no-whole-archive)

CMake指定gcc,g++版本编译

系统默认的gcc/g++在/usr/bin目录下。

我们升级安装的gcc目录在/usr/local/bin目录下,现在我们希望使用升级后的gcc。

通过百度搜索出来的结果,大多是如下操作:

在CMakeLists.txt中调用编译器之前添加:

SET(CMAKE_C_COMPILER "/usr/local/bin/gcc")
SET(CMAKE_CXX_COMPILER "/usr/local/bin/g++")

然而经过本人亲自实践,该方法不起作用(没有删掉build里面的东西重新cmake的原因?),正确的做法是:

执行cmake命令之前,在shell终端先设置如下两个变量:

export CC=/usr/local/bin/gcc
export CXX=/usr/local/bin/g++

可能还需要

export LD_LIBRARY_PATH=/usr/local/gcc-xxx/lib:$LD_LIBRARY_PATH

/usr/local/gcc-xxx/lib 是你的新gcc的lib位置

然后再执行cmake等后续命令,这样就可以用指定的编译器版本了。

vim ~/.bashrc  source ~/.bashrc

CMake 关闭警告的方法

在CMakeLists.txt中添加add_definitions(-w)

应用于单个target

  if(CMAKE_COMPILER_IS_GNUCC)
 target_compile_options(main PRIVATE"-Wall")
 endif()
 if(MSVC)
 target_compile_options(main PRIVATE"/ W4")
 endif()
  

应用于所有target

  if(CMAKE_COMPILER_IS_GNUCC)
 set(CMAKE_CXX_FLAGS"$ {CMAKE_CXX_FLAGS} -Wall")
 endif()
 if(MSVC)
 set(CMAKE_CXX_FLAGS"$ {CMAKE_CXX_FLAGS} / W4")
 endif()
  

注意:为GCC或/ WX添加-Werror以便MSVC将所有警告视为错误。这会将所有警告视为错误。这对于新项目来说可以方便地执行严格的警告。

另外, -Wall 并不意味着"所有错误";从历史意义上讲,它意味着"每个人都可以达成一致的所有错误""。从 -Wall -Wextra 开始,然后仔细阅读您的版本的GCC手册,并找到 else 编译器可以为您提供关于警告的信息。

CMake和编译器警告 https://www.it1352.com/784160.html

关闭编译器优化

(未验证)

1)add_compile_options(-fno-elide-constructors)    #关闭编译器优化

2)set(CMAKE_CXX_FLAGS "-fno-elide-constructors ${CMAKE_CXX_FLAGS}")

CMakeLists 实现动态宏开关

去掉编译优化

在CMakeList中添加:

if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE Release)
endif()

set(CMAKE_CXX_FLAGS "-Wall -Wextra")
set(CMAKE_CXX_FLAGS_DEBUG "-g")
set(CMAKE_CXX_FLAGS_RELEASE "-O3")

执行的时候

cmake  -DCMAKE_BUILD_TYPE=Release

也可以在上一层(调用本CMakeList.txt的)的CMakeList.txt中添加下面:

option (CMAKE_BUILD_TYPE "Use tutorial provided math implementation"  ON)

表示启用CMAKE_BUILD_TYPE 宏。

option (CMAKE_BUILD_TYPE "Use tutorial provided math implementation"  OFF) #表示关

参考:

《c++ - Optimize in CMake by default 》:https://stackoverflow.com/questions/41361631/optimize-in-cmake-by-default

《How to compile without optimizations -O0 using CMake》:https://unix.stackexchange.com/questions/187455/how-to-compile-without-optimizations-o0-using-cmake

例子

最近在工作中需要通过一份C代码控制逻辑走向,网上找了一下资料,发现可以通过在CMakeLists文件中动态定义宏开关,从而能够达到编译出不同逻辑流的代码。

具体步骤:

首先,我在src代码里编写了若干debug的输出:

#IFDEF DEBUG
 
    some print command;
 
#ENDIF
然后,在CMakeLists文件中添加DEBUG的定义:

IF (CMAKE_BUILD_TYPE STREQUAL DEBUG)
    ADD_DEFINITIONS(-DDEBUG)
ENDIF()
最后,在cmake的时候设置参数 -DCMAKE_BUILD_TYPE 为 DEBUG:

$ cmake .. -DCMAKE_BUILD_TYPE=DEBUG
$ make -j4
这样再运行可执行文件时就会打印出some print command的debug信息了。如果不想看到debug信息,只需在参数中不设置DEBUG参数,或者将DEBUG参数设置为其它值即可(以下两种方式二者选其一):

$ cmake ..
$ cmake .. -DCMAKE_BUILD_TYPE=RELEASE
到此 CMakeLists 实现动态宏开关介绍完成。

原文链接:https://blog.csdn.net/qq_19734597/article/details/104461963

CMake--List用法

CMake--List用法 :https://www.cnblogs.com/narjaja/p/8343765.html

CmakeLists.txt单行注释和多行注释 

单行注释:使用“#”
多行注释:使用“#[[ ]]”

在这里插入图片描述

CMakeList 通配符

  $<TARGET_OBJECTS:A>:

说明:

$<TARGET_OBJECTS:objLib>¶
New in version 3.1.

List of objects resulting from build of objLib.
构建 objLib 产生的对象列表

add_executable(test   test.cc   $<TARGET_OBJECTS:A>),表示使用 test.cc和构建A产生的对象 联合编译出test。

add_executable(test2   $<TARGET_OBJECTS:A>  $<TARGET_OBJECTS:B> ),表示使用构建A产生的对象  和 构建B产生的对象 联合编译出test。

例子:某个工程如下

-  CMakeLists.txt
-  include
    -  a.hh
-  lib
    -  CMakeLists.txt
    -  a.cc
-  src
    -  CMakeLists.txt
    -  main.cc
-  test
    -  CMakeLists.txt
    -  test.cc

使用 CMake 使用命令编译 test.cc
add_executable(test test.cc $<TARGET_OBJECTS:A>)

使用的A.o 的编译为
add_library(A   OBJECT  A.cc)

来自:https://stackoverflow.com/questions/35696103/cmake-wildcard-for-target-objects

其他未归类

add_custom_target  自定义命令

https://www.bookstack.cn/read/CMake-Cookbook/content-chapter5-5.4-chinese.md

add_custom_target(finish 
    COMMAND ${CMAKE_COMMAND} -E echo compile finish
    COMMAND ${CMAKE_COMMAND} -E copy_directory ${SOURCE_DIR}/config ${SOURCE_DIR}/etc
    COMMAND ${CMAKE_COMMAND} -E copy ${SOURCE_DIR}/log.txt ${SOURCE_DIR}/etc
    )

定义了一个自定义命令:finish ,执行该命令就会进行以下操作:

COMMAND ${CMAKE_COMMAND} -E echo compile finish
COMMAND ${CMAKE_COMMAND} -E copy_directory ${SOURCE_DIR}/config ${SOURCE_DIR}/etc
COMMAND ${CMAKE_COMMAND} -E copy ${SOURCE_DIR}/log.txt ${SOURCE_DIR}/etc

单独执行命令的方式是: cmake --build <dir> [<options>] [-- <build-tool-options>]

cmake --build  /home/mydir   --target   finish

与 add_executable(main main.cpp) 、add_library(mylib mylib.cpp) 的不同是,这两个会生成(产出)main、mylib文件。add_custom_target(comandname  ……)只会执行列出的操作,不会产生comandname文件

add_dependencies 

如果main 依赖a.so b.so TARGET_LINK_LIBRARIES(main a.so b.so c.so d.so) 

而a.so b.so 的生成晚于main(即编译脚本的顺序把a.so b.so 安排在后面编译),则需要ADD_DEPENDENCIES(main a.so b.so) 提前为main编译a.so b.so,否则可能会报错:符号的定义找不到 (这些符号恰恰就在a.so 和 b.so中)

详情见:https://blog.csdn.net/KingOfMyHeart/article/details/112983922

mark_as_advanced  

mark_as_advanced  将CMake 的缓存变量标记为高级。

mark_as_advanced([CLEAR|FORCE] VAR VAR2 VAR...)
将缓存的变量标记为高级变量。其中,高级变量指的是那些在CMake GUI中,只有当“显示高级选项”被打开时才会被显示的变量。如果CLEAR是第一个选项,参数中的高级变量将变回非高级变量。如果FORCE是第一个选项,参数中的变量会被提升为高级变量。如果两者都未出现,新的变量会被标记为高级变量;如果这个变量已经是高级/非高级状态的话,它将会维持原状。
该命令在脚本中无效。

原文链接:https://blog.csdn.net/hankern/article/details/120376405

Cmake条件判断指令|if 判断优先级

cmake(十六)Cmake条件判断指令:https://blog.csdn.net/wzj_110/article/details/116105719

一  基础语法

①  基本框架

②  优先级

③  条件的类型

设置编译时和程序运行时去哪个目录找动态库

#指定运行时动态库的加载路径


SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)  #Use the install path for the RPATH
SET(CMAKE_INSTALL_RPATH   "\${ORIGIN}/lib" )     #The rpath to use for installed targets.

https://cmake.org/cmake/help/v3.0/variable/CMAKE_INSTALL_RPATH.html?highlight=cmake_install_rpath

程序运行时,搜索动态库的顺序(优先级)优先级是这样的:

1. RPATH ,编译链接时加入 -rpath 参数 指定的目录
2. LD_LIBRARY_PATH  这个环境变量指定的目录
3. /etc/ld.so.conf 配置文件。
4. /usr/lib 、 /lib 和 /usr/local/lib ,系统默认路径。

所以我们设置了RPATH ,并且RPATH 下有要找的动态库,程序就首先加载它

注意:

可以看到,RPATH与RUNPATH中间隔着LD_LIBRARY_PATH。为了让用户可以通过修改LD_LIBRARY_PATH来指定.so文件,大多数编译器都将输出的RPATH留空,并用RUNPATH代替RPATH。

linux cmake分别指定编译/运行时动态库链接路径:https://blog.csdn.net/JCYAO_/article/details/102519998



探讨CMake中关于RPATH的使用(https://www.cnblogs.com/rickyk/p/3875084.html):

CMake在默认情况下是会给你的exe加入相关RPATH的,可能给你加入你不想要的一些RPATH(就是一些Cmake认为的你的程序应该去哪里寻找需要的动态库的目,可以用“readelf -d 你的程序”来查看,例子:

[root]readelf -d  /opt/bin/ceph-osd

Dynamic section at offset 0x10a36e8 contains 59 entries:
  Tag        Type                         Name/Value
 ……
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [ld-linux-x86-64.so.2]
 0x000000000000000f (RPATH)              Library rpath: [/opt/lib_upgrade/osd:/opt/lib:/opt/lib_upgrade/ceph-common:/opt/lib:/opt/lib/ceph:/opt/lib/ceph/erasure-code:/opt/lib/ceph/compressor:/opt/lib/ceph/crypto:/opt/lib/rados-classes:/opt/lib_upgrade_reserve/ceph]
 0x000000000000000c (INIT)               0x34ffe0
 0x000000000000000d (FINI)               0xddee2c
……

仔细查阅寻找之后发现,CMake里面维护了3个比较重要的RPATH变量,即CMAKE_SKIP_RPATH,CMAKE_SKIP_BUILD_RPATH,CMKAE_INSTALL_RPATH.

CMAKE_SKIP_RPATH,简单说,就是强制CMake不在构建期间安装install期间给你加上它所认为的RPATH.(不在动态库中写入RUNPATH项)

cmake .. -DCMAKE_SKIP_RPATH=TRUE或CMakelist中加set(CMAKE_SKIP_BUILD_RPATH TRUE)

   第二个和第三个变量也比较简单,就是分别在构建期间安装期间不允许CMake给你加入相关RPATH

cmake .. -DCMAKE_SKIP_BUILD_RPATH=TRUE 或CMakelist中加set(CMAKE_SKIP_BUILD_RPATH=TRUE)
cmake .. -DCMAKE_SKIP_INSTALL_RPATH=TRUE 或CMakelist中加set(CMAKE_SKIP_INSTALL_RPATH=TRUE)

当然了,如果你之后想要追加RPATH,只需要对这三个变量设置成FALSE就可以了。

再谈CMake与RPATH()https://www.cnblogs.com/rickyk/p/3884257.html再谈CMake与RPATH()
 

CMake为了方便用户的安装,默认在make install之后会自动remove删除掉相关的RPATH,如果我们在运行环境上有个目录:${CMAKE_INSTALL_PREFIX}/lib,我们希望执行make install 之后,RPATH可以自动添加它,我们就可以这么写

set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib)

需要注意的是,这个变量是全局变量,意味着你所有的target的RPATH都会在install的时候被写成这个

有没有简单的针对某个target呢,聪明的你应该已经想到了
set_target_properties(myexe PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")

这样就可以保证只针对当前的target进行make install的时候RPATH的写入了。

(扩展:已经生成的可执行文件的RPATH可以用patchelf  工具修改https://blog.csdn.net/bandaoyu/article/details/113181179

#指定链接时动态库的路径

###################################################################################################################
set(OPENCV_DYNAMIC_LIBS  "${CMAKE_CURRENT_SOURCE_DIR}/../lib")       # 动态 opencv native 库路径
add_library(libopencv_core SHARED IMPORTED )
set_target_properties(libopencv_core PROPERTIES   IMPORTED_LOCATION "${OPENCV_DYNAMIC_LIBS}/libopencv_core.so")
add_library(libopencv_highgui SHARED IMPORTED )
set_target_properties(libopencv_highgui PROPERTIES   IMPORTED_LOCATION "${OPENCV_DYNAMIC_LIBS}/libopencv_highgui.so")
add_library(libopencv_imgproc SHARED IMPORTED )
set_target_properties(libopencv_imgproc PROPERTIES   IMPORTED_LOCATION "${OPENCV_DYNAMIC_LIBS}/libopencv_imgproc.so")
add_library(libopencv_video SHARED IMPORTED )
set_target_properties(libopencv_video PROPERTIES   IMPORTED_LOCATION "${OPENCV_DYNAMIC_LIBS}/libopencv_video.so")

set(mOpenCV_LIBS  libopencv_core libopencv_highgui  libopencv_imgproc libopencv_video)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp ")
target_link_libraries(main ${mOpenCV_LIBS} )

类似上面,添加好所有需要的库,链接的时候就能找得到自定义路径lib下面的库了

linux cmake分别指定编译/运行时动态库链接路径:https://blog.csdn.net/JCYAO_/article/details/102519998

set_target_properties 设置目标属性语法是列出您要更改的所有文件,然后提供您接下来要设置的值。

set_target_properties:https://cmake.org/cmake/help/v3.0/command/set_target_properties.html?highlight=set_target_properties

cmake会先到*_DIR里指定.cmake文件里去找找不到了才会到/usr里去找

所以要小心cmake给你找的文件,如果在一台电脑里装了不止一个版本库(或:多个版本的源码编译后把库放到环境公共目录下)。

什么是RPATH?

程序运行时,搜索动态库的顺序(优先级)优先级是这样的:

1. RPATH ,编译链接时加入 -rpath 参数 指定的目录
2. LD_LIBRARY_PATH  这个环境变量指定的目录
3. /etc/ld.so.conf 配置文件。
4. /usr/lib 、 /lib 和 /usr/local/lib ,系统默认路径。

#其实在Linux环境下,使用动态链接的程序会先链接 ld.so 这个库(OS X上是 dyld),然后通过 ld.so 来查找链接其它的库。

Cmake和RPATH

部署程序到机器上后,程序依赖的动态库 机器上的系统内不一定会有,或者自带了的版本不对,所以一般会在程序文件夹内附带其依赖的链接库,所以最好还是把 RPATH 加上。

Cmake对RPATH提供了很多选项支持,我们一般只关注这几个变量就好了:

CMAKE_SKIP_BUILD_RPATH  (就是不在 可执行文件里面添加Cmake自认为的 RPATH)
CMAKE_BUILD_WITH_INSTALL_RPATH (build的时候连接动态库时去INSTALL_RPATH下去找
CMAKE_INSTALL_RPATH (上面的INSTALL_RPATH的值,就用这个啦设置
CMAKE_INSTALL_RPATH_USE_LINK_PATH (用LINK_PATH的值给INSTALL_RPATH

不使用Cmake默认RPATH设置,加上完整的RPATH

Cmake默认RPATH设置是这样的:

set(CMAKE_SKIP_BUILD_RPATH FALSE)            # FALSE-->设定编译时加上要RPATH
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)    # FALSE-->编译时RPATH不使用INSTALL_RPATH
set(CMAKE_INSTALL_RPATH "")                  # 设置INSTALL_RPATH为空
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) # FALSE-->INSTALL_RPATH不使用LINK_PATH,安装的执行文件不加上RPATH

Cmake在默认情况下, make install 会把安装的执行文件的 RPATH 删掉的(即执行文件搜索动态库是不是去RPATH 找),所以就会出现上面我执行安装好的执行文件报错的问题。

Cmake的默认设置我们肯定是不能使用的,我们需要安装的时候也要带上 RPATH 的设置。

set(INSTALL_LIB_DIR "${PROJECT_BINARY_DIR}/lib") # 假设安装目录在编译目录的lib子目录内
set(CMAKE_SKIP_BUILD_RPATH FALSE)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

# 确保链接库不在 系统默认安装的目录 上时更改到项目lib上

list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES ${CMAKE_INSTALL_RPATH} isSystemDir)
if("${isSystemDir}" STREQUAL "-1")
set(CMAKE_INSTALL_RPATH "${INSTALL_LIB_DIR}")
endif("${isSystemDir}" STREQUAL "-1")

cmake install 和打包设定

//执行make install的时候 把static_lib  放到bin

install(TARGETS  static_lib  DESTINATION  bin)

//也可以使用变量

install(TARGETS  static_lib  DESTINATION  ${your_path})

//还可以一次指定多个对象,并按类型指定放入的目的地

install(TARGETS static_lib shared_lib exe  //static_lib shared_lib exe 这个三个对象
            RUNTIME DESTINATION bin  //可执行文件放在bin目录
            LIBRARY DESTINATION lib     //共享库文件放在lib目录
            ARCHIVE DESTINATION lib)  //静态库文件放在lib目录

命令第一个参数TARGETS指定需要安装的构建目标的列表,可以是静态库文件、动态库文件、可执行文件;安装时常常按照文件类型安装到不同的子目录,比如库文件放在lib目录,可执行文件放在bin目录。

针对不同文件类型,比如(RUNTIMEARCHIVELIBRARYPUBLIC_HEADER),可以分开进行配置,比如分别指定安装路径(DESTINATION)、设置文件权限(PERMISSIONS);如果不是在某个类别下的单独配置,那么就是针对所有类型。

值得一提的是,ARCHIVE一般是指静态库,LIBRARY则是指共享库,在不同平台上,略有差异,实际应用感觉不符合预期时查看一下官方文档即可,问题不大。

更多详情见文章:

cmake应用:安装和打包:https://zhuanlan.zhihu.com/p/377131996

CMakeLists.txt单行注释和多行注释

单行注释:使用“#”多行注释:使用“#[[ ]]”以下图为例

在这里插入图片描述

target_link_libraries 中的PRIVATE, PUBLIC, INTERFACE 区别

库的依赖关系为:

app-->libbar.so-->libfoo.so

链接选项barapp
PRIVATE传入foo传入bar
INTERFACE不传入foo传入bar,传入foo
PUBLIC传入foo传入bar,传入foo

链接选项:PRIVATE,INTERFACE,PUBLIC :https://zhuanlan.zhihu.com/p/493493849

网上关于 target_link_libraries 中的 PRIVATE, PUBLIC , INTERFACE  的解释大部分是错的,而且不是一般的错,是胡说。 因为这三个属性在不同的命令中使用时意义不同,有很多是从target_include_libraries中抄来的。

以下的解释主要场景是linux中,windows不存在这种关系,无需考虑。

我们来解释下,假设我们有一个程序 A  , A调用库B, B调用库C. 

A -> B -> C

A link B时不管是private还是public都没关系,毕竟A不需要导出符号,也没有人以API方式调用它。

现在主要问题就是B这个库用private还是public.  C是动态库。

如果B是动态或静态库,C是动态库,这个问题就会有影响。同样,如果B、C同为静态库时也会有问题。 

B用private link C,  此时A link B,但是不知道B->C这层关系,可以正常link B. 运行时,A->B->C 时,B找不到C中的函数。linux下没有直接依赖关系,所有的B/C的依赖都会转到到A下,可以用LDD命令验证,此时A只依赖于B, 不见C, 当B中的函数调用C中的函数时,因为没有加载C, 所以报找不到符号错误。解决的办法就是在A link B时,同样也写上C. 但是因为private的原因,A是不知道C中的符号这事,只能强制 link C到A才能解决。

如果B link C时用public 指示, 当编译A时,就会检查到C中的符号没有实现,此时你就会知道要把C link到A来解决这个问题了。

其实private/public 解决的是指示问题,本质上可以使用public 来解决, 可以减少坑。

下面是target_link_libraries中的解释,不想看英文的,直接拉到最后。

Link Inheritance

Similarly, for any target, in the linking stage, we would need to decide, given the item to be linked, whether we have to put the item in the link dependencies, or the link interface, or both, in the compiled target. Here the link dependencies means the item has some implementations that the target would use, and it is linked to the item, so that whenever we call the functions or methods corresponding to those implementations it will always be mapped correctly to the implementations in item via the link, whereas the link interface means the target becomes an interface for linking the item for other targets which have dependencies on the target, and the target does not have to use item at all.

Link Type    Description
PUBLIC    All the objects following PUBLIC will be used for linking to the current target and providing the interface to the other targets that have dependencies on the current target.
PRIVATE    All the objects following PRIVATE will only be used for linking to the current target.
INTERFACE    All the objects following INTERFACE will only be used for providing the interface to the other targets that have dependencies on the current target.
For example, if the fruit library has the implementation of functions, such as size and color, and the apple library has a function apple_size which called the size from the fruit library and was PRIVATE linked with the fruit library. We could create an executable eat_apple that calls apple_size by PUBLIC or PRIVATE linking with the apple library. However, if we want to create an executable eat_apple that calls the size and color from the fruit library, only linking with the apple library will cause building error, since the fruit library was not part of the interface in the apple library, and is thus inaccessible to eat_apple. To make the apple library to inherit the size and color from the fruit library, we have to make the linking of the apple library to the the fruit library PUBLIC instead of PRIVATE.

下面用人话(汉语)翻译下:

PUBLIC    在public后面的库会被Link到你的target中,并且里面的符号也会被导出,提供给第三方使用。

PRIVATE  在private后面的库仅被link到你的target中,并且终结掉,第三方不能感知你调了啥库

INTERFACE   在interface后面引入的库不会被链接到你的target中,只会导出符号。

---- 更新----------

target_link_libraries 会在目标程序中生成rpath, 这点请注意 。

原文链接:https://blog.csdn.net/znsoft/article/details/119035578

posted on 2022-10-04 01:24  bdy  阅读(4000)  评论(0编辑  收藏  举报

导航