精华内容
参与话题
问答
  • CMake进阶之CMake原理与关键概念

    千次阅读 多人点赞 2018-08-09 15:56:34
    上一篇文章中我们通过一个很简单的示例项目展示了CMake构建脚本...这篇文章我们来学习一下CMake原理,并详细阐述这些概念。 1 CMake的结构  CMake有三个关键概念:target、generator和command,其中target和com...

        上一篇文章中我们通过一个很简单的示例项目展示了CMake构建脚本CMakeFileLists.txt的构成,我们初步认识了CMake中的一些概念:target(目标)、command(命令)等等。这篇文章我们来学习一下CMake的原理,并详细阐述这些概念。

    1 CMake的结构

        CMake有三个关键概念:target、generator和command,其中target和command我们已经有所了解了。在CMake中,这些东西本质上都是C++的类。理解这些概念对于编写出高效的CMake构建脚本很有帮助。

        在更进一步了解CMake这些概念之前,有必要先了解一下target、generator和command之间的关系,一图胜千言:

        

        这张图够大够清晰,从《mastering cmake》中截取的,很好的描述了CMake主要组件关系:

        (1) cmake:控制cmake进程,可以在各种gui、命令行中创建和使用;

        (2) cmake持有一个cmGlobalGenerator的实例,cmGlobalGenerator是一个抽象基类,针对不同的平台有不同的子类:比如unix平台下的子类cmGlobalUnixMakeFileGenerator,微软vs的cmGlobalVisualStudio6Generator等。全局生成器的作用是负责特定平台的构建。

        (3) 全局生成器持有多个本地生成器cmLocalGenerator的实例,与cmGlobalGenerator类似,cmLocalGenerator也是抽象基类,有不同的子类去负责生成特定平台的构建文件。本地生成器对应于项目中各目录的CMakeFileLists.txt脚本。

        (4) 每一个localGenerator持有一个cmMakeFile实例,cmMakeFile解析并存储所有从CMakeFileLists.txt中解析出来的数据,包括target列表、所有的变量、依赖的库等等。

        (5) cmCommand:抽象基类,代表各种CMake命令的实现。

        CMake处理的最底层的东西就是若干个源代码文件,这些源代码文件被整合成一个或多个target,target一般情况下是一个可执行文件或者是库文件。对于源码目录树种的每一个目录,如果该目录中存在名为CMakeFileLists的构建脚本,并且有一个或多个target与之关联,则cmake会创建一个cmLocalGenerator的派生类(依据平台)为该目录生成特定平台的makefile或者项目构建文件。所有的local generator共享一个全局的global generator对象,这个全局global generator对象有cmake创建并管理。

    2 CMake的运行原理

        我们已经了解了构成CMake的一些主要的组件以及他们之间的关系,接下来更详细的说明CMake的运行原理。

        当我们在命令行输入:cmake ../Test这样一条命令的时候,一个cmake对象会被创建出来,命令行参数传入该对象。cmake对象管理整个项目的配置,并且持有构建过程需要用到的一些全局信息(比如缓存值)。cmake对象首先要做的事情之一就是创建一个全局生成器对象(cmGlobalGenerator的子类),然后调用该对象的相关方法,传入必要的参数。

        全局生成器负责配置并生成项目所有的Makefile(或者工程文件),当然全局生成器是通过创建若干个本地生成器(localGenerator),然后把工作委托给本地生成器来完成这项艰巨任务的。对于项目中每一个包含CMakeFileLists脚本的目录,全局生成器都会创建一个本地生成器来生成对应的目录的构建文件。

        绝大多数工作其实是由本地生成器去做的,例如类Unix平台上,cmake会创建出cmGlobalUnixMakefileGenerator全局生成器,对于每个目录,全局生成器生成cmLocalUnixMakefileGenerator本地生成器,由它去解析目录中的CMakefileLists脚本,并生成makefile或其他特定于平台的文件。全局生成器要做的主要是将各个loacal generator的工作收集起来生成一个最顶层的Makefile。

        每个本地生成器都会创建并持有一个cmMakefile对象,cmMakefile解析CMakefileLists脚本,并存储解析到的数据。因为一个cmMakefile对应项目中的一个目录(确切的说应该是包含CMakefileLists脚本的目录),因此cmMakefile对象有时候也用目录来称呼。可以把cmMakefile理解成是一个由父目录用相关参数初始化的对象,然后在解析CMakefileLists脚本的过程中逐渐向其中填充各种数据。

        最后再来看看command,上一篇文章我们已经看到了许多的CMake命令(find_library,set,target_link_libraries等等),在CMake中每个命令都对应一个C++的类。每个命令对象主要有两个关键方法:

        InitialPass,该方法以命令参数以及当前处理的目录对应的cmMakefile对象的实例 为参数,然后执行相应的操作。以set (Foo a)这个命令为例,该命令会调用传入的cmMakefile对象的相关方法给变量设定值,命令的执行结果通常也是存放在cmMakefile对象中。

        command对象的另一个主要方法是FinalPass,当所有的命令的InitialPass执行完以后,会调用这个方法。绝大多数命令都不实现这个方法,只有极少数的情况才会用到。

        一旦项目中所有的CMakefileLists构建脚本处理完毕,生成器就会用已经存放在cmMakefile实例中的信息去生成目标构建系统(例如make或者visual studio)上需要的文件。

    3 详解target

        target顾名思义就是cmake最终要生成的目标,一般情况下就是可执行文件和库。通过下面三条命令都可以生成一个目标:

        add_library :生成一个库目标;

        add_executable:生成一个可执行目标;

        add_custom_target:生成一个自定义目标,这个用途也很大,我们在后续文章中在单独详细说明。

        我们以add_library为例,上一篇文章已经看到过它的魅影,这里更详细的认识一下这个命令:

        add_library (foo, STATIC, foo.c)

        这条命令将生成一个名为foo的静态库目标,有了这条命令,foo这个名字就可以作为库名在你的项目中的任何地方使用。库通常是STATIC,SHARED和MODULE三种类型之一,STATIC表明生成的是一个静态库,类似的SHARED为共享库,MOUDLEB表明生成的库可以被可执行文件动态加载。除了mac os x系统外,SHARED和MODULE作用是相同的。如果使用add_library命令时不指定类型,则cmake会根据BUILD_SHARED_LIBS变量的值来确定是编译出SHARED库还是STATIC库,如果BUILD_SHARED_LIBS也没有指定,则默认编译出STATIC库。

        类似的,add_executable命令也有一些选项,默认的情况下cmake会生成一个带有main函数的控制台应用程序,如果在可执行目标名的后面指定了WIN32,则该命令将生成一个win32应用程序,其入口为WinMain,在非windows的平台上,WIN32选项不起任何作用。

        除了类型以为,target还持有一些通用的属性,这些属性可以通过set_target_property和get_target_property访问。这些属性中最常见的是LINK_FLAGS,用来为待生成的目标指定链接选项。  target要链接的库可以通过target_link_libraries,这个上一篇文章的示例程序也已经看到过。

        对于每一个生成的库,cmake都会跟踪该库所依赖的其他库,举个例子就清楚这话是什么意思了,假设有如下命令:

        add_library (foo foo.c)

        target_link_libraries (foo bar)

        add_executable (foobar foobar.cpp)

        target_link_libraries (foobar foo)

        首先添加了一个库目标foo,然后foo这个库依赖于另外一个库bar,因此用target_link_libraries链接之。然后添加了一个可执行目标foobar,注意上面的代码中,只给改可执行目标链接了foo库,但是cmake知道foo库链接了bar库,因此虽然我们没有写显示指定,但是cmake会自动给可执行目标foobar链接上bar库。

    4 变量和缓存

    4.1 变量以及作用域

        和其他编程语言一样,CMAKE中也有变量的概念,用来把值存起来以备后用。变量可以只有一个值,也可以拥有一组值(想象成数组)除了自定义的变量外,CMAKE已经内置了一些很有用的变量。

        变量通过set命令定义,值得注意的是CMAKE中的变量也是有作用域的,来看个例子:

        set (foo 1)  #定义变量foo,值为1

        add_subdirectory (dir1) #处理子目录dir1,之前定义的变量foo被传入子目录的生成器中,并且值为1

        set (bar 2) #定义变量bar,值为2,注意此时dir1中看不到此变量

        add_subdirectory (dir2) #处理子目录dir2,变量foo和bar都会被传入到dir2的生成器中,因此在该目录下两个变量均可见。

        再来看一个关于CMAKE中变量作用域的例子:   

    function (foo) #定义一个函数foo
    
            message (${test}) #输出变量test的值
    
            set (test 2) #将变量test的值设置为2
    
            message (${test}) #变量的值为2
    
        endfunction() #函数结束
    
        set (test 1) #将变量test的值设置为1
    
        foo () #调用函数
    
        message (${test}}) #打印变量test的值,此时仍旧为1

        注意到上面这个例子中,定义了一个函数foo,函数调用前test的值为1,然后在foo函数中将变量test的值置为2,可以看到函数调用结束以后变量的值仍旧为1,也就是说函数foo中对变量的改变仅限于函数内部,这有点类似于C语言中以传值的方式给函数传递参数,此时相当于变量被拷贝了一份传递给函数。

        那如果想让函数中对变量的改变在函数外部也生效呢?可以把函数改成下面这样:    

    function (foo) #定义一个函数foo
    
            message (${test}) #输出变量test的值
    
            set (test 2 PARENT_SCOPE) #将变量test的值设置为2
    
            message (${test}) #变量的值为2
    
    endfunction() #函数结束
    
        

        注意到函数中在设置值时,set命令多了一个PARENT_SCOPE,表示此次的改变在变量的父作用域中也生效,这样当函数调用完以后,值就变成新值2了。

    4.2 缓存

        我们经常会遇到这种情况:有些变量的值想让用户在构建的时候自己去做选择,缓存变量(cache entry)可以满足我们的需求。我们运行CMAKE的时候,CMAKE都会在构建目录下生成一个缓存文件,这个缓存文件中的值将会显示在cmake gui中。缓存的目的有两个:

        (1) 将用户的选择缓存下来,这样用户之后不论何时启动cmake,他们不必重新去输入这些值;

        通过option命令可以就可以创建一个缓存变量并存放在缓存文件中:

        option (USE_JPEG "Do you want to use the jpeg library")

        上面这个option命令帮我们生成一个USE_JPEG缓存变量,当用户运行cmake-gui时,就可以通过ui设置变量的值。

        有3中方式来创建一个缓存变量:

        option命令、find_file命令以及set命令,用set命令生成缓存变量时,只需要加一个CACHE就可以了,向下面这样:

        set (USE_JPEG ON CACHE BOOL "Do you want to use the jpeg library")

        和一般的set命令不同,需要指定变量的类型,并且用CACHE告诉CMAKE这是一个缓存变量。

        (2) 使用缓存变量的另外一个原因是:有些变量的值确定起来非常耗时,比如一些系统相关的变量如字节序,为了确定字节序,CMAKE必须要编译并且运行一小段程序才能确定,对于这种代价比较高的变量,同时值确定以后不会怎么变的,将它缓存起来是一个不错的主意。

        除了单一的值以外,有时候可能想提供一组值让用户选择,可以通过给缓存变量提供STRINGS属性达成此目的。参考下面的代码段:
       

    #定义一个类型为STRING的缓存变量
    
    set (CRYPTOBACKEND "OpenSSL" CACHE STRING "Select a crypto backend" )
    
    #给缓存变量CRYPTOBACKEND设置属性STRINGS,指定三个值作为选项
    
    set_property (CACHE CRYPTOBACKEND PROPERTY STRINGS "OpenSSL" "LibTomCrypt" "LibDES")

        加入这段代码以后,运行cmake-gui,会在界面上看到一个下拉选择菜单,让用户选择值。    

        最后一点:缓存的值在CMAKE中是可以覆盖的。通过不带CACHE选项的set命令,就可以覆盖缓存变量的值。注意覆盖以后,缓存中变量的值不会改变,只有当前目录以及子目录中的值会被覆盖。

        例如有一个缓存变量foo,缓存中的值是1,然后在顶层的CMakefileLists文件中通过set命令改写了它的值:

        set (foo 2)

        这样当前目录及所有子目录中该变量的值均为2,但是缓存中依旧为1。

    5 CMAKE构建配置

         CMAKE通过配置可以构建出不同的软件,默认的情况下CMKAE支持四种构建配置:

        (1) Debug:CMKAE会打开一些基本的调试开关进行构建;

        (2) Release:CMAKE在构建时会打开一些基本的优化选项开关;

        (3) MinSizeRel:CMAKE会使构建出的目标代码体积尽可能小,但不一定是最优最快的;

        (4) RelWithDebInfo:CMKAE构建出带有调试信息并且经过编译优化后的版本;

        CMAKE会根据具体的生成器进行处理(就是之前提到的cmUnixMakefileGenerator,cmVisualStudioGenerator等),一般情况下CMAKE会尽量遵守原生构建系统的规则。

        对于visual studio,可以通过变量CMAKE_CONFIGURATION_TYPE告诉CMAKE工程应该使用哪种配置,而基于makefile类型的生成器,可以通过CMAKE_BUILD_TYPE来指定配置类型,举个例子:

        假设源码目录是TestProjSrc,可以在同级目录中创建一个名为Debug的构建目录,然后cd Debug,接着:

        cmake ../TestProjSrc -DCMAK_BUILD_TYPE:STRING=Debug

        同样的,如果想构建一个Release版本,可以在同级目录创建名为Release的目录,然后与上面命令类似。

    6 小结

        本文我们了解了cmake的一些主要组件以及其运行的原理。

        (1) 介绍了cmake的主要组件以及其运行原理;

        (2) 介绍了cmake的变量,包括缓存变量,以及变量的作用域;

        理解cmake的运行原理有助于我们写出更高效的cmake构建脚本,可以结合项目实践来加深对相关概念的理解。

        

    展开全文
  • Cmake 编译原理

    千次阅读 2019-06-02 17:17:26
    CMake编译原理   CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多。CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令...

    一. CMake编译原理

      CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多。CMake主要是编写CMakeLists.txt文件,然后用cmake命令CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令编译源码生成可执行程序或共享库(so(shared object))。因此CMake的编译基本就两个步骤:

    cmake
    make

      cmake 指向CMakeLists.txt所在的目录,例如cmake .. /表示CMakeLists.txt在当前目录的上一级目录。cmake后会生成很多编译的中间文件以及makefile文件,所以一般建议新建一个新的目录,专门用来编译,例如:

    mkdir build
    cd build
    cmake ../
    make

    make根据生成makefile文件,编译程序。

     

    二. CMakeLists.txt文件编写

    #表示注释   
    #cmake file for project association
    
    #cmake 最低版本要求,低于2.8 构建过程会被终止。   
    CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
    
    #定义工程名称
    PROJECT(association)
                         
    #打印相关消息消息   
    #MESSAGE(STATUS "Project: ${PROJECT_NAME}")
    #MESSAGE(STATUS "Project Directory: ${PROJECT_SOURCE_DIR}")  
    
    #指定编译类型debug版
    SET(CMAKE_BUILE_TYPE DEBUG)
    #发行版
    #SET(CMAKE_BUILE_TYPE RELEASE)
    
    #SET(CMAKE_C_FLAGS_DEBUG "-g -Wall")             #C
    #SET(CMAKE_CXX_FLAGS_DEBUG "-g -Wall")           #C++
    
    #设置C++ 编译
    SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -s -Wall -W -O3")   
     
    #添加子目录   
    ADD_SUBDIRECTORY(src/include)
    
    #设置变量,表示所有的源文件  
    SET(SOURCE_FILES
        src/main.cpp    
        )
    
    
    #配置相关库文件的目录,  
    LINK_DIRECTORIES(                                  
        /usr/local/lib
        )  
    
    #找BZip2
    FIND_PACKAGE(BZip2)
    if (BZIP2_FOUND)
        MESSAGE(STATUS "${BZIP_INCLUDE_DIRS}")  
        MESSAGE(STATUS " ${BZIP2_LIBRARIES}")  
    endif (BZIP2_FOUND)
    if (NOT BZIP2_FOUND)
        MESSAGE(STATUS "NOT  BZIP2_FOUND")  
    endif (NOT  BZIP2_FOUND)
    
    
    #相关头文件的目录
    INCLUDE_DIRECTORIES(  
         /usr/local/include  
         ${PROJECT_SOURCE_DIR}/utility_inc
         ${BZIP_INCLUDE_DIRS}
        )
    
    #链接库
    LINK_LIBRARIES(
        ${PROJECT_SOURCE_DIR}/static_libs/libSentinelKeys64.a
        ${BZIP2_LIBRARIES}
        )
    
    #生成可执行文件
    ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCE_FILES})
    
    #依赖的库文件  
    TARGET_LINK_LIBRARIES(${PROJECT_NAME} eventloop)
    

    三. 子目录CMakeLists.txt文件编写

    SET(EVENTLOOP_SOURCE_FILES
            tool/BlockingQueue.hpp
            tool/Copyable.h
            tool/ExecuteState.h
            tool/Likely.h
            EventLoop.h
            EventLoop.cpp
            )
    #生成静态链接库eventloop 
    ADD_LIBRARY(eventloop ${EVENTLOOP_SOURCE_FILES})
    


     

    展开全文
  • CMake编译原理

    2019-12-30 16:38:12
    cmake实际是做了一种转换。将CMakeList文件转换成makefile文件,然后由make进行编译。 cmake 命令使用 camke 执行该目录下的CMakeList .txt文件。 cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../...

    cmake实际是做了一种转换。将CMakeList文件转换成makefile文件,然后由make进行编译。

    cmake 命令使用

    camke  执行该目录下的CMakeList .txt文件。

    cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../release ..

    指定编译版本 Debug 或 Release  ,指定安装路径。../release

    CMakeLIst编写规则

    #1.cmake verson,指定cmake版本
    cmake_minimum_required(VERSION 3.2)

    #2.project name,指定项目的名称,一般和项目的文件夹名称对应
    PROJECT(test_wwd)

    #3.head file path,头文件目录
    INCLUDE_DIRECTORIES(
    include
    )

    #4、intall 安装

    install(DIRECTORY ./data DESTINATION .)  ./data 是指CMakeList.txt所在目录下的data,  destination 指向目标文件夹 . 是指目标的根目录。

    #5、set(CMAKE_VERBOSE_MAKEFILE on)   

    开启编译命令行调试、可以输出错误信息

    #6、set(CMAKE_CXX_COMPILER "g++")

    指定g++编译

    #7、add_subdirectory(Route)

    #添加工程子目录

    #8、未完 待续。。。。

     

     

    展开全文
  • CMake 两种变量原理

    2020-01-01 20:59:24
    CMake 两种变量原理 目录 1、两种变量的定义参考 2、两种变量的作用域原理及使用 参考: 博文原始链接 摘要: 本文记录一下 CMake 变量的定义、原理及其使用。CMake 变量包含Normal Variables、Cache ...

    CMake 两种变量原理

     

    博文原始链接


    摘要:

    本文记录一下 CMake 变量的定义、原理及其使用。CMake 变量包含 Normal Variables、Cache Variables。通过 set 指令可以设置两种不同的变量。也可以在 CMake 脚本中使用和设置环境变量。set(ENV{<variable>} <value>...),本文重点讲述 CMake 脚本语言特有的两种变量。

     

    正文:

    1、两种变量的定义参考

    • Normal Variables

      通过 set(<variable> <value>... [PARENT_SCOPE])这个命令来设置的变量就是 Normal Variables。例如 set(MY_VAL "666") ,此时 MY_VAL 变量的值就是 666。

    • Cache Variables

      通过 set(<variable> <value>... CACHE <type> <docstring> [FORCE])这个命令来设置的变量就是 Cache Variables。例如 set(MY_CACHE_VAL "666" CACHE STRING INTERNAL),此时 MY_CACHE_VAL 就是一个 CACHE 变量。

    2、两种变量的作用域原理及使用

    1、Normal Variables

    ​ 作用域属于整个 CMakeLists.txt 文件,当该文件包含了 add_subdirectory()、include()、macro()、function()语句时,会出现两种不同的效果。

    (1)、包含 add_subdirectory()、function()。(本质是值拷贝)

    假设,我们在工程根目录 CMakeLists.txt 文件中使用 add_subdirectory(src) 包含另一个 src 目录,在 src 目录中有另一个 CMakeLists.txt 文件。在终端运行的目录结构如下:

    <span style="color:#000000"><code><span style="color:#2b91af">$</span> tree
    .
    ├── CMakeLists.txt
    └── src
        └── CMakeLists.txt
    
    1 directory, 2 files</code></span>

    以根目录 CMake 文件为父目录,src 目录为子目录,此时子目录 CMake 文件会拷贝一份父目录文件的 Normal 变量。需要说明的是,我们在子目录中如果想要修改父目录 CMake 文件包含的 Normal 变量。必须通过 set(... PARENT_SCOPE) 的方式。下面通过例子来说明。

    在父/根目录的 CMakeLists.txt 文件内容如下:

    <span style="color:#000000"><code><span style="color:#0000ff">cmake_minimum_required</span>(VERSION 3.10)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"父目录 CMakeLists.txt 文件"</span>)
    <span style="color:#0000ff">set</span>(MY_VAL <span style="color:#a31515">"666"</span>)
    
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第一次在父目录 MY_VAL=${MY_VAL}"</span>)
    <span style="color:#0000ff">add_subdirectory</span>(src) 
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第二次在父目录,MY_VAL=${MY_VAL}"</span>)</code></span>

    在子目录 src/CMakeLists.txt 文件内容如下:

    <span style="color:#000000"><code><span style="color:#0000ff">cmake_minimum_required</span>(VERSION 3.10)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"进入子目录 src/CMakeLists.txt 文件"</span>)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"在子目录,MY_VAL=${MY_VAL}"</span>)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"退出子目录"</span>)</code></span>

    运行结果:

    <span style="color:#000000"><code><span style="color:#2b91af">$</span> cmake .
    父目录 CMakeLists.txt 文件
    第一次在父目录 MY_VAL=666
    进入子目录 src/CMakeLists.txt 文件
    在子目录,MY_VAL=666
    退出子目录
    第二次在父目录,MY_VAL=666</code></span>

    从结果可以看出,在子目录 CMake 文件中可以直接使用父目录定义的 MY_VAL 变量的值 666。当在子目录 CMake 文件中修改 MY_VAL 变量值,看看在父目录中 MY_VAL 的值如何变化。下面仅仅在子目录 CMake 文件中加入一行代码 set(MY_VAL "777"),最后的子目录 CMake 文件代码如下:

    <span style="color:#000000"><code><span style="color:#0000ff">cmake_minimum_required</span>(VERSION 3.10)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"进入子目录 src/CMakeLists.txt 文件"</span>)
    <span style="color:#0000ff">set</span>(MY_VAL <span style="color:#a31515">"777"</span>) <span style="color:#008000"># 刚刚加入的</span>
    
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"在子目录,MY_VAL=${MY_VAL}"</span>)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"退出子目录"</span>)</code></span>

    运行结果:

    <span style="color:#000000"><code><span style="color:#2b91af">$</span> cmake .
    父目录 CMakeLists.txt 文件
    第一次在父目录 MY_VAL=666
    进入子目录 src/CMakeLists.txt 文件
    在子目录,MY_VAL=777
    退出子目录
    第二次在父目录,MY_VAL=666</code></span>

    我们发现在 src/CMakeLists.txt 中打印的 MY_VAL 的值是 777,然后退出子目录回到根目录后,打印 MY_VAL 的值仍然是 666。这就说明了:子目录的 CMakeLists.txt 文件仅仅是拷贝了一份父目录的 Normal 变量,即使在子目录 CMake 文件中修改了 MY_VAL 变量,那也只是子目录自己的变量,不是父目录的变量。因为 Normal 变量的作用域就是以 CMakeLists.txt 文件为基本单元。那么我们如何在子目录 CMake 文件中修改父目录 CMake 文件的 Normal 变量呢? 我们需要在子目录 CMakeLists.txt 文件中设置 MY_VAL 时,加上 PARENT_SCOPE 属性。即用如下代码: set(MY_VAL "777" PARENT_SCOPE),子目录 CMakeLists.txt 文件如下:

    <span style="color:#000000"><code><span style="color:#0000ff">cmake_minimum_required</span>(VERSION 3.10)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"进入子目录 src/CMakeLists.txt 文件"</span>)
    <span style="color:#0000ff">set</span>(MY_VAL <span style="color:#a31515">"777"</span> PARENT_SCOPE) <span style="color:#008000"># 修改处</span>
    
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"在子目录,MY_VAL=${MY_VAL}"</span>)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"退出子目录"</span>)</code></span>

    运行结果:

    <span style="color:#000000"><code><span style="color:#2b91af">$</span> cmake .
    父目录 CMakeLists.txt 文件
    第一次在父目录 MY_VAL=666
    进入子目录 src/CMakeLists.txt 文件
    在子目录,MY_VAL=666
    退出子目录
    第二次在父目录,MY_VAL=777</code></span>

    可以看出在第二次回到父目录时,MY_VAL 的值已经变成了 777。同理,对于 function() 最开始的结论也适用。代码如下:

    <span style="color:#000000"><code><span style="color:#0000ff">cmake_minimum_required</span>(VERSION 3.10)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"父目录 CMakeLists.txt 文件"</span>)
    <span style="color:#0000ff">set</span>(MY_VAL <span style="color:#a31515">"666"</span>)
    
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第一次在父目录 MY_VAL=${MY_VAL}"</span>)
    
    <span style="color:#008000"># 函数定义</span>
    <span style="color:#0000ff">function</span>(xyz test_VAL) <span style="color:#008000"># 函数定义处!</span>
     <span style="color:#0000ff">set</span>(MY_VAL <span style="color:#a31515">"888"</span> PARENT_SCOPE)
     <span style="color:#0000ff">message</span>(<span style="color:#a31515">"functions is MY_VAL=${MY_VAL}"</span>)
    <span style="color:#0000ff">endfunction</span>(xyz)
    
    xyz(<span style="color:#008000">${MY_VAL}</span>) <span style="color:#008000"># 调用函数</span>
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第二次在父目录,MY_VAL=${MY_VAL}"</span>)
    </code></span>

    运行结果:

    <span style="color:#000000"><code>父目录 CMakeLists.txt 文件
    第一次在父目录 MY_VAL=666
    functions is MY_VAL=666
    第二次在父目录,MY_VAL=888</code></span>

    可以看出在该函数中使用 MY_VAL 这个变量值,其实就是一份父目录变量的值拷贝,此时打印值为 666。在 函数中修改值,那么也是用 set(${MY_VAL} 888 PARENT_SCOPE)。此时,退出函数第二次打印变量值时。该值就是在函数中修改好的值 888。 本质讲,对于 function() 而言,刚刚说到的父目录其实不是严格正确的。因为函数定义可以是在其他 .cmake 模块文件中定义的。也可以在其他 CMakeLists.txt 文件中调用,因此准确的说,这里的父目录应该改为『调用函数的地方所属的 CMakeLists.txt 』,我们做的这个实验是在根目录 CMakeLists.txt 文件中定义了函数,又在本文件中使用了。因此之前的说法理解其意思即可。对于 add_subdirectory() 而言,其实也是说调用的地方。下面的 include()、macro() 例子会涉及到,将 function() 放在一个外部的 .cmake 文件中。那里也会说明 function() 与 macro() 的不同。

    (2)、包含 include()、macro() (本质有点类似 c 中的 #include 预处理含义)

    现在在上面的根目录中加入了一个 cmake_modules 目录。目录中有一个 Findtest.cmake 文件。新的目录结构如下:

    <span style="color:#000000"><code><span style="color:#2b91af">$</span> tree
    .
    ├── CMakeLists.txt
    ├── cmake_modules
    │   └── Findtest.cmake
    └── src
        └── CMakeLists.txt
    
    2 directories, 3 files</code></span>

    在根目录中的 CMakeLists.txt 文件包含的代码如下:

    <span style="color:#000000"><code><span style="color:#0000ff">cmake_minimum_required</span>(VERSION 3.10)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"父目录 CMakeLists.txt 文件"</span>)
    <span style="color:#0000ff">set</span>(MY_VAL <span style="color:#a31515">"666"</span>)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第一次在父目录 MY_VAL=${MY_VAL}"</span>)
    
    <span style="color:#008000"># 使用 include() 文件的宏</span>
    list(APPEND CMAKE_MODULE_PATH <span style="color:#008000">${PROJECT_SOURCE_DIR}</span>/cmake_modules) 
    <span style="color:#0000ff">include</span>(Findtest) <span style="color:#008000"># 从 CMAKE_MODULE_PATH 包含的路径中搜索 Findtest.cmake 文件</span>
    <span style="color:#008000">#test(${MY_VAL}) # 调用宏</span>
    <span style="color:#008000">#xyz(${MY_VAL}) # 调用函数</span>
    
    <span style="color:#008000">#find_package(test REQUIRED) # 从 CMAKE_MODULE_PATH 包含的路径中搜索 Findtest.cmake 文件 与 include () 两者的效果是一样的!</span>
    
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第二次在父目录,MY_VAL=${MY_VAL}"</span>)
    
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"include test=${test_VAL}"</span>) 
    <span style="color:#008000">#message("macro_val=${macro_val}")</span></code></span>

    cmake_modules/Findtest.cmake 文件内容如下:

    <span style="color:#000000"><code><span style="color:#008000"># 该文件定义了一个函数以及一个宏</span>
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"进入 Findtest.cmake 文件"</span>)
    
    <span style="color:#0000ff">set</span>(test_VAL <span style="color:#a31515">"222"</span>) <span style="color:#008000"># 验证根目录 CMake 文件能够访问这个变量</span>
    <span style="color:#0000ff">set</span>(MY_VAL <span style="color:#a31515">"000"</span>) <span style="color:#008000"># 测试 include() 效果</span>
    
    <span style="color:#008000"># 宏定义</span>
    <span style="color:#0000ff">macro</span>(test MY_VA) <span style="color:#008000"># 定义一个宏!</span>
     <span style="color:#0000ff">set</span>(macro_val <span style="color:#a31515">"1"</span>) <span style="color:#008000"># 宏内部定义变量</span>
     <span style="color:#0000ff">message</span>(<span style="color:#a31515">"macro is MY_VAL=${MY_VA}"</span>)
     <span style="color:#0000ff">set</span>(MY_VAL <span style="color:#a31515">"999"</span>) <span style="color:#008000"># 直接修改的就是调用该宏所处的文件中的 Normal 变量</span>
    <span style="color:#0000ff">endmacro</span>(test)
    
    <span style="color:#008000"># 函数定义</span>
    <span style="color:#0000ff">function</span>(xyz test_VAL)
     <span style="color:#0000ff">set</span>(MY_VAL <span style="color:#a31515">"888"</span> PARENT_SCOPE)  <span style="color:#008000"># 修改 调用者的 变量</span>
     <span style="color:#0000ff">message</span>(<span style="color:#a31515">"function is MY_VAL=${MY_VAL}"</span>)
    <span style="color:#0000ff">endfunction</span>(xyz)</code></span>

    运行结果:

    <span style="color:#000000"><code><span style="color:#2b91af">$</span> cmake .
    父目录 CMakeLists.txt 文件
    第一次在父目录 MY_VAL=666
    进入 Findtest.cmake 文件
    第二次在父目录,MY_VAL=000
    include test=222</code></span>

    从结果可以看出,include() 内部是可以修改调用者 MY_VAL 变量。include() 包含的文件内定义的变量 test_VAL,也可以在调用 include() 的 CMakeLists.txt 文件中直接访问,同样的对于 macro() 也适用,在根目录 CMake 文件中调用宏,即取消 test(MYVAL)以及message("macroval=MYVAL)以及message("macroval={macro_val}") 部分的注释,此时最后输出结果 :

    <span style="color:#000000"><code><span style="color:#2b91af">$</span> cmake .
    父目录 CMakeLists.txt 文件
    第一次在父目录 MY_VAL=666
    进入 Findtest.cmake 文件
    macro is MY_VAL=000
    第二次在父目录,MY_VAL=999
    include test=222
    macro_val=1</code></span>

    可以看出,这次输出的结果在第二次进入父目录后,MY_VAL 变量的值就是 999 了。注意到在根目录中 CMakeLists.txt 中 注释语句中有一个 find_package() ,这个和 include() 其实都是一样的结果。

    总结:

    结合 include() 、macro() 最后结果,能够得出一个结论:通过 include() 和 macro() 相当于把这两部分包含的代码直接加入根目录 CMakeLists.txt 文件中去执行,相当于他们是一个整体。因此变量直接都是互通的。这就有点像 C/C++ 中的 #include 包含头文件的预处理过程了。这一点其实与刚开始讲的 function() 、add_subdirectory() 完全不同,在函数以及 add_subdirectory() 中,他们本身就是一个不同的作用域范围,仅仅通过拷贝调用者的 Normal 值(仅仅在调用 add_subdirectory() / function() 之前的 Normal 变量),如果要修改调用者包含的 Normal 变量,那么只能通过 set(MY_VAL "某个值" PARENT_SCOPE)注明我们修改的是调用者 Normal 值。虽然在 C/C++ 中,可以通过指针的方式,通过函数可以修改外部变量值,但是在 CMake 脚本语言中 function() 虽然能够传入形式参数,但是者本质上就是 C/C++ 中的值拷贝。而不是引用。上面所说的 Normal 变量其实就是一个局部变量。

    2、Cache Variables

    相当于一个全局变量,我们在同一个 cmake 工程中都可以使用。Cache 变量有以下几点说明:

    • Cache 变量 CMAKE_INSTALL_PREFIX 默认值是 /usr/local (可以在生成的 CMakeCache.txt 文件中查看),这时候如果我们 在某个 CMakeLists.txt 中,仍然使用 set(CMAKE_INSTALL_PREFIX "/usr"),那么此时我们 install 的时候,CMake 以后面的 /usr 作为 CMAKE_INSTALL_PREFIX 的值,这是因为 CMake 规定,有一个与 Cache 变量同名的 Normal 变量出现时,后面使用这个变量的值都是以 Normal 为准,如果没有同名的 Normal 变量,CMake 才会自动使用 Cache 变量。
    • 所有的 Cache 变量都会出现在 CMakeCache.txt 文件中。这个文件是我们键入 cmake .命令后自动出现的文件。打开这个文件发现,CMake 本身会有一些默认的全局 Cache 变量。例如:CMAKE_INSTALL_PREFIX、CMAKE_BUILD_TYPE、CMAKE_CXX_FLAGSS 等等。可以自行查看。当然,我们自己定义的 Cache 变量也会出现在这个文件中。Cache 变量定义格式为 set(<variable> <value> CACHE STRING INTERNAL)。这里的 STRING可以替换为 BOOL FILEPATH PATH ,但是要根据前面 value 类型来确定。参考
    • 修改 Cache 变量。可以通过 set(<variable> <value> CACHE INSTERNAL FORCE),另一种方式是直接在终端中使用 cmake -D var=value ..来设定默认存在的CMake Cache 变量。

    下面通过一个例子来说明以上三点:

    首先看一下目录树结构:

    <span style="color:#000000"><code><span style="color:#2b91af">$</span> tree
    .
    ├── CMakeLists.txt
    └── src
        └── CMakeLists.txt
    
    1 directory, 2 files</code></span>

    根目录 CMakeLists.txt 文件内容如下:

    <span style="color:#000000"><code><span style="color:#0000ff">cmake_minimum_required</span>(VERSION 3.10)
    <span style="color:#0000ff">set</span>(MY_GLOBAL_VAR <span style="color:#a31515">"666"</span> CACHE <span style="color:#0000ff">STRING</span> INTERNAL )
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第一次在父目录 CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"</span>)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第一次在父目录 MY_GLOBAL_VAR=${MY_GLOBAL_VAR}"</span>)
    <span style="color:#0000ff">add_subdirectory</span>(src)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第二次在父目录 CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"</span>)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第二次在父目录 MY_GLOBAL_VAR=${MY_GLOBAL_VAR}"</span>)
    <span style="color:#0000ff">set</span>(CMAKE_INSTALL_PREFIX <span style="color:#a31515">"-->usr"</span> )
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第三次在父目录 CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"</span>)</code></span>

    src/CMakeLists.txt 文件内容如下:

    <span style="color:#000000"><code><span style="color:#0000ff">cmake_minimum_required</span>(VERSION 3.10)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"子目录,CMAKE_INSTALL_PREFIX=${CMAKE_INSTALL_PREFIX}"</span>)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"子目录,MY_GLOBAL_VAR=${MY_GLOBAL_VAR}"</span>)
    <span style="color:#0000ff">set</span>(CMAKE_INSTALL_PREFIX <span style="color:#a31515">"/usr"</span> CACHE <span style="color:#0000ff">STRING</span> INTERNAL FORCE)
    <span style="color:#0000ff">set</span>(MY_GLOBAL_VAR <span style="color:#a31515">"777"</span> CACHE <span style="color:#0000ff">STRING</span> INTERNAL FORCE )</code></span>

    运行结果:

    <span style="color:#000000"><code><span style="color:#2b91af">$</span> cmake .
    第一次在父目录 CMAKE_INSTALL_PREFIX=/usr/local
    第一次在父目录 MY_GLOBAL_VAR=666
    子目录,CMAKE_INSTALL_PREFIX=/usr/local
    子目录,MY_GLOBAL_VAR=666
    第二次在父目录 CMAKE_INSTALL_PREFIX=/usr
    第二次在父目录 MY_GLOBAL_VAR=777
    第三次在父目录 CMAKE_INSTALL_PREFIX=-->usr</code></span>

    程序说明:首先在根目录中打印一下当前的 Cache 变量 CMAKE_INSTALL_PREFIX 值,主要看看默认值是什么,然后在子目录 src/CMakeLists.txt 中再次打印和修改该 Cache 值,目的是熟悉修改全局 Cache 变量,当返回根目录 CMakeLists.txt 文件中再次执行第二次打印该 Cache 值时,主要看一看在子目录中修改后的效果。接着在根目录中设定一个 CMAKE_INSTALL_PREFIX 的 Normal 同名变量,此时第三次打印 CMAKE_INSTALL_PREFIX 的值,此时是为了证明,当有与 Cache 同名的 Normal 变量出现时,CMake 会优先使用 Normal 属性的值。通过设定 MY_GLOBAL_VAR 主要是为了说明可以自己设定全局 Cache 变量。最后的结果如上面显示,当我们再次执行 cmake . 的时候,程序结果如下:

    <span style="color:#000000"><code><span style="color:#2b91af">$</span> cmake .
    第一次在父目录 CMAKE_INSTALL_PREFIX=/usr
    第一次在父目录 MY_GLOBAL_VAR=777
    子目录,CMAKE_INSTALL_PREFIX=/usr
    子目录,MY_GLOBAL_VAR=777
    第二次在父目录 CMAKE_INSTALL_PREFIX=/usr
    第二次在父目录 MY_GLOBAL_VAR=777
    第三次在父目录 CMAKE_INSTALL_PREFIX=-->usr</code></span>

    可以发现第一次在父目录打印 CMAKE_INSTALL_PREFIX 和 MY_GOLBAL_VAR 时,他们的结果是上次cmake .后生成的值,存储在 CMakeCache.txt 中,自己可以找到,解决方案就是可以把 CMakeCache.txt 文件删除,然后在 cmake .我们以后在实际使用时要注意这个坑。对于修改 Cache 变量的另一种方式就是cmake -D CMAKE_INSTALL_PREFIX=/usr。可以自己验证。这里说一个重要的点,就是在终端中输入的 cmake -D var=value . 如果 CMake 中默认有这个 var Cache 变量,那么此时就是赋值,没有的话,CMake 就会默认创建了一个全局 Cache 变量然后赋值。(tips: $CACHE{VAR}表示获取 CACHE 缓存变量的值)。例子如下:(目录结构同上)

    根目录 CMakeLists.txt :

    <span style="color:#000000"><code><span style="color:#0000ff">cmake_minimum_required</span>(VERSION 3.10)
    <span style="color:#0000ff">set</span>(MY_GLOBAL_VAR <span style="color:#a31515">"666"</span>)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第一次在父目录 MY_GLOBAL_VAR=$CACHE{MY_GLOBAL_VAR}"</span>)
    <span style="color:#0000ff">add_subdirectory</span>(src)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第二次在父目录局部 MY_GLOBAL_VAR=${MY_GLOBAL_VAR}"</span>)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"第二次在父目录全局 MY_GLOBAL_VAR=$CACHE{MY_GLOBAL_VAR}"</span>)</code></span>

    src/CMakeLists.txt :

    <span style="color:#000000"><code><span style="color:#0000ff">cmake_minimum_required</span>(VERSION 3.10)
    <span style="color:#0000ff">message</span>(<span style="color:#a31515">"子目录,MY_GLOBAL_VAR=${MY_GLOBAL_VAR}"</span>)
    <span style="color:#0000ff">set</span>(MY_GLOBAL_VAR <span style="color:#a31515">"777"</span> CACHE <span style="color:#0000ff">STRING</span> INTERNAL FORCE )</code></span>

    运行结果:

    <span style="color:#000000"><code>第一次在父目录 MY_GLOBAL_VAR=8
    子目录,MY_GLOBAL_VAR=666
    第二次在父目录局部 MY_GLOBAL_VAR=666
    第二次在父目录全局 MY_GLOBAL_VAR=777</code></span>

    有了上面的基础,相信这个例子很快能看明白。

    参考:

    1、https://stackoverflow.com/questions/31037882/whats-the-cmake-syntax-to-set-and-use-variables/31044116#31044116

    2、https://stackoverflow.com/questions/3249459/for-the-cmake-include-command-what-is-the-difference-between-a-file-and-a-mod

    3、https://cmake.org/cmake/help/v3.11/command/set.html#set

    展开全文
  • OpenCV免Cmake配制的原理与方法

    千次阅读 2012-08-10 17:13:10
    首先,我想说的是CMake的作用,每个编译器在调用头文件的时候都需要编译器配置好读取头文件(*.h)对应的(*.c||*.cpp)的路径,就像调用math.h你得告诉编译器对应的math.c要去什么地方寻找,但是新下载的OpenCV编译器是...
  • 下面简单介绍Cmake 如何使用find_package命令对外部库进行查找: cmake本身不提供任何关于搜索库的便捷方法,也不会对库本身的环境变量进行设置。它仅仅是按照优先级顺序在指定的搜索路径进行查找Findxxx.cmake文件...
  • 大量开源库需要通过cmake编译后使用,了解cmake的基本指令以及CMakeLists.txt的写法非常重要,其基础是了解编译原理。另外,为了对cmake编译的代码进行调试,需要了解CMakeList.txt的写法。本文将对这些关键问题进行...
  • Cmake

    2019-01-01 21:45:40
    1.CMake编译原理 CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多。CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令编译...
  • 简单的find_package的示例, 一、目录结构如下: 二、代码文件&安装目录: 1、lib目录: demo.h: #include <iostream> #include <string> namespace test{ std::string run();... ...
  • 一、find_package可以解决的问题 当构建一个依赖第三库或外部库的project时(即:project需要链接第三方库或外部库),我们需要知道以下信息: 去哪儿找第三 方库的头文件 .h 对比GCC的 -I 参数 ...
  • CMake快速入门

    2020-01-05 00:24:08
    CMake 是一款跨平台的构建工具. 介绍CMake的基本用法 1 为什么要使用CMake, 了解cmake的运行原理 2 使用CMake编译可执行文件、库 3 编译结果的安装、测试
  • 一、 find_package()的原理解析 这部分暂时搁置,以后补充试验 参考内容: ubuontu16.04安装Opencv库引发的find_package()错误信息处理及其简单使用 cmake教程4(find_package使用) CMake如何查找链接库---find_...
  • cmake教程4(find_package使用)

    万次阅读 多人点赞 2018-06-03 19:34:31
    1. cmake find_package的基本原理 2. 如何编写自己的 cmake module模块 3. 使用cmake find_package 使用不同版本的opencv lib问题(opencv 安装在指定的目录,不是系统的目录) 1. cmake find_package的...
  • CMake知识

    2018-09-14 10:19:52
    1.CMake编译原理 CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多。CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令编译...
  • cmake 工程

    2018-12-20 15:36:58
    1.CMake编译原理 CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多。CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令编译...
  • CMake手册

    2018-11-12 22:47:12
    整理了一些有关cmake的在线文档: ...命令:find_package、check_include_file、check_function_exists解析,以及find_package的原理cmake中查找模块的编写: https://mubu.com/doc/i8w8VAYRg0       ...
  • CMake使用

    2019-04-17 15:42:19
    1.CMake编译原理 CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多。CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令编译...
  • 1、下载、构建boost,然后配置BOOST_ROOT环境变量(建议放在~/.bashrc文件中,然后执行:source ~/.bashrc) 2、下载、构建 、安装glfags,这里放在了/usr/local目录 ...list(APPEND CMAKE_MODULE_PATH
  • cmake中经常使用find_package寻找模块,使用起来非常方便.find_package的原理是什么呢?如果自己写个模块,如何提供给别人使用.如果别人希望用find_package的形式使用你的模块中的库,我该如何用cmake写这个库呢?...
  • C实战:项目构建Make,Automake,CMake

    千次阅读 2015-10-08 20:51:12
    C实战:项目构建Make,Automake,CMake在本系列文章《C实战:强大的程序调试工具GDB》中我们简要学习了流行的调试工具GDB的使用方法。本文继续“C实战”的主题,对同样非常流行的构建工具Make的用法和原理一探究竟,并...
  • cmake中经常使用find_package寻找模块,使用起来非常方便.find_package的原理是什么呢?如何自己写个模块提供给别人使用?如果别人希望用find_package的形式使用你的模块中的库,我该如何用cmake写这个库呢? 这个...
  • CMake学习笔记

    2018-12-02 14:47:12
    1.1 CMake编译原理 CMake是一种跨平台编译工具,比make更为高级,使用起来要方便得多。CMake主要是编写CMakeLists.txt文件,然后用cmake命令将CMakeLists.txt文件转化为make所需要的makefile文件,最后用make命令...
  • cmake学习笔记

    2016-05-20 09:37:37
    CMake是干嘛的,我不详说。我只知道,我写一个程序要将多个文件组织起来并编译。...从原理上讲,CMake只是在帮助我们自动生成MakeFile而己。我们编译程序还是要make一下的。  我写了一个简单

空空如也

1 2 3 4 5 ... 13
收藏数 251
精华内容 100
关键字:

cmake原理