欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

CMakeLists.txt

程序员文章站 2024-02-19 19:11:40
...

引言

慢慢积累。

1.find_package()函数

Find_package()函数参考.

2.设置编译后的壳执行文件路径

源文件多了,编译会产生很多的其他文件,如果全部都放在同一文件夹里面看着不舒服。放个ORB-SLAM2的做参考:set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/Examples/RGB-D)

cmake_minimum_required(VERSION 2.8)
project(ORB_SLAM2)

IF(NOT CMAKE_BUILD_TYPE)
  SET(CMAKE_BUILD_TYPE Release)
ENDIF()

MESSAGE("Build type: " ${CMAKE_BUILD_TYPE})

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}  -Wall  -O3 -march=native ")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall   -O3 -march=native")

# Check C++11 or C++0x support
include(CheckCXXCompilerFlag)
CHECK_CXX_COMPILER_FLAG("-std=c++11" COMPILER_SUPPORTS_CXX11)
CHECK_CXX_COMPILER_FLAG("-std=c++0x" COMPILER_SUPPORTS_CXX0X)
if(COMPILER_SUPPORTS_CXX11)
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
   add_definitions(-DCOMPILEDWITHC11)
   message(STATUS "Using flag -std=c++11.")
elseif(COMPILER_SUPPORTS_CXX0X)
   set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x")
   add_definitions(-DCOMPILEDWITHC0X)
   message(STATUS "Using flag -std=c++0x.")
else()
   message(FATAL_ERROR "The compiler ${CMAKE_CXX_COMPILER} has no C++11 support. Please use a different C++ compiler.")
endif()

LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake_modules)

find_package(OpenCV 3.0 QUIET)
if(NOT OpenCV_FOUND)
   find_package(OpenCV 2.4.3 QUIET)
   if(NOT OpenCV_FOUND)
      message(FATAL_ERROR "OpenCV > 2.4.3 not found.")
   endif()
endif()

find_package(Eigen3 3.1.0 REQUIRED)
find_package(Pangolin REQUIRED)

include_directories(
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/include
${EIGEN3_INCLUDE_DIR}
${Pangolin_INCLUDE_DIRS}
)

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/lib)

add_library(${PROJECT_NAME} SHARED
src/System.cc
src/Tracking.cc
src/LocalMapping.cc
src/LoopClosing.cc
src/ORBextractor.cc
src/ORBmatcher.cc
src/FrameDrawer.cc
src/Converter.cc
src/MapPoint.cc
src/KeyFrame.cc
src/Map.cc
src/MapDrawer.cc
src/Optimizer.cc
src/PnPsolver.cc
src/Frame.cc
src/KeyFrameDatabase.cc
src/Sim3Solver.cc
src/Initializer.cc
src/Viewer.cc
)

target_link_libraries(${PROJECT_NAME}
${OpenCV_LIBS}
${EIGEN3_LIBS}
${Pangolin_LIBRARIES}
${PROJECT_SOURCE_DIR}/Thirdparty/DBoW2/lib/libDBoW2.so
${PROJECT_SOURCE_DIR}/Thirdparty/g2o/lib/libg2o.so
)

# Build examples

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/Examples/RGB-D)

add_executable(rgbd_tum
Examples/RGB-D/rgbd_tum.cc)
target_link_libraries(rgbd_tum ${PROJECT_NAME})

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/Examples/Stereo)

add_executable(stereo_kitti
Examples/Stereo/stereo_kitti.cc)
target_link_libraries(stereo_kitti ${PROJECT_NAME})

add_executable(stereo_euroc
Examples/Stereo/stereo_euroc.cc)
target_link_libraries(stereo_euroc ${PROJECT_NAME})


set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/Examples/Monocular)

add_executable(mono_tum
Examples/Monocular/mono_tum.cc)
target_link_libraries(mono_tum ${PROJECT_NAME})

add_executable(mono_kitti
Examples/Monocular/mono_kitti.cc)
target_link_libraries(mono_kitti ${PROJECT_NAME})

add_executable(mono_euroc
Examples/Monocular/mono_euroc.cc)
target_link_libraries(mono_euroc ${PROJECT_NAME})

3.CMake中的function和macro

此部分参考.CMake中函数:function, 和宏:macro。二者区别很小,函数有作用域的概念,适用范围更广。后面分享一个很实用的递归函数用于包含指定目录的所有子目录。

3.1.CMake中function和macro的区别

从其官方文档的描述并不会看出二者有什么大的区别,除了在function的定义中提到了Scope的概念。

以*上的一个例子来直观的了解一下二者的区别:

set(var "ABC")

macro(Moo arg)
  message("arg = ${arg}")
  set(arg "abc")
  message("# After change the value of arg.")
  message("arg = ${arg}")
endmacro()
message("=== Call macro ===")
Moo(${var})

function(Foo arg)
  message("arg = ${arg}")
  set(arg "abc")
  message("# After change the value of arg.")
  message("arg = ${arg}")
endfunction()
message("=== Call function ===")
Foo(${var})

输出结果是:

➜  /Users/sunyongjian1/codes/local_codes/cmake_test/build cmake ..
=== Call macro ===
arg = ABC
# After change the value of arg.
arg = ABC
=== Call function ===
arg = ABC
# After change the value of arg.
arg = abc

可以看到,Moo这个宏的表现与C语言中的宏类似,仅仅是做字符串的替换; Foo函数里arg则是被赋值为var的值,在Foo内部可以修改这个arg变量的值。

个人感觉, 对于CMake里的函数和宏的使用原则可以以C语言里函数和宏的使用原则来作为参考。下面看看function的常见用法。

