本文内容1-5来自于,https://zhuanlan.zhihu.com/p/267111099
0 Q&A
#新
set(TARGET_NAME 00)
find_package(Eigen3 REQUIRED)
add_executable (${TARGET_NAME} main.cpp)
target_link_libraries(${TARGET_NAME} PRIVATE Eigen3::Eigen)
#原来
cmake_minimum_required (VERSION 2.8.11)
project (Transformation)
find_package(Eigen3 REQUIRED)
include_directories(EIGEN3_INCLUDE_DIR)
add_executable (Transformation main.cpp)
新编译过程
使用 cmake 进行编译。
首先,编写作业的程序 main.cpp。
然后, 在 main.cpp 所在目录下,打开终端 (命令行),依次输入:
• mkdir build: 创建名为 build 的文件夹。
• cd build: 移动到 build 文件夹下。
• cmake ..: 注意其中’..’ 表示上一级目录,若为’.’ 则表示当前目录。
• make: 编译程序,错误提示会显示在终端中。
• ./00:若上一步无错误,则可运行程序 (这里的 00
为可执行文件名,可参照 CMakeLists.txt 中修改)。
原来编译过程
使用 cmake 进行编译。
首先,编写作业的程序 main.cpp。
然后, 在 main.cpp 所在目录下,打开终端 (命令行),依次输入:
• mkdir build: 创建名为 build 的文件夹。
• cd build: 移动到 build 文件夹下。
• cmake ..: 注意其中’..’ 表示上一级目录,若为’.’ 则表示当前目录。
• make: 编译程序,错误提示会显示在终端中。
• ./Transformation:若上一步无错误,则可运行程序 (这里的 Transformation
为可执行文件名,可参照 CMakeLists.txt 中修改)。
编译的时候遇到了cmake 成功后, make 出现 No such file or directory 问题解决
参考解决方法
出现这种问题是说明没有找到相关的文件,例如:
<gio/gio.h> No such file or directory
查看CMakeLists.txt, 发现包含的该头文件确实没有 include 进去
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/_dev-fs/usr/include/libmm-glib) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/_dev-fs/usr/include/ModemManager) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/_dev-fs/usr/include/libnm)
后加入
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/_dev-fs/usr/include/gio)
这个文件夹是确实存在的,里面的gio.h也存在,但是还是报同样的问题,弄了半天都卡在这里了,结果最后发现应该是路径上的问题,
应为在预处理依赖头文件的过程中是按照你加入的路径去找的(<gio/gio.h> 前面有个文件夹gio),也就是 include_directories
进去目录是作为寻找的根目录,也就是起始目录,
结果找到的是 .../_dev-fs/usr/include/gio/gio/gio.h
, 这个路径当然不存在,所有应该是加入
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/_dev-fs/usr/include)
即路径是 .../_dev-fs/usr/include/gio/gio.h
这样就可以找到了,问题解决!!!!!!!!!!!!!!!!!
1 概述
Cmake全称Cross Platform Make,起初为了跨平台需求,而后不断完善并广泛使用。优势是跨平台, 支持Linux, Mac和Windows等不同操作系统。
编译过程
CMake是一种跨平台编译工具,CMake主要是编写CMakeLists.txt文件,然后通过cmake命令将CMakeLists.txt文件转化为make所需要的Makefile文件,最后用make命令编译源码生成可执行程序或者库文件。实际项目中的C/C++文件不计其数、文件放置的位置也不同,Makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作。实现自动化的编译
2 CMake与其他编译工具的对比
- gcc/g++是很常见的编译工具,是由GNU开发的编程语言译器, 主要用于C/C++等语言的开发,当项目简单,可以用gcc/g++编译目标和项目,但比较复杂时, 只用gcc组织编译架构会变得极其困难。
- Makefile是有条理的gcc编译命令的文件,利用make工具来执行Makefile文件的编译指令,当程序简单时, 可以手写Makefile,当程序复杂时, 一般利用CMake来自动生成Makefile。
- 因而CMake类似Make工具功能,用来“读取”并执行CMakeLists.txt文件的语句, 最终生成Makefile文件。CMake语言开发相对简单,易于理解。
3 CMake的常用指令
CMake类似Make工具功能,用来读取并执行CMakeLists.txt,这个文件是cmake的构建定义文件,文件名是大小写相关的,如果工程存在多个目录,需要确保每个要管理的目录都存在一个CMakeLists.txt
CMakeLists.txt有一些的常用指令,笔者总结了自己项目过程里最常用的指令,不常用的没有列出。主要${}
是引用变量,这是cmake的变量应用方式,但是,有一些例外,比如在IF
控制语句,变量是直接使用变量名引用,而不需要${}
。如果使用了${}
去应用变量,其实IF会去判断名为${}
所代表的值的变量,那当然是不存在的了。
-
project(projectname [CXX] [C] [Java])
用这个指令定义工程名称,并可指定工程支持的语言,支持的语言列表是可以忽略的。 -
set(<variable> <value>)
set指令可以用来显式的定义变量。 -
message([SEND_ERROR | STATUS | FATAL_ERROR] "message to display"...)
这个指令用于向终端输出用户定义的信息,包含了三种类型:
SEND_ERROR,产生错误,生成过程被跳过。
SATUS,输出前缀为-- 的信息(以简洁的方式显示用户感兴趣的信息)。
FATAL_ERROR,立即终止所有cmake过程。
如message(STATUS "PROJECT_NAME: " ${PROJECT_NAME})
最终能看到-- PROJECT_NAME: demo
这样的提示信息。也可以直接这样:message("OpenCV: ${OpenCV_LIBS}")
就直接显示信息。 -
find_package(<PackageName> [version] [EXACT] [QUIET] [MODULE] [REQUIRED [[COMPONENTS] [components...]] [OPTIONAL_COMPONENTS components...])
主要是寻找和加载外部项目。如果PackageName找到了,PackageName-found会显出,当没有找到时,默认显示PackageName-not found。通过模式的选择,可以处理在没有找到包时的解决方案。QUIET:不显示有用信息,REQUIRED:报错。
如找oepncv库可以这样用:find_package(OpenCV REQUIRED) -
find_path (<VAR> name0|NAMES name1 [path1 path2 ...])
用以寻找包含着name1文件的目录,如果找到了结果存储在VAR,没有找到结果结果是VAR-not found。成功时,变量被清除find_path再次搜索,没有成功,fin_path再次以相同的变量被调用时搜索。 -
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
这个指令用于向当前工程添加将被编译的存放源文件的子目录,并可以指定中间二进制和目标二进制存放的位置。指明CMakeLists.txt所在目录下包含了一个子目录source_dir,这样source_dir下的源文件和CMakeLists.txt等也会被处理。EXCLUDE_FROM_ALL参数的含义是将这个目录从编译过程中排除,比如,工程的example,可能就需要工程构建完成后,再进入example目录单独进行构建。 -
INSTALL
用于定义安装规则,安装的内容可以包括目标二进制、动态库、静态库以及文件、目录、脚本等。 常用的如OpenCV一般情况下安装到系统目录,即/usr/lib, /usr/bin和/usr/include [Ubuntu系统]
4 与静态库和共享库相关的指令
静态库:链接阶段, 库中目标文件所含的所有将被程序使用的函数的机器码,
被copy到最终的可执行文件中,因此对应的链接方式称为静态链接。静态库对函数库的链接是放在编译时期完成的,程序在运行时与函数库再无瓜葛,
移植方便,所以运行效率相对快,但缺点是占用磁盘和内存空间,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
动态库(共享库):程序编译时并不会被连接到目标代码中, 而是在程序运行时才被载入。所以静态库是牺牲了空间效率, 换取了时间效率;共享库是牺牲了时间效率, 换取了空间效率;
首先看看如何建立静态库和共享库。
-
add_library (name dir)
用在目录dir下的源文件生成一个名为name的静态链接库:libname.a (win下是产生name.lib)注意会自动在名字前加lib。 -
add_library(libname SHARED source1 source2 ... sourceN)
生成一个名为name的动态库(扩展名为.so,win下是.dll)
接下来看看如何使用外部共享库和头文件。
-
add_executable(hello ${SRC_LIST})
定义了这个工程会生成一个文件名为hello的可执行文件,相关的源文件是SRC_LIST中定义的源文件列表, 例如add_executable(hello main.cpp)
。一般都要包含main.hpp头文件。 -
target_link_libraries(exec library1<debug | optimized> library2...)
表示为可执行程序exec添加需要链接的静态库或共享库。如TARGET_LINK_LIBRARIES(main libhello.so)
再如TARGET_LINK_LIBRARIES(main libhello.a)
。 -
include_directories([AFTER|BEFORE] [SYSTEM] dir1 dir2 ...)
这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的后面。 -
target_include_directories(<target>[SYSTEM][BEFORE]<INTERFACE|PUBLIC|PRIVATE> [items])
设置include文件查找的目录,具体包含头文件应用形式,安装位置等。 -
ADD_CUSTOM_COMMAND/TARGET
[COMMAND] : 为工程添加一条自定义的构建规则。[TARGET] : 用于给指定名称的目标执行指定的命令,该目标没有输出文件,并始终被构建。
如在cmakelist.txt里写如下函数就自定义了一个copy共享库的自定义命令。
function(cpy_dlls_to_target targe)
foreach (DFILE ${ARGN})
message("dll: " ${DFILE})
add_custom_command(TARGET ${targe} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${DFILE}"
$<TARGET_FILE_DIR:${targe}>)
endforeach ()
endfunction()
5 简单的案例
生成的库文件和可执行文件可以放在不同的文件夹下面,方便管理:
#把生成的全部的dll和exe输出到某个文件夹下
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#把生成的全部的lib文件输出到某个文件夹下
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
抓取某个文件夹下面的全部cpp文件可以这样:
#抓取全部的cpp文件
file(GLOB SOURCES ${PROJECT_SOURCE_DIR}/Xxxx/*.cpp)
这里举以调opencv库来实现自己的程序main.cpp为例串联下常用指令。那么项目目录下CMakeLists.txt这样写。
cmake_minimum_required(VERSION 3.6)
set(PROJECT_NAME "demo")
project(${PROJECT_NAME})
set(CMAKE_CXX_STANDARD 11)
set(OpenCV_INCLUDE_DIR "D:/opencv3.3/opencv/build/include/")
set(OpenCV_LIBS "D:/opencv3.3/opencv/build/x64/vc14/lib/opencv_world330d.lib")
set(OpenCV_DLL "D:/opencv3.3/opencv/build/x64/vc14/bin/opencv_world330d.dll")
add_executable(${PROJECT_NAME} main.cpp)
# 链接OpenCV库文件
target_include_directories(${PROJECT_NAME} PUBLIC ${OpenCV_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS})
当然假如说自己写的源代码太多,就不能都以add_executable(${PROJECT_NAME} main.cpp xxx.cpp)
这样的方式了,而是应该把自己实现的某些程序当成库。这里假设自己实现的库名字叫myfunctions,创了一个名为myfunctions的文件夹,此时有好几个用于实现某种想要功能的源代码在这个文件夹下,我们可以在myfunctions下也写一个CMakeLists.txt,
生成一个自建的库:add_library(myfunctions xxx.cpp xxxx.cpp)
当然自建库相当于就是开发一个sdk,自然可以使用外部的库如opencv等库,CMakeLists.txt可以写很多功能。完成了自建库后,在项目目录下的CMakeLists.txt里调用自建库。
经过修改,再加一个自动复制opencv的dll到clion创建项目的所需目录下(因为如果使用clion开发,必须要有dll文件在文件夹cmake-build-release或cmake-build-debug下)。假如自己的项目有一个文件夹叫src,存放了许多自己的项目代码,那么就需要aux_source_directory(./src DIR_my),然后add_executable(${PROJECT_NAME} ${DIR_SRCS})
。完善的项目目录下的CMakeLists.txt这么写:
cmake_minimum_required(VERSION 3.6)
set(PROJECT_NAME "demo")
project(${PROJECT_NAME})
MESSAGE(STATUS "PROJECT_NAME: " ${PROJECT_NAME})
set(CMAKE_CXX_STANDARD 11)
# Check platforms
if (CMAKE_HOST_WIN32)
set(WINDOWS 1)
MESSAGE(STATUS "PLATFORM: WINDOWS")
elseif (CMAKE_HOST_APPLE)
set(MACOS 1)
MESSAGE(STATUS "PLATFORM: MACOS")
elseif (CMAKE_HOST_UNIX)
set(LINUX 1)
MESSAGE(STATUS "PLATFORM: LINUX")
endif ()
set(OpenCV_INCLUDE_DIR "D:/opencv3.3/opencv/build/include/")
set(OpenCV_LIBS "D:/opencv3.3/opencv/build/x64/vc14/lib/opencv_world330d.lib")
set(OpenCV_DLL "D:/opencv3.3/opencv/build/x64/vc14/bin/opencv_world330d.dll")
function(cpy_dlls_to_target targe)
foreach (DFILE ${ARGN})
message("dll: " ${DFILE})
add_custom_command(TARGET ${targe} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${DFILE}"
$<TARGET_FILE_DIR:${targe}>)
endforeach ()
endfunction()
aux_source_directory(./src DIR_my)
include_directories ("${PROJECT_SOURCE_DIR}/myfunctions")
add_subdirectory (myfunctions)
set (EXTRA_LIBS ${EXTRA_LIBS} myfunctions)
if (WINDOWS)
cpy_dlls_to_target(${PROJECT_NAME} "${OpenCV_DLL}")
endif (WINDOWS)
add_executable(${PROJECT_NAME} ${DIR_SRCS})
# 链接库文件
target_include_directories(${PROJECT_NAME} PUBLIC ${OpenCV_INCLUDE_DIR})
target_link_libraries(${PROJECT_NAME} ${OpenCV_LIBS} ${EXTRA_LIBS})
自动复制也可以这么写:
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${REQUIRED_DLL} $<TARGET_FILE_DIR:${PROJECT_NAME}>)
再放一个例子:
cmake_minimum_required(VERSION 3.15)
set(PROJECT_NAME "undistort")
project(${PROJECT_NAME})
set(CMAKE_BUILD_TYPE "Release")
set(CMAKE_CXX_STANDARD 14)
#把生成的全部的dll和exe输出到某个文件夹下
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/bin)
#把生成的全部的lib文件输出到某个文件夹下
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# OpenMP
FIND_PACKAGE(OpenMP)
IF(OPENMP_FOUND)
OPTION(WITH_OPENMP "Whether to use parallel processing capabilities of OPENMP. ON/OFF" ON)
ENDIF(OPENMP_FOUND)
IF(OPENMP_FOUND AND WITH_OPENMP)
MESSAGE(STATUS "With OpenMP ")
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMAX_OPENMP_NUM_THREADS=${PROCESSOR_COUNT} -DOPENMP_NUM_THREADS=${PROCESSOR_COUNT} ${OpenMP_CXX_FLAGS} -DOPENMP")
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DMAX_OPENMP_NUM_THREADS=${PROCESSOR_COUNT} -DOPENMP_NUM_THREADS=${PROCESSOR_COUNT} ${OpenMP_CXX_FLAGS} ${OpenMP_C_FLAGS} -DOPENMP")
ELSE(OPENMP_FOUND AND WITH_OPENMP)
MESSAGE(STATUS "Without OpenMP")
SET (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DMAX_OPENMP_NUM_THREADS=1 -DOPENMP_NUM_THREADS=1")
SET (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DMAX_OPENMP_NUM_THREADS=1 -DOPENMP_NUM_THREADS=1")
ENDIF(OPENMP_FOUND AND WITH_OPENMP)
#头文件载入
include_directories(
"${PROJECT_SOURCE_DIR}/libpng1637_x64/include"
"D:/opencv/opencv3.4.5/opencv/build/include"
)
#lib库的载入
link_directories(
"D:/opencv/opencv3.4.5/opencv/build/x64/vc15/lib"
"${PROJECT_SOURCE_DIR}/libpng1637_x64/library"
)
#抓取全部的cpp文件
file(GLOB SOURCES ${PROJECT_SOURCE_DIR}/ldm_pq2p_io/*.cpp)
#生成可执行文件
add_executable(${PROJECT_NAME} ${SOURCES})
#链接lib静态库
target_link_libraries(${PROJECT_NAME} opencv_world345 libpng16 zlib)
#载入必要的dll库
list(APPEND REQUIRED_DLL "D:/opencv/opencv3.4.5/opencv/build/x64/vc15/bin/opencv_world345.dll")
list(APPEND REQUIRED_DLL "${PROJECT_SOURCE_DIR}/libpng1637_x64/library/libpng16.dll")
#将dll复制进去
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${REQUIRED_DLL}
$<TARGET_FILE_DIR:${PROJECT_NAME}>)
注意事项:
在linux下编译cmakelist,只需要工程目录下新建build目录,然后cmake …,再make就行了(当然make -j8是多线程make),但注意相比windows,linux下编译需要加如下行:
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3")
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
前两行是编译选项,当然如果用编译器编译,就不用加这两行,否则不加这两行速度会变慢,第三行是为了与位置无关!否则别人调用你的sdk就会报错啦(windows不用,因为是手动copy dll文件的)
6 参考
Cmakelist编写小记
《CMake实践》笔记三:构建静态库(.a) 与 动态库(.so) 及 如何使用外部共享库和头文件
C++静态库与动态库
CMakeList语法知识