ubuntu下dlib编译C++(共享库)及使用,即cmake编译dlib开源文件的步骤和文件结构,

一、cmake的工作机制

        使用CMake很简单。 构建过程是通过创建一个或多个CMakeLists文件(实际上是CMakeLists.txt,但本指南将在大多数情况下脱离扩展)控制在项目的每个目录中。 CMakeLists文件应该包含CMake简单语言的项目描述。 语言表达为一系列命令。 每个命令按照它在CMakeLists文件中出现的顺序进行评估。

        一旦CMake安装在您的系统上,使用它来建立一个项目很容易。 CMake在构建项目时使用了两个主目录:源目录和二进制目录。 源目录是您的项目的源代码所在的位置。 这也是CMakeLists文件被找到的地方。二进制目录是你希望CMake放置生成的目标文件,库和可执行文件的位置。

        每个本地生成器都有一个类cmMakefile的实例,cmMakefile是存储解析CMakeLists文件结果的地方。具体而言,对于项目中的每个目录,都会有一个cmMakefile实例,这就是cmMakefile类通常称为目录的原因。这对于不使用Makefiles的构建系统更为清晰。该实例将包含解析该目录的CMakeLists文件的所有信息。一种考虑cmMakefile类的方法是以一种初始化其父目录中的一些变量的结构,然后在处理CMakeLists文件时填充该结构。读取CMakeLists文件只是CMake按照遇到的顺序执行命令的一个步骤。

        通过上述步骤大概知道了,cmake是如何编译多文件目录的开源库,“cmake”命令就是指向CMakeLists.txt并解析它,根据文件中的命令执行编译过程。

二、ubuntu环境下cmake编译dlib-19.10开源库


按照官方网站中的说明How to compile的解答
1. 在examples目录下新建一个build文件夹,存放编译过程中生成的文件
    mkdir build
    cd build
2. cmake ..
    表示CMakeLists.txt在当前目录的上一级目录。
    命令cmake.. 表示在当前目录的上一级目录(父目录)寻找CMakeLists.txt,并读取该CMakeLists.txt文件,
    并执行文件中的命令。
3. cmake --build . --config Release
    在当前目录(“.”表示当前目录)构建,--build表示构建选项,--config Release表示构建模式在Release下。
    原型是cmake --build <dir> ,意思是在目录dir构建。--config <cfg>,意思是对于多配置工具,请选择配置<cfg>;
    整个命令也可以用make 代替,只是构建模式为默认模式。
    
    而常用的利用多个cpu核编译代码的命令:make -j4(利用cpu的四个核,根据自己的电脑情况),也可以利用cmake命令代       替:cmake --build build -- -j3,意思是在目录build构建项目,并同时利用3个cpu核,一般默认的只用其中一个cpu核。

    经过以上三个步骤,完成了dlib库的源码编译。

    下面我们从CMakeLists.txt中详细分析cmake编译的过程。

    (1) 从第一条命令开始,mkdir build和cd build,就是在 */dlib/examples目录下建立文件夹build,并进入文件夹。
    (2) 第二条命令 cmake .. , 之前也解释了就是在当前目录的上一级目录(父目录,也就是和examples同一级目录)寻找CMakeLists.txt,并解析CMakeLists.txt,主要命令如下:
        1)打开CMakeLists.txt就有一个非常醒目的一句话“THIS IS A TUTORIAL, READ THE COMMENTS !!(这是一个教程,阅读注释)”,因为很重要所以才写的非常醒目。
        2)命令 “cmake_minimum_required(VERSION 2.8.12)    ” 提出了对编译dlib库源码的cmake最低版本的要求,如果低于这个版本,会报错并让你升级cmake。
        3)命令 “add_subdirectory(../dlib dlib_build)” 关于这个命令有必要好好解释一下:
        如果遇到add_subdirectory( source_dir [binary_dir])时,进入目录source_dir对其中的CMakeLists.txt进行解析。


            cmake 官方网站解释:
            向构建添加一个子目录。source_dir指定源CMakeLists_txt和代码文件所在的目录。
            如果它是一个相对路径,它将根据当前目录(典型用法)进行计算,但它也可能是一个绝对路径。binary_dir指定放置
            输出文件的目录。如果它是一个相对路径,它将根据当前输出目录计算,但它也可能是一个绝对路径。如果没有指定
            binary_dir,在展开任何相对路径之前,将使用source_dir的值(典型用法)。CMake将立即处理指定源目录中的
            CMakeLists_txt文件,然后在此命令之外继续处理当前输入文件。


        CMakeLists.txt文件类似代码文件,从上到下顺序执行,而add_subdirectory相当于函数调用,执行source_dir中的CMakeLists.txt。执行完之后跳转回来,
        顺序向下继续执行。
        4)先把当前CMakeLists.txt命令看完。命令 “add_executable(assignment_learning_ex assignment_learning_ex.cpp)”,在此命令之前有一段注释:

