1.0 CMakeLists.txt注解

cmake是什么

cmake是一个生成工程文件的工具,生成的工程文件可以在各个平台上用开发工具来第二次编译。也就是说cmake并不是直接的编译工具,它只能生成对应平台的工程文件,比如:Windows上的Visual studio的sln,Linux上的makefile等。作为一个中间层的工具,cmake集成了很多内置的命令和变量,这也是我们在阅读cmake源码时候应该注意的。
cmake的源码文件是CMakeLists.txt文件,在下级目录中也有需要生成成makefile的情况下,需要在每个目录中添加CMakeLists.txt文件,在上级中用include添加即可。

本文的目标

本文从实用度的角度解析libevent中CMakeLists.txt所做的内容。注解与libevent源码相关度最大的cmake代码。在【cmake源码解读】中我将会从第一句开始解读。

libevent中cmake使用

为了避免cmake中间文件污染要生成makefile的源码文件目录,我们需要新建一个build目录,之后在build目录中生成makefile和编译

cd build 
cmake .. -CMAKE_BUILD_TYPE=Release|Debug
make && make install

关于cmake的使用在很多项目中都像上面一样,cmake ..代表要解释的CMakeLists.txt在哪个目录。

cmake源码解读
  1. cmake_minimum_required(VERSION 3.1.2 FATAL_ERROR)
    约定使用cmake生成工程文件的最小的版本,如果没有满足,直接FATAL退出

  2. set(CMAKE_BUILD_TYPE Release CACHE STRING "Set build type to Debug or Release (default Release)" FORCE)

设置默认的CMAKE_BUILD_TYPE,CMAKE_BUILD_TYPE也就是发布版和调试版

  1. project(libevent C)
    设置project的名字,cmake的一些命令比较特别,后面可以带多个参数

  2. list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
    将cmake/目录下的文件名全部设置到CMAKE_MODULE_PATH中。cmake/目录下有一些宏函数和函数,在下面的include中会include进去

include(CheckFileOffsetBits) 
include(Macros) 
include(CheckVariableExists) 
include(CheckSymbolExists) 
include(CheckStructHasMember) 
include(CheckCSourceCompiles) 
include(CheckPrototypeDefinition) 
include(CheckFunctionKeywords) 
include(CheckConstExists)
include(AddCompilerFlags) 
include(VersionViaGit)

这里会include本地的宏函数和函数,如果本地找不到,那就是cmake系统内置的宏函数和函数

  1. option(EVENT__DISABLE_DEBUG_MODE "Define if libevent should build without support for a debug mode" OFF)

这种是设置某个内部变量的方式,如果cmake源码中需要根据某个内部变量来改变逻辑,就可以用这个选项,最后一个参数为OFF/ON,表示是否默认关闭开启

if (NOT DEFINED CMAKE_RUNTIME_OUTPUT_DIRECTORY) 
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY 	${PROJECT_BINARY_DIR}/bin) 
endif() 
if (NOT DEFINED CMAKE_LIBRARY_OUTPUT_DIRECTORY) 
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) 
endif() 
if (NOT DEFINED CMAKE_ARCHIVE_OUTPUT_DIRECTORY) 
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib) 
endif()

设置输出文件路径

# Check if header files exist list(APPEND FILES_TO_CHECK fcntl.h
    inttypes.h
    memory.h
    signal.h
    stdarg.h
    stddef.h
    stdint.h
    stdlib.h
    string.h
    errno.h
    unistd.h
    time.h
    sys/types.h
    sys/stat.h
    sys/time.h
    sys/param.h ) 
if (WIN32) 
list(APPEND FILES_TO_CHECK io.h
        winsock2.h
        ws2tcpip.h
        afunix.h ) 
else()
 list(APPEND FILES_TO_CHECK netdb.h
        dlfcn.h
        arpa/inet.h
        poll.h
        port.h
        sys/socket.h
        sys/random.h
        sys/un.h
        sys/devpoll.h
        sys/epoll.h
        sys/eventfd.h
        sys/event.h
        sys/ioctl.h
        sys/mman.h
        sys/queue.h
        sys/select.h
        sys/sendfile.h
        sys/uio.h
        sys/wait.h
        sys/resource.h
        sys/timerfd.h
        netinet/in.h
        netinet/in6.h
        netinet/tcp.h
        ifaddrs.h ) 
