cmake函数、宏和模块
cmake函数function和宏定义macro在某种程度上来说是一样的,都是创建一段有名字的代码稍后可以调用,还可以传参数。
他们的定义如下:
macro定义:
macro(<name> [arg1 [arg2 [arg3 ...]]])
...
endmacro([name])
function定义:
function(<name> [arg1 [arg2 [arg3 ...]]])
...
endfunction([name])
函数和宏的默认内部变量
变量 | 说明 |
---|---|
ARGV# | ARGV0为第一个参数,ARGV1为第二个参数,依次类推 |
ARGV | 定义宏(函数)时参数为2个,实际传了4个,则ARGV代表实际传入的两个 |
ARGN | 定义宏(函数)时参数为2个,实际传了4个,则ARGN代表剩下的两个 |
ARGC | 实际传入的参数的个数 |
从定义上看他们貌似一模一样,宏和函数确实差不多,宏跟C语言中的宏概念不一样,不过还是有一点区别
相同点
调用方式一模一样,都是name(arg1....)形式调用,都是要先声明在调用,实际传入参数个数可以大于定义的参数个数
使用示例:
在sample9创建macro_function.cmake,内容如下:
# 定义函数 Function(myfunction ag1 ag2 ag3) message(STATUS "function ag is " ${ag1}) message(STATUS "function ag is " ${ag2}) message(STATUS "function ag is " ${ag3}) endfunction(myfunction) # 定义宏 macro(mymacro ag1 ag2 ag3) message(STATUS "macro ag is " ${ag1}) message(STATUS "macro ag is " ${ag2}) message(STATUS "macro ag is " ${ag3}) endmacro(mymacro) # 调用函数 myfunction(1 2 3 4 5) message(STATUS "\n") # 调用宏 mymacro(1 2 3 4 5)
输出如下:
-- function ag is 1
-- function ag is 2
-- function ag is 3
--
-- macro ag is 1
-- macro ag is 2
-- macro ag is 3
不同点
宏的ARGN、ARGV等内部变量不能直接在if语句和foreach(..IN LISTS..)语句中使用。其它一样
示例如下:
macro(_bar)
message(STATUS "this is in macro bar " ${ARGN})
set(aa ${ARGV0})
if(ARGV0)
message(STATUS "this is in a 1")
endif()
if(aa)
message(STATUS "this is in a 2 " ${aa})
endif()
set(list_var ${ARGN})
foreach(arg IN LISTS ARGN)
message(STATUS "this is in macro 2 ${arg}")
endforeach()
foreach(arg ${ARGN})
message(STATUS "this is in macro 3 ${arg}")
endforeach()
foreach(arg IN LISTS list_var)
message(STATUS "this is in macro 4 ${arg}")
endforeach()
endmacro(_bar)
message(STATUS "\n")
_bar(a b c)
function(_func)
message(STATUS "this is in func __func " ${ARGN})
set(aa ${ARGV0})
if(ARGV0)
message(STATUS "this is in a 1 " ${ARGV0})
endif()
if(aa)
message(STATUS "this is in a 2 " ${aa})
endif()
set(list_var ${ARGN})
foreach(arg IN LISTS ARGN)
message(STATUS "this is in __func 2 ${arg}")
endforeach()
foreach(arg IN LISTS list_var)
message(STATUS "this is in __func 3 ${arg}")
endforeach()
endfunction(_func)
message(STATUS "\n")
_func(d e f)
macro(_bar)
message(STATUS "this is in macro bar " ${ARGN})
set(aa ${ARGV0})
if(ARGV0)
message(STATUS "this is in a 1")
endif()
if(aa)
message(STATUS "this is in a 2 " ${aa})
endif()
set(list_var ${ARGN})
foreach(arg IN LISTS ARGN)
message(STATUS "this is in macro 2 ${arg}")
endforeach()
foreach(arg ${ARGN})
message(STATUS "this is in macro 3 ${arg}")
endforeach()
foreach(arg IN LISTS list_var)
message(STATUS "this is in macro 4 ${arg}")
endforeach()
endmacro(_bar)
message(STATUS "\n")
_bar(a b c)
function(_func)
message(STATUS "this is in func __func " ${ARGN})
set(aa ${ARGV0})
if(ARGV0)
message(STATUS "this is in a 1 " ${ARGV0})
endif()
if(aa)
message(STATUS "this is in a 2 " ${aa})
endif()
set(list_var ${ARGN})
foreach(arg IN LISTS ARGN)
message(STATUS "this is in __func 2 ${arg}")
endforeach()
foreach(arg IN LISTS list_var)
message(STATUS "this is in __func 3 ${arg}")
endforeach()
endfunction(_func)
message(STATUS "\n")
_func(d e f)
输出:
-- this is in macro bar abc
-- this is in a 2 a
-- this is in macro 3 a
-- this is in macro 3 b
-- this is in macro 3 c
-- this is in macro 4 a
-- this is in macro 4 b
-- this is in macro 4 c
--
-- this is in func __func def
-- this is in a 1 d
-- this is in a 2 d
-- this is in __func 2 d
-- this is in __func 2 e
-- this is in __func 2 f
-- this is in __func 3 d
-- this is in __func 3 e
-- this is in __func 3 f
模块
cmake能够识别CMakeLists.txt文件和xxx.cmake结尾的文件,模块就是以xxx.cmake结尾的文件,可以理解为,将一些通用的函数功能封装到到一个指定的文件中,然后通过include(xxx.cmake)方式引用,这样可以达到代码复用的目的。模块既可以被CMakeLists.txt引用,也可以被其它模块引用
cmake系统本身内置了一些预定义的模块可以供我们使用,比如FindCURL模块,它的使用方式是通过FIND_PACKAGE指令来完成的,请看如下例子:
创建sample10,建立src/main.cpp文件,内容如下:
#include <curl/curl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> FILE *fp; int write_data(void *ptr, size_t size, size_t nmemb, void *stream) { int written = fwrite(ptr, size, nmemb, (FILE *)fp); return written; } int main() { const char * path = "curl-test"; const char * mode = "w"; fp = fopen(path,mode); curl_global_init(CURL_GLOBAL_ALL); CURLcode res; CURL *curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, "http://www.baidu.com"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1); res = curl_easy_perform(curl); curl_easy_cleanup(curl); return 0; }
这段代码的作用是通过curl [取回www.baidu.com]的首页并写入当前目录下的curl-test 文件中。
创建工程根目录的CMakeLists.txt文件,内容如下:
# CMake 最低版本号要求 cmake_minimum_required (VERSION 2.8) if(POLICY CMP0042) cmake_policy(SET CMP0042 NEW) # CMake 3.0+ (2.8.12): MacOS "@rpath" in target's install name endif() # 项目工程名 project (sample10) message(STATUS "root This is BINARY dir " ${PROJECT_BINARY_DIR}) message(STATUS "root This is SOURCE dir " ${PROJECT_SOURCE_DIR}) # 添加子目录 ADD_SUBDIRECTORY(src)
创建src/CMakeLists.txt,内容如下:
# 打印信息 message(STATUS "src This is BINARY dir " ${PROJECT_BINARY_DIR}) message(STATUS "src This is SOURCE dir " ${PROJECT_SOURCE_DIR}) set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) # 定义工程根目录; CMAKE_SOURCE_DIR为内建变量,表示工程根目录的CMakeLists.txt文件路径 SET(ROOT_DIR ${CMAKE_SOURCE_DIR}) # 构建可执行程序 ADD_EXECUTABLE(sample10 main.cpp) # 查找指定的库 FIND_PACKAGE(CURL) IF(CURL_FOUND) MESSAGE(STATUS ”CURL library ${CURL_INCLUDE_DIR}”) MESSAGE(STATUS ”CURL library ${CURL_LIBRARY}”) INCLUDE_DIRECTORIES(${CURL_INCLUDE_DIR}) TARGET_LINK_LIBRARIES(sample10 ${CURL_LIBRARY}) ELSE(CURL_FOUND) MESSAGE(FATAL_ERROR ”CURL library not found”) ENDIF(CURL_FOUND)
在build文件夹内构建工程 cmake .. 然后执行 make,执行bin/sample10,就模拟了一次curl操作,将百度的首页内容写入到curl-text文件中了
cmake中关于内置Find.cmake模块的使用语法:
对于系统预定义的Find.cmake 模块,使用方法一般如上例所示:每一个模块都会定义以下几个变量
- _FOUND
- _INCLUDE_DIR or _INCLUDES
- _LIBRARY or _LIBRARIES
比如上面是要查找CURL库的头文件以及库文件路径,对应的变量就是CURL_FOUND、CURL_INCLUDE_DIR、CURL_INCLUDE_DIR
通过_FOUND 来判断模块是否找到,如果_FOUND 为真,则将_INCLUDE_DIR 加入INCLUDE_DIRECTORIES,将_LIBRARY 加入TARGET_LINK_LIBRARIES 中。
看如下一段代码,通过_FOUND 来控制工程特性:
SET(mySources viewer.c) SET(optionalSources) SET(optionalLibs) FIND_PACKAGE(JPEG) IF(JPEG_FOUND) SET(optionalSources ${optionalSources} jpegview.c) INCLUDE_DIRECTORIES( ${JPEG_INCLUDE_DIR} ) SET(optionalLibs ${optionalLibs} ${JPEG_LIBRARIES} ) ADD_DEFINITIONS(-DENABLE_JPEG_SUPPORT) ENDIF(JPEG_FOUND) ADD_EXECUTABLE(viewer ${mySources} ${optionalSources} ) TARGET_LINK_LIBRARIES(viewer ${optionalLibs}
通过判断系统是否提供了JPEG 库来决定程序是否支持JPEG 功能。