扫描二维码关注公众号,回复: 4340965 查看本文章

            # The next thing we need to do is tell CMake about the code you want to
            # compile.  We do this with the add_executable() statement which takes the name
            # of the output executable and then a list of .cpp files to compile.  Here we
            # are going to compile one of the dlib example programs which has only one .cpp
            # file, assignment_learning_ex.cpp.  If your program consisted of multiple .cpp
            # files you would simply list them here in the add_executable() statement.  
            接下来我们需要做的是告诉CMake你要编译的代码。 我们使用add_executable()语句执行此操作,
            该语句获取输出可执行文件的名称,然后是要编译的.cpp文件列表。 这里我们将编译一个dlib示例程序,
            它只有一个.cpp文件,assignment_learning_ex.cpp。 如果您的程序由多个.cpp文件组成,您只需在add_executable()语句中列出它们。

            总之一句话,该命令就是指定生成可执行文件要用到的源码。
        5)命令 “target_link_libraries(assignment_learning_ex dlib::dlib)” ,意思是告诉CMake这个程序assignment_learning_ex依赖于dlib。
    (3)在第三条命令 “add_subdirectory(../dlib dlib_build)” ,相当于函数调用跳转到当前目录(即example文件夹所包含的CMakeLists.txt文件所在目录,命令 “cmake ..” 已经跳转到examples文件夹中的CMakeLists.txt)的上一级目录(父目录)的dlib文件夹,然后解析dlib文件夹中的CMakeLists.txt.

    下面解析dlib文件夹中的CMakeLists.txt.
    里面的命令对比着https://blog.gmem.cc/cmake-study-note的关于cmake的讲解,基本都能够看明白。里面的内容,一般情况下与需要更改。编译dlib库最主要的目的就是使用dlib库,静态库(libdlib.a)或者共享库(动态库,libdilb.so)。而源码中的CMakeLists.txt中设置的是静态库,实现方式是通过命令 “set(BUILD_SHARED_LIBS false)”(第60行左右),可以通过更改false为true来使得编译得到的dlib库为共享库。

