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 功能。
posted on 2020-09-13 07:00  莫水千流  阅读(388)  评论(0编辑  收藏  举报