endif()
if (NOT "${CMAKE_HOST_SYSTEM_NAME}" STREQUAL "Linux")
 	list(APPEND FILES_TO_CHECK sys/sysctl.h)
endif()
if (APPLE) 
	list(APPEND FILES_TO_CHECK mach/mach_time.h
        mach/mach.h ) 
endif()
foreach(FILE ${FILES_TO_CHECK}) 
CHECK_INCLUDE_FILE_CONCAT(${FILE} "EVENT")
endforeach() 
unset(FILES_TO_CHECK)

将头文件汇总到FILES_TO_CHECK变量中,使用CHECK_INCLUDE_FILE_CONCAT宏函数进行检测是否存在
对应CHECK_INCLUDE_FILE_CONCAT宏函数,我们需要追踪到Macros.cmake文件中找到其定义

macro(CHECK_INCLUDE_FILE_CONCAT FILE PREFIX)
  string(REGEX REPLACE "[./]" "_" FILE_UL ${FILE})
  string(TOUPPER "${FILE_UL}" FILE_UL_UPPER)
  if ("${PREFIX}" STREQUAL "")
    set(HAVE_FILE_DEF "HAVE_${FILE_UL_UPPER}")
  else()
    set(HAVE_FILE_DEF "${PREFIX}__HAVE_${FILE_UL_UPPER}")
  endif()
  CHECK_INCLUDE_FILES("${EVENT_INCLUDES};${FILE}" ${HAVE_FILE_DEF})
  if(${HAVE_FILE_DEF})
    set(EVENT_INCLUDES ${EVENT_INCLUDES} ${FILE})
  endif()
endmacro()

最终调用的是CHECK_INCLUDE_FILES这个cmake内置的宏函数,这个宏函数的原理很简单,就是将头文件放到一个c/c++到源码,然后gcc判断是否能够成功,如果报错就代表该文件不存在。 上面还有个重要的地方, 这里会根据select,poll,epoll等的头文件来设置EVENT__HAVE_EPOLL的值!

# test.c
#include <netinet/tcp.h>
int main() {
	return 0;
}

# gcc -o test test.c
# Check if functions exist 
list(APPEND SYMBOLS_TO_CHECK getaddrinfo
    getnameinfo
    getprotobynumber
    getservbyname
    gethostbyname
    inet_ntop
    inet_pton
    gettimeofday
    signal
    strtoll
    splice
    strlcpy
    strsep
    strtok_r
    vasprintf
    timerclear
    timercmp
    timerisset
    timeradd
    nanosleep
    putenv
    umask ) 
if (NOT EVENT__DISABLE_CLOCK_GETTIME) 
	list(APPEND SYMBOLS_TO_CHECK clock_gettime) 
endif()

if (WIN32) 
list(APPEND SYMBOLS_TO_CHECK _gmtime64_s
        _gmtime64 ) else() list(APPEND SYMBOLS_TO_CHECK getifaddrs
        select
        epoll_create
        epoll_create1
        epoll_ctl
        eventfd
        poll
        port_create
        kqueue
        fcntl
        mmap
        pipe
        pipe2
        sendfile
        sigaction
        strsignal
        sysctl
        accept4
        arc4random
        arc4random_buf
        arc4random_addrandom
        getrandom
        getegid
        geteuid
        issetugid
        usleep
        timerfd_create
        setenv
        unsetenv
        setrlimit
        gethostbyname_r )
    if (APPLE) 
    list(APPEND SYMBOLS_TO_CHECK mach_absolute_time) 
    endif() 
 endif()
# Add stdio.h for vasprintf set(EVENT_INCLUDES ${EVENT_INCLUDES} stdio.h) CHECK_SYMBOLS_EXIST("${SYMBOLS_TO_CHECK}" "${EVENT_INCLUDES}" "EVENT") 
unset(SYMBOLS_TO_CHECK)