三、错误分析 

        而最令人郁闷的是,在编译得到libdlib.so之后(通过cmake-gui,设置了DLIB_USE_CUDA,在后面打勾,说明编译过程中使用了cuda),引用libdlib.so之后,以人脸检测为例使用dnn_face_detection,正常调试通过之后,运行出现 “CUDA NOT ENABLED” 的错误提示。就是这个错误让我百思不得其解,当时对cmake还只是一知半解,明明编译的时候已经使用了cuda,反而提示 “CUDA NOT ENABLED” 。当时,把cuda从8.0升级到了9.2,cudnn相应升级,cuda驱动升级。后来还是提示 “CUDA NOT ENABLED” ,整整折腾了差不多一个星期,中间几次想放弃,但是不甘心,因为感觉它本身不难,肯定是哪里没有学习到(cmake最全的学习资料是第一个链接)。在最后仿照第一个链接中的宏定义和初始值设置,在自己工程的CMakeLists.txt,自己重新定义了DLIB_USE_CUDA:

        # 定义宏MACRO_NAME,注意前面的-D
        add_definitions(-DDLIB_USE_CUDA)
        # 赋值
        option(DLIB_USE_CUDA "DLIB_USE_CUDA" ON)

    再次运行自己的工程,不再提示 “CUDA NOT ENABLED” 但是提示当前cuda驱动不匹配cuda9.2。于是按照https://blog.csdn.net/davidhopper/article/details/81144914的
    第七种方法利用 “7.2通过添加ppa源的方法安装驱动” 成功升级cuda驱动:

        sudo add-apt-repository ppa:graphics-drivers/ppa  
        sudo apt-get update  
        sudo apt-get install nvidia-390 # 此处的390要根据上面查询到的版本号适当更改(查询地址:https://www.geforce.cn/drivers)

    升级之后,再次运行没有出现驱动不匹配,反而出现使用cudaMalloc的时候,提示 “out of memory”(内存不足)。原因是加载每一张图片是有一个要求:

            while(img.size() < 1800*1800)
                pyramid_up(img);

    图片太大,导致分配内存时,出现 “out of memory”,改成 900*900就正常运行。

    结束了。
    
    还是不知道变量 “DLIB_USE_CUDA” 这个变量或者宏在哪里定义的,继续探寻

四、其他关于cmake的变量定义

CMAKE_BUILD_TYPE 可以设置为debug或Release,dlib编译的时候设置为Release,而相应的CMAKE_CXX_FLAGS_DEBUG和CMAKE_CXX_FLAGS_RELEASE 也会被设置(是系统默认的)。

CMAKE_CXX_FLAGS_DEBUG  -g,表示可执行程序包含调试信息。下面是各个选项的含义。

-g 可执行程序包含调试信息
-o 指定输出文件名
-c 只编译不链接

CMAKE_CXX_FLAGS_RELEASE -O3 -DNDBUG,表示开启编译优化,等级为三。

debug模式下,加-Os,速度会快。

Release模式下,加 -O3和不加,有比较显著的差异。

-DNDEBUG 是告诉如G++之类的编译器在每个translation unit中定义macro NDEBUG,进而导致所有assert()都被关闭!

参考资料:
    https://blog.gmem.cc/cmake-study-note                (CMake学习笔记)
    https://blog.csdn.net/qqwangfan/article/details/79093527    (linux下使用CmakeLists.txt生成makefile文件进行编译)
    https://blog.csdn.net/u012150179/article/details/17852273    (CMake 使用方法 & CMakeList.txt)
    https://www.cnblogs.com/tla001/p/6827808.html            (简易cmake多文件多目录工程模板)
    https://www.cnblogs.com/autophyte/p/6147751.html        (使用CMake构建复杂工程)
    https://blog.csdn.net/a794226986/article/details/18616511    (cmake处理多源文件目录的方法)
    https://www.ibm.com/developerworks/cn/linux/l-cn-cmake/        (在 linux 下使用 CMake 构建应用程序)
    https://cmake.org/cmake/help/v3.11/command/add_subdirectory.html?highlight=add_subdirectory
    https://blog.csdn.net/sinat_38431275/article/details/78487370#22-%E8%87%AA%E5%B7%B1%E5%BB%BA%E7%AB%8Bcmake(【译文】Mastering CMake 第二章 开始)
    https://blog.csdn.net/sinat_38431275/article/details/78506051    (【译文】Mastering CMake 第三章 关键概念)
    https://cmake.org/cmake/help/v3.11/manual/cmake.1.html?highlight=options%20native%20options#build-tool-mode(cmake --build . --config Release的官网解释)

  https://blog.csdn.net/yuhengyue/article/details/78626102

猜你喜欢

转载自blog.csdn.net/QTVLC/article/details/83313208