美文网首页
cmake 学习指南

cmake 学习指南

作者: horsetif | 来源:发表于2018-09-05 20:25 被阅读0次

    这篇文章主要介绍如何一步一步自己写cmake文件。

    一,内部链接和外部链接

    在总文件夹下新建main.c

    //main.c
    #include<stdio.h>
    int main(){
        printf("hello world from t1 main!\n");
        return 0;
    }
    

    在同一目录下写好CMakeLists.txt(这个文件很重要,大小写有特殊要求,如果存在多个目录,要在每一个目录下面都要存在一个CMakeLists.txt)

    PROJECT(HELLO)#新建项目
    SET(SRC_LIST main.c)#把文件名指定为特殊字符 如果有多个,那么SET(SRC_LIST main.c t1.c t2.c)-->基本语法:参数之间用空格分开即可。
    #指令是大小写无关的,但是参数和变量是大小写相关的。
    MESSAGE(STATUS "this is BINARY dir" ${PROJECT_BINARY_DIR})#这里默认变量
    MESSAGE(STATUS "this is  SOURCE dir" ${PROJECT_SOURCE_DIR})#这里是默认变量
    #message 语法:SEND_ERROR:产生错误,生成过程被跳过。
    #STATUS:输出前缀为-的信息。FATAL_ERROR:立刻终止cmake过程。
    ADD_EXECUTABLE(hello ${SRC_LIST})#添加到编译生成的执行文件
    #定义的这个工程会生成一个文件名字为hello的可执行文件
    #对于变量的引用,只有在IF的情况下,变量才会被直接引用,其他时候都要加入${}才可以。
    

    接下来输入:

    cmake .
    make
    

    就生成执行文件.(以上为内部构建,比较冗长,容易生成很多无用的信息)

    外部构建
    mkdir build
    cd build
    cmake ..
    make 
    

    我们可以在当前目录下获得目标文件hello。最大的好处是对原有工程没有任何影响,所有动作都全部发生在编译目录。
    此时默认变量:PROJECT_SOURCE_DIR 依然是工程目录
    但是 PROJECT_BINARY_DIR 在外部编译之后从原来的工程目录转化为工程目录/build

    二,多文件下的cmake与安装(外部构建)

    文件分层:src--工程源码(main.c) doc--工程文档 COPYRIGHT/README 放在根目录 bin--构建后目标文件所在的子目录
    安装:我们要把编译得到的内容安装到/tmp/usr/test2 中
    src下的main.c文件:

    #include<stdio.h>
    int main(){
        printf("hello world from t1 main!\n");
        return 0;
    }
    

    src下的CMakeList.txt 文件

    SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
    #设定变量的得到的编译执行文件输出到bin文件下。
    #如果有编译好的lib文件,那么使用
    #SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)
    #在哪里加入ADD_EXECUTABLE or ADD_LIBRARY,在哪里写这两句话
    ADD_EXECUTABLE(hello main.c)
    install(TARGETS hello RUNTIME DESTINATION bin)
    #安装目标文件(三种类型)可执行文件-RUNTIME 动态库-LIBRARY 静态库-ARCHIVE 
    #example:
    #INSTALL(TARGETS myrun mylib mystaticlib
    #              RUNTIME DESTINATION bin
    #              LIBRARY DESTINATION lib
    #              ARCHIVE DESTINATION libstatic
    #)
    

    根目录下的CMakeList.txt文件

    cmake_minimum_required(VERSION 3.5)
    #确定最小版本号
    PROJECT(HELLO)
    install(FILES COPYRIGHT README DESTINATION share/doc/cmake/test2)
    #安装普通文件
    install(PROGRAMS runhello.sh DESTINATION bin)
    #安装非目标文件的可执行程序
    install(DIRECTORY doc/ DESTINATION share/doc/cmake/test2)
    #安装文件夹
    add_subdirectory(src)
    #向当前工程添加存放源文件的子目录,并可以指定编译输出的结果
    #add_subdirectory(source_dir [binary_dir]) 如果编译输出目录为空,那么就是build下的相同目录
    

    在安装之前,必须要定义安装的位置,否则将会默认为/usr/local
    我们使用DCMAKE_INSTALL_PREFIX 定义

    cmake  -DCMAKE_INSTALL_PREFIX=/tmp/test2/usr  ..
    make 
    make install
    

    静态库与动态库的构建

    首先建立lib文件夹,写入hello.c 和 hello.h

    //hello.c
    #include "hello.h"
    void HelloFunc(){
        printf("hello world in test3!\n");
    }
    
    //hello.h
    #ifndef HELLO_H
    #define HELLO_H
    #include <stdio.h>
    void HelloFunc();
    #endif
    

    lib下的CMakeLists.txt:

    set(LIBHELLO_SRC hello.c)
    add_library(hello SHARED ${LIBHELLO_SRC})
    #生成共享库
    add_library(hello_static STATIC ${LIBHELLO_SRC})
    #生成静态库(这里不能重名,否则只能生成一个),不需要写全名
    #SHARED 动态库 STATIC 静态库
    set_target_properties(hello_static PROPERTIES OUTPUT_NAME "hello")
    #我希望输出的名字一样,所以要改变静态库的名字
    set_target_properties(hello_static PROPERTIES CLEAN_DIRECT_OUTPUT 1)
    set_target_properties(hello PROPERTIES CLEAN_DIRECT_OUTPUT 1)
    #cmake会在构建新的target时候,尝试清理其他使用这个名字的库,我们要回避这个问题,所以要修改参数
    set_target_properties(hello PROPERTIES VERSION 1.2 SOVERSION 1)
    #设置动态库版本
    get_target_property(OUTPUT_VALUE hello_static OUTPUT_NAME)
    #获取输出名字的内容
    message(STATUS "this is the hello_static output_name:"${OUTPUT_VALUE})
    install(TARGETS hello hello_static 
        LIBRARY DESTINATION lib
        ARCHIVE DESTINATION lib)
    #安装静态库和动态库
    install(FILES hello.h DESTINATION include/hello)
    #安装
    

    主目录下的CMakeList.txt

    project(HELLOLIB)
    add_subdirectory(lib lib_HTF)
    #如果要修改输出位置,可以在add_subdirectory 中设置或者在lib/CMakeList.txt中设置SET(LIBRARY_OUTPUT_PATH ...)中设置。
    

    三,使用外部共享库和头文件

    在src文件下写入源文件 main.c :

    #include <hello.h>
    int main(){
        HelloFunc();
        return 0;
    }
    

    编写src/CMakeLists.txt:

    include_directories(/tmp/usr/test3/include/hello)
    #添加包含目录
    link_directories(/tmp/usr/test3/lib)
    #添加库目录
    add_executable(main hello.c)
    #链接执行文件
    target_link_libraries(main hello)
    #添加执行文件下所需要的以来动态库
    

    编写根目录下的CMakeList.txt

    cmake_minimum_required(VERSION 3.5)
    #设定cmake版本最小号
    PROJECT(NEWHELLO)
    EXEC_PROGRAM(ls ARGS "-l" OUTPUT_VARIABLE LS_OUTPUT RETUREN_VALUE LS_RVALUE)
    #cmake中处理执行命令,args添加参数,output_variable为输出值,return_value为返回值
    IF(NOT LS_RVALUE)
    message(STATUS "ls result: "${LS_OUTPUT})
    ENDIF(NOT LS_RVALUE)
    aux_source_directory(. SRC_LIST)
    #作用是发现一个目录下所有的源代码并将列表储存到一个变量中。
    IF(NOT SRC_LIST)
    message(STATUS "do not find any files")
    ENDIF(NOT SRC_LIST)
    foreach(F ${SRC_LIST})
        message(${F})
    endforeach(F)
    #foreach循环 ,使用方法:
    #1,foreach(F  ${SRC_LIST})
    #2,foreach(VAR RANGE 10)
    #3, foreach (loop_var RANGE start stop [step])
    add_subdirectory(src)
    

    如果使用find的命令来查找,那么src/CMakeLists.txt 修改为:

    #export CMAKE_INCLUDE_PATH=/tmp/usr/test3/include/hello
    #export CMAKE_LIBRARY_PATH=/tmp/usr/test3/lib
    这个两句都是外部命令,也可以用下面来指定
    find_path(myHeader hello.h /tmp/usr/test3/include/hello)
    #查找文件位置是否存在
    IF(myHeader)
    message(STATUS ${myHeader})
    include_directories(${myHeader})
    ENDIF(myHeader)
    
    find_library(myLib hello /tmp/usr/test3/lib)
    查找lib是否存在
    IF(myLib)
    message(STATUS ${myLib})
    ELSEIF(NOT myLib)
    MESSAGE(FAULT "libhello not found")
    ENDIF(myLib)
    
    add_executable(main hello.c)
    target_link_libraries(main ${myLib})
    #添加lib,这里应该是绝对路径了
    

    四,cmake常用变量

    1,引用方式 {} ,只有在IF情况下才直接用 2,常用变量 CMAKE_BINARY_DIR=PROJECT_BINARY_DIR=<projectname>_BINARY_DIR CMAKE_SOURCE_DIR=PROJECT_SOURCE_DIR=<projectname>_SOURCE_DIR CMAKE_CURRENT_SOURCE_DIR 当前处理的CMakeList所在路径 CMAKE_CURRENT_BINARY_DIR,out-of-source 中指的是target编译路径 3,cmake 调用环境变量ENV{NAME} 可以直接调用环境变量
    message(STATUS "home dir: $ENV{HOME}")

    五,自定义模块和模块使用

    一般使用FIND_PACKAGE(XXX)来使用模块
    每一个模块都有这几个变量:
    1,<name>_FOUND
    2,<name>_INCLUDE_DIR or <name>_INCLUDES
    3,<name>_LIBRARY or <name>_LIBRARIES

    编写属于自己的FindHello 模块

    建立cmake文件夹,编写cmake/FindHELLO.cmake 模块

    FIND_PATH(HELLO_INCLUDE_DIR hello.h /tmp/usr/test3/include/hello)
    FIND_LIBRARY(HELLO_LIBRARY hello /tmp/usr/test3/lib)
    IF(HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
        SET(HELLO_FOUND TRUE)
    ELSE(HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
        message(STATUS "include: " ${HELLO_INCLUDE_DIR})
        message(STATUS "lib: " ${HELLO_LIBRARY})
    ENDIF(HELLO_INCLUDE_DIR AND HELLO_LIBRARY)
    IF(HELLO_FOUND)
        IF(NOT HELLO_FIND_QUIETLY)
    #REQUIRED 参数,就是确定共享库是否必须
            message(STATUS "Found Hello: ${HELLO_LIBRARY}")
        ENDIF(NOT HELLO_FIND_QUIETLY)
    ELSE(HELLO_FOUND)
        IF(HELLO_FIND_REQUIRED)
            message(FATAL_ERROR "Could not find hello library")
        ELSE(HELLO_FIND_REQUIRED)
            message(STATUS "in find_hello.cmake,we do not find hello lib")
        ENDIF(HELLO_FIND_REQUIRED)
    ENDIF(HELLO_FOUND)
    

    定义src/CMakeLists.txt

    set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
    #注意确定cmake module的路径
    FIND_PACKAGE(HELLO)
    #这里可以改为 FIND_PACKAGE(HELLO QUIET) or FIND_PACKAGE(HELLO REQUIRED)
    IF(HELLO_FOUND)
        ADD_EXECUTABLE(hello main.c)
        include_directories(${HELLO_INCLUDE_DIR})
        target_link_libraries(hello ${HELLO_LIBRARY})
        message(STATUS "include: " ${HELLO_INCLUDE_DIR})
        message(STATUS "lib: " ${HELLO_LIBRARY})
    ELSEIF(HELLO_FOUND)
        message("do not find hello!")
    ENDIF(HELLO_FOUND)
    

    定义src/main.c

    #include<hello.h>
    int main(){
        HelloFunc();
        return 0;
    }
    

    主目录下的CMakeLists.txt

    cmake_minimum_required(VERSION 3.5)
    project(HELLO)
    add_subdirectory(src)
    

    相关文章

      网友评论

          本文标题:cmake 学习指南

          本文链接:https://www.haomeiwen.com/subject/wwqnwftx.html