set(EVENT__HAVE_EPOLL ${EVENT__HAVE_EPOLL_CREATE})
if(WIN32 AND NOT CYGWIN) 
	set(EVENT__HAVE_WEPOLL 1)
endif()

检测函数是否存在,这个原理和上面的一样。在这里使用了CHECK_SYMBOLS_EXIST宏函数

CHECK_TYPE_SIZE("struct sockaddr_un" EVENT__HAVE_STRUCT_SOCKADDR_UN) 
CHECK_TYPE_SIZE("uint8_t" EVENT__HAVE_UINT8_T) 
CHECK_TYPE_SIZE("uint16_t" EVENT__HAVE_UINT16_T) 
CHECK_TYPE_SIZE("uint32_t" EVENT__HAVE_UINT32_T) 
CHECK_TYPE_SIZE("uint64_t" EVENT__HAVE_UINT64_T) 
CHECK_TYPE_SIZE("short" EVENT__SIZEOF_SHORT BUILTIN_TYPES_ONLY) 
CHECK_TYPE_SIZE("int" EVENT__SIZEOF_INT BUILTIN_TYPES_ONLY) 
CHECK_TYPE_SIZE("unsigned" EVENT__SIZEOF_UNSIGNED BUILTIN_TYPES_ONLY) 
CHECK_TYPE_SIZE("unsigned int" EVENT__SIZEOF_UNSIGNED_INT BUILTIN_TYPES_ONLY) 
CHECK_TYPE_SIZE("long" EVENT__SIZEOF_LONG BUILTIN_TYPES_ONLY) 
CHECK_TYPE_SIZE("long long" EVENT__SIZEOF_LONG_LONG BUILTIN_TYPES_ONLY)

检测某个数据类型是否存在,使用的是CHECK_TYPE_SIZE这个宏函数。后面还有其他检测,内容类似,方法类似。

set(HDR_PRIVATE bufferevent-internal.h
    changelist-internal.h
    defer-internal.h
    epolltable-internal.h
    evbuffer-internal.h
    event-internal.h
    evmap-internal.h
    evrpc-internal.h
    evsignal-internal.h
    evthread-internal.h
    ht-internal.h
    http-internal.h
    iocp-internal.h
    ipv6-internal.h
    log-internal.h
    minheap-internal.h
    mm-internal.h
    ratelim-internal.h
    strlcpy-internal.h
    util-internal.h
    evconfig-private.h
    compat/sys/queue.h)
    
set(HDR_COMPAT include/evdns.h
    include/evrpc.h
    include/event.h
    include/evhttp.h
    include/evutil.h)
    
set(HDR_PUBLIC include/event2/buffer.h
    include/event2/bufferevent.h
    include/event2/bufferevent_compat.h
    include/event2/bufferevent_struct.h
    include/event2/buffer_compat.h
    include/event2/dns.h
    include/event2/dns_compat.h
    include/event2/dns_struct.h
    include/event2/event.h
    include/event2/event_compat.h
    include/event2/event_struct.h
    include/event2/watch.h
    include/event2/http.h
    include/event2/http_compat.h
    include/event2/http_struct.h
    include/event2/keyvalq_struct.h
    include/event2/listener.h
    include/event2/rpc.h
    include/event2/rpc_compat.h
    include/event2/rpc_struct.h
    include/event2/tag.h
    include/event2/tag_compat.h
    include/event2/thread.h
    include/event2/util.h
    include/event2/visibility.h
    ${PROJECT_BINARY_DIR}/include/event2/event-config.h)

设置隐藏头文件,兼容头文件和共有头文件。这就是libevent中的隐藏的使用方法,即将一些头文件不放入到打包的include中

。在发布打包后,只能在对应的.h文件中找到的方法才是可以外部使用的,可以看到log头文件都是外部不能使用的。