3.2.function 的使用技巧

如何按引用来传递参数?(在function中修改外部作用域的值)?答:通过名字来传递变量。
例如:有一个var变量,在函数外部定义,要通过调用一个函数f1, 来修改var的值

set(var "abc")                      # 定义一个变量var,初值为abc

function(f1 arg)
    set(${arg} "ABC" PARENT_SCOPE)  # ${arg} == var, 于是相当于set(var "ABC" PARENT_SCOPE)
endfunction()

message("before calling f1, var = ${var}")
f1(var)                                     # 如果写成了f1(${var})会怎样?
message("after calling f1, var = ${var}")

结果:

➜  /Users/sunyongjian1/codes/local_codes/cmake_test/build cmake ..
before calling f1, var = abc
after calling f1, var = ABC

需要注意的两点:

  • 函数调用处用变量的名字var,而不是它的值${var}
  • 在函数内部,set的时候,要加上作用域PARENT_SCOPE.

试试写成f1(${var}):

set(var "abc")                      # 定义一个变量var,初值为abc

function(f1 arg)
    set(${arg} "ABC" PARENT_SCOPE)  # ${arg} == var, 于是相当于set(var "ABC" PARENT_SCOPE)
endfunction()

message("before calling f1, abc = ${abc}")
f1(${var})                                     
message("after calling f1, abc = ${abc}")

输出是:

➜  /Users/sunyongjian1/codes/local_codes/cmake_test/build cmake ..
before calling f1, abc = 
after calling f1, abc = ABC

如果写成了 f1(${var}), 那么先计算表达式${var}, 即相当于调用f1(abc), 调用结果是在函数的父作用域定义了一个abc变量.
其实在了解了参数展开之后,这个问题很显而易见,本质上就是调用了一个set(<var-name> <var-value> <var-scope>), 只不过如果需要通过函数来包装他的话就要注意传参传过来的东西是个变量名还是变量的值。

3.3.如何传递列表类型的参数?

如果要打印一个列表要怎么写?

set(arg hello world)

foreach(v ${arg})
    message(${v})
endforeach()

输出:

➜  /Users/sunyongjian1/codes/local_codes/cmake_test/build cmake ..
hello
world

在调试CMake脚本的时候,可能经常用到这种打印列表的代码,于是很自然的我需要写一个打印列表的函数:print_list
实现很简单,只要把上面那个foreach丢到一个函数体里面就好了

function(print_list arg)
    foreach(v ${arg})
        message(${v})
    endforeach()
endfunction()

现在来调用一下这个函数:

set(arg hello world) 

print_list(${arg})

输出结果:

➜  /Users/sunyongjian1/codes/local_codes/cmake_test/build cmake ..
hello

应该是hello world才对,怎么会只有一个hello ?这几乎是初学者必犯的错误,问题出在对print_list的调用方式上:
print_list(${arg}) 展开来看就是 print_list(hello world), 因此,传递给print_list的第一个参数只有hello。正确的调用方式应该是下面这样,使用双引号把参数括起来:

print_list("${arg}")

输出:

➜  /Users/sunyongjian1/codes/local_codes/cmake_test/build cmake ..
hello
world

函数里几个有用的隐含参数:
name description

  • ARGC 函数实参的个数
  • ARGV 所有实参列表
  • ARGN 所有额外实参列表, 即ARGV去掉函数声明时显示指定的实参,剩余的实参
  • ARGV0 函数第1个实参
  • ARGV1 函数第2个实参
  • ARGV2 函数第3个实参

依次类推,使用上面表格里的几个隐含参数,通过下面这个例子可以更好的说明上面两种传递参数的方式,函数内部发生了什么。

function(print_list arg)
    message("======= args count : ${ARGC} ======= ")    # 实际实参个数

    message("======= all args ======= ")                # 打印所有参数
    foreach(v IN LISTS ARGV)
        message(${v})
    endforeach()


    message("======= all extra args ======= ")          # 打印所有额外参数
    foreach(v IN LISTS ARGN)
        message(${v})
    endforeach()

    message("======= print content of ARGV0 ======= ")  # 打印第一个参数里的所有内容
    foreach(v IN LISTS ARGV0)
        message(${v})
    endforeach()
endfunction()

set(arg hello world) 

message("------------ calling with qutoes -----------")     # 使用引号来调用
print_list("${arg}")

message("------------ calling without qutoes -----------")  # 不使用引号调用
print_list(${arg})

输出:

➜  /Users/sunyongjian1/codes/local_codes/cmake_test/build cmake ..
------------ calling with qutoes -----------
======= args count : 1 ======= 
======= all args ======= 
hello
world
======= all extra args ======= 
======= print content of ARGV0 ======= 
hello
world
------------ calling without qutoes -----------
======= args count : 2 ======= 
======= all args ======= 
hello
world
======= all extra args ======= 
world
======= print content of ARGV0 ======= 
hello

从两个输出结果里可以看到:
1.使用引号包裹参数时
参数个数:1, 即hello world 额外参数个数: 0 打印第一个参数的内容 = 要打印的列表内容

2.不使用引号包裹参数时
参数个数:2, 分别是 hello 和 world 额外参数个数: 1, world 打印第一个参数的内容 = hello
在不使用括号包裹的情况下,因为函数只需要一个参数,列表里除了第一个元素的其它元素被当做额外的参数传给函数了,当我打印第一个参数的时候,就仅仅把列表的第一个元素打印出来了。

通过这个例子可以看到,在不使用括号来包裹列表类型的参数作为函数实参时,列表参数内部的空格(或者分号)会使得这个列表的内容被当做多个参数传递给函数。