set(SRC_CORE buffer.c
    bufferevent.c
    bufferevent_filter.c
    bufferevent_pair.c
    bufferevent_ratelim.c
    bufferevent_sock.c
    event.c
    evmap.c
    evthread.c
    evutil.c
    evutil_rand.c
    evutil_time.c
    watch.c
    listener.c
    log.c
    signal.c
    strlcpy.c)
if(EVENT__HAVE_SELECT) 
	list(APPEND SRC_CORE select.c) 
endif()
if(EVENT__HAVE_POLL) 
	list(APPEND SRC_CORE poll.c) 
endif()
if(EVENT__HAVE_KQUEUE) 
	list(APPEND SRC_CORE kqueue.c) 
endif()
if(EVENT__HAVE_DEVPOLL) 
	list(APPEND SRC_CORE devpoll.c) 
endif()
if(EVENT__HAVE_EPOLL) 
	list(APPEND SRC_CORE epoll.c) 
endif()
if(EVENT__HAVE_WEPOLL) 
	list(APPEND SRC_CORE epoll.c wepoll.c) 
endif()
if(EVENT__HAVE_EVENT_PORTS) 
	list(APPEND SRC_CORE evport.c) 
endif()
if (NOT EVENT__DISABLE_OPENSSL) 
find_package(OpenSSL REQUIRED)
    set(EVENT__HAVE_OPENSSL 1)
    message(STATUS "OpenSSL include: ${OPENSSL_INCLUDE_DIR}")
    message(STATUS "OpenSSL lib: ${OPENSSL_LIBRARIES}")
    include_directories(${OPENSSL_INCLUDE_DIR})
    list(APPEND SRC_OPENSSL bufferevent_openssl.c)
    list(APPEND HDR_PUBLIC include/event2/bufferevent_ssl.h)
    list(APPEND LIB_APPS ${OPENSSL_LIBRARIES}) endif()

设置源码文件,这里会根据是否存在EVENT__HAVE_SELECT等变量来添加对应的.c文件。至于EVENT_HAVE_SELECT这些变量怎么设置的,在前面(第8条)已经讲解过。

set(SRC_EXTRA event_tagging.c
    http.c
    evdns.c
    evrpc.c)

设置扩展库的源文件,libevent会编译成几个库文件,这种就是通过这种方式设置的。

configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/event-config.h.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/include/event2/event-config.h NEWLINE_STYLE UNIX)
configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/evconfig-private.h.cmake
    ${CMAKE_CURRENT_BINARY_DIR}/include/evconfig-private.h)

根据模板,同时根据上面生成的一些变量来生成event-config.h和evconfig-private.h文件,这两个文件至关重要,是后面进行事件分发器选择的根据。

include(AddEventLibrary) 
add_event_library(event_core SOURCES ${SRC_CORE}) 
add_event_library(event_extra INNER_LIBRARIES event_core
    SOURCES ${SRC_EXTRA})
if (NOT EVENT__DISABLE_OPENSSL) 
	add_event_library(event_openssl INNER_LIBRARIES event_core
        OUTER_INCLUDES ${OPENSSL_INCLUDE_DIR}
        LIBRARIES ${OPENSSL_LIBRARIES}
        SOURCES ${SRC_OPENSSL})
endif()
if (EVENT__HAVE_PTHREADS) 
	set(SRC_PTHREADS evthread_pthread.c)
    add_event_library(event_pthreads INNER_LIBRARIES event_core
        SOURCES ${SRC_PTHREADS}) endif()

生成的几个库分别为event_core, event_extra, event_openssl, event_pthreads就是在这里设置的

# Install compat headers install(FILES ${HDR_COMPAT} DESTINATION "include"
        COMPONENT dev)
# Install public headers install(FILES ${HDR_PUBLIC} DESTINATION "include/event2"
        COMPONENT dev)
# Install the configs. install(FILES ${PROJECT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/LibeventConfig.cmake
        ${PROJECT_BINARY_DIR}/LibeventConfigVersion.cmake
        DESTINATION "${EVENT_INSTALL_CMAKE_DIR}"
        COMPONENT dev)

cmake生成完成后,将对应的文件拷贝到对应的目录,至此输出一些信息,cmake完成

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值