zoukankan      html  css  js  c++  java
  • cmake和make

    学计算机的,在写代码的时候,IDE安装好,环境按着教程配置好,就直接代码了,编辑器的具体原理只是一知半解,现在来系统学习一下,为了方便以后学习HElib!

    make和cmake

    写程序大体步骤为:

    1.用编辑器编写源代码,如.c文件。

    2.用编译器编译代码生成目标文件,如.o。

    3.用链接器连接目标代码生成可执行文件,如.exe。

    gcc

    gcc是GNU Compiler Collection(就是GNU编译器套件),也可以简单认为是编译器,它可以编译很多种编程语言(括C、C++、Objective-C、Fortran、Java等等)。

    gcc如何编译参考:cmake 学习

    当你的程序只有一个源文件时,直接就可以用gcc命令编译它。

    但是当你的程序包含很多个源文件时,用gcc命令逐个去编译时,你就很容易混乱而且工作量大,一个一个编译时就会特别麻烦,于是人们想到,为什么不设计一种类似批处理的程序,来批处理编译源文件呢,于是就有了make工具。它是一个自动化编译工具,你可以使用一条命令实现完全编译。但是你需要编写一个规则文件,make依据它来批处理编译,这个文件就是makefile,所以编写makefile文件也是一个程序员所必备的技能。

    对于一个大工程,编写makefile实在是件复杂的事,于是人们又想,为什么不设计一个工具,读入所有源文件之后,自动生成makefile呢,于是就出现了cmake工具,它能够输出各种各样的makefile或者project文件,从而帮助程序员减轻负担。但是随之而来也就是编写cmakelist文件,它是cmake所依据的规则。所以在编程的世界里没有捷径可走,还是要脚踏实地的。

    所以流程如下:

    cmake使用

    由上面可知,我们想要编译,运行程序,只需要写一个cmakelist文件,用cmake去运行该文件,生成project文件和makefile文件,然后再使用make,运行makefile文件,进行编译。

    我这里实验环境实在Centos8.0下,首先先安装,非常简单:

    yum install -y cmake
    

    然后,接下来主要的就是cmakelist文件的编写,下面学习具体语法:

    单个源文件

    1、准备一下源文件 main.c .该程序的用途是计算一个数的指数幂

    #include <stdio.h>
    #include <stdlib.h>
    
    /**
     * power - Calculate the power of number.
     * @param base: Base value.
     * @param exponent: Exponent value.
     *
     * @return base raised to the power exponent.
     */
    double power(double base, int exponent)
    {
        int result = base;
        int i;
        
        if (exponent == 0) {
            return 1;
        }
        
        for(i = 1; i < exponent; ++i){
            result = result * base;
        }
    
        return result;
    }
    
    int main(int argc, char *argv[])
    {
        if (argc < 3){
            printf("Usage: %s base exponent 
    ", argv[0]);
            return 1;
        }
        double base = atof(argv[1]);
        int exponent = atoi(argv[2]);
        double result = power(base, exponent);
        printf("%g ^ %d is %g
    ", base, exponent, result);
        return 0;
    }
    

    2、编写 CMakeLists.txt 文件,并和main.c放在同一个目录 demo

    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)
    
    # 项目信息
    project (demo)
    
    # 指定生成目标
    add_executable(Demo main.c)
    

    CMakeLists.txt 的语法比较简单,由命令、注释和空格组成,其中命令是不区分大小写的。符号 # 后面的内容被认为是注释。命令由命令名称、小括号和参数组成,参数之间使用空格进行间隔

    对于上面的 CMakeLists.txt 文件,依次出现了几个命令:

    • cmake_minimum_required:指定运行此配置文件所需的 CMake 的最低版本;
    • project:参数值是 demo,该命令表示项目的名称是 demo 。
    • add_executable: 将名为 main.c 的源文件编译成一个名称为 Demo 的可执行文件。

    3、编译

    在当前目录执行 cmake . ,得到 Makefile 后再使用 make 命令编译得到 Demo1 可执行文件

    4、执行

    ./Demo
    

    多个源文件

    当在一个目录下,有多个源文件时,实验目录为demo1

    1、编写源文件

    上面的例子只有单个源文件。现在假如把 power 函数单独写进一个名为 MathFunctions.c 的源文件里,使得这个工程变成如下的形式:

    main.c

    #include "MathFunctions.h"
    
    int main(int argc, char *argv[])
    {
        if (argc < 3){
            printf("Usage: %s base exponent 
    ", argv[0]);
            return 1;
        }
        double base = atof(argv[1]);
        int exponent = atoi(argv[2]);
        double result = power(base, exponent);
        printf("%g ^ %d is %g
    ", base, exponent, result);
        return 0;
    }

    MathFunctions.c

    #include "MathFunctions.h"
    
    /**
     * power - Calculate the power of number.
     * @param base: Base value.
     * @param exponent: Exponent value.
     *
     * @return base raised to the power exponent.
     */
    double power(double base, int exponent)
    {
        int result = base;
        int i;
        
        if (exponent == 0) {
            return 1;
        }
        
        for(i = 1; i < exponent; ++i){
            result = result * base;
        }
    
        return result;
    }

    MathFunctions.h

    #include <stdio.h>
    #include <stdlib.h>
    
    double power(double base, int exponent);

    2、编写CMakeLists.txt 文件,并和源文件放在同一个目录 demo1

    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)
    
    # 项目信息
    project (demo1)
    
    # 指定生成目标
    add_executable(Demo main.c MathFunctions.c)
    

    唯一的改动只是在 add_executable 命令中增加了一个 MathFunctions.c 源文件。这样写当然没什么问题,但是如果源文件很多,把所有源文件的名字都加进去将是一件烦人的工作。更省事的方法是使用 aux_source_directory 命令,该命令会查找指定目录下的所有源文件,然后将结果存进指定变量名。其语法如下:

    aux_source_directory(<dir> <variable>)
    

    因此可以修改为:

    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)
    
    # 项目信息
    project (demo1)
    
    # 查找当前目录下的所有源文件
    # 并将名称保存到 DIR_SRCS 变量
    aux_source_directory(. DIR_SRCS)
    
    # 指定生成目标
    add_executable(Demo ${DIR_SRCS})
    

    这样,CMake 会将当前目录所有源文件的文件名赋值给变量 DIR_SRCS ,再指示变量 DIR_SRCS 中的源文件需要编译成一个名称为 Demo 的可执行文件。

    3、编译和执行

    多个目录,多个源文件

    本实验在demo2目录下:

    现在进一步将 MathFunctions.h 和 MathFunctions.c 文件移动到 math 目录下。

    对于这种情况,需要分别在项目根目录 demo2math 目录里各编写一个 CMakeLists.txt 文件。为了方便,我们可以先将 math 目录里的文件编译成静态库再由 main 函数调用。

    根目录(demo2)中的 CMakeLists.txt

    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)
    
    # 项目信息
    project (demo2)
    
    # 查找当前目录下的所有源文件
    # 并将名称保存到 DIR_SRCS 变量
    aux_source_directory(. DIR_SRCS)
    
    # 添加 math 子目录
    add_subdirectory(math)
    
    # 指定生成目标 
    add_executable(Demo main.c)
    
    # 添加链接库
    target_link_libraries(Demo MathFunctions)

    该文件添加了下面的内容: 第3行,使用命令 add_subdirectory 指明本项目包含一个子目录 math,这样 math 目录下的 CMakeLists.txt 文件和源代码也会被处理 。第6行,使用命令 target_link_libraries 指明可执行文件 main 需要连接一个名为 MathFunctions 的链接库 。

    子目录(math)中的 CMakeLists.txt

    # 查找当前目录下的所有源文件
    # 并将名称保存到 DIR_LIB_SRCS 变量
    aux_source_directory(. DIR_LIB_SRCS)
    
    # 生成链接库
    add_library (MathFunctions ${DIR_LIB_SRCS})

    在该文件中使用命令 add_library 将 src 目录中的源文件编译为静态链接库。

    编译运行:

    自定义编译

    本次实验在demo3下:

    CMake 允许为项目增加编译选项,从而可以根据用户的环境和需求选择最合适的编译方案。

    例如,可以将 MathFunctions 库设为一个可选的库,如果 某选项 为 ON ,就使用该库定义的数学函数来进行运算。否则就调用标准库中的数学函数库。

    只需修改CMakeLists.txt 文件:

    1、主目录下CMakeLists.txt 文件:

    # CMake 最低版本号要求
    cmake_minimum_required (VERSION 2.8)
    
    # 项目信息
    project (demo3)
    
    # 加入一个配置头文件,用于处理 CMake 对源码的设置
    configure_file (
      "${PROJECT_SOURCE_DIR}/config.h.in"
      "${PROJECT_BINARY_DIR}/config.h"
      )
    
    # 是否使用自己的 MathFunctions 库
    option (USE_MYMATH
           "Use provided math implementation" ON)
    
    # 是否加入 MathFunctions 库
    if (USE_MYMATH)
      include_directories ("${PROJECT_SOURCE_DIR}/math")
      add_subdirectory (math)  
      set (EXTRA_LIBS ${EXTRA_LIBS} MathFunctions)
    endif (USE_MYMATH)
    
    # 查找当前目录下的所有源文件
    # 并将名称保存到 DIR_SRCS 变量
    aux_source_directory(. DIR_SRCS)
    
    # 指定生成目标
    add_executable(Demo ${DIR_SRCS})
    target_link_libraries (Demo  ${EXTRA_LIBS})

    其中:

    • 第7行的 configure_file 命令用于加入一个配置头文件 config.h ,这个文件由 CMake 从 config.h.in 生成,通过这样的机制,将可以通过预定义一些参数和变量来控制代码的生成。
    • 第13行的 option 命令添加了一个 USE_MYMATH 选项,并且默认值为 ON 。
    • 第17行根据 USE_MYMATH 变量的值来决定是否使用我们自己编写的 MathFunctions 库。

    2、子目录下CMakeLists.txt 文件:

    # 查找当前目录下的所有源文件
    # 并将名称保存到 DIR_LIB_SRCS 变量
    aux_source_directory(. DIR_LIB_SRCS)
    
    # 生成链接库
    add_library (MathFunctions ${DIR_LIB_SRCS})
    

    3、修改main.c

    让其根据 USE_MYMATH 的预定义值来决定是否调用标准库还是 MathFunctions 库

    #include <stdio.h>
    #include <stdlib.h>
    #include "config.h"
    
    #ifdef USE_MYMATH
      #include "math/MathFunctions.h"
    #else
      #include <math.h>
    #endif
    
    
    int main(int argc, char *argv[])
    {
        if (argc < 3){
            printf("Usage: %s base exponent 
    ", argv[0]);
            return 1;
        }
        double base = atof(argv[1]);
        int exponent = atoi(argv[2]);
        
    #ifdef USE_MYMATH
        printf("Now we use our own Math library. 
    ");
        double result = power(base, exponent);
    #else
        printf("Now we use the standard library. 
    ");
        double result = pow(base, exponent);
    #endif
        printf("%g ^ %d is %g
    ", base, exponent, result);
        return 0;
    }
    

    4、编写config.h.in

    上面的程序值得注意的是第2行,这里引用了一个 config.h 文件,这个文件预定义了 USE_MYMATH 的值。但我们并不直接编写这个文件,为了方便从 CMakeLists.txt 中导入配置,我们编写一个 config.h.in 文件,内容如下:

    #cmakedefine USE_MYMATH
    

    这样 CMake 会自动根据 CMakeLists 配置文件中的设置自动生成 config.h 文件。 

    打包

    配置生成各种平台上的安装包,包括二进制安装包和源码安装包。为了完成这个任务,我们需要用到 CPack ,它同样也是由 CMake 提供的一个工具,专门用于打包。

    1、首先在顶层的 CMakeLists.txt 文件尾部添加下面几行:

    # 构建一个 CPack 安装包
    include (InstallRequiredSystemLibraries)
    set (CPACK_RESOURCE_FILE_LICENSE
      "${CMAKE_CURRENT_SOURCE_DIR}/License.txt")
    set (CPACK_PACKAGE_VERSION_MAJOR "${Demo_VERSION_MAJOR}")
    set (CPACK_PACKAGE_VERSION_MINOR "${Demo_VERSION_MINOR}")
    include (CPack)
    

    上面的代码做了以下几个工作:

    • 导入 InstallRequiredSystemLibraries 模块,以便之后导入 CPack 模块;
    • 设置一些 CPack 相关变量,包括版权信息和版本信息,其中版本信息用了上一节定义的版本号;
    • 导入 CPack 模块。

    2、接下来的工作是像往常一样构建工程,并执行 cpack 命令。

    • 生成二进制安装包:
    cpack -C CPackConfig.cmake

    这 3 个二进制包文件所包含的内容是完全相同的。我们可以执行其中一个。此时会出现一个由 CPack 自动生成的交互式安装界面:

    • 生成源码安装包
    cpack -C CPackSourceConfig.cmake
    

     

    以上学习内容源自:CMake 入门实战

     感谢大佬分享!

    参考

    1、CMake与Make最简单直接的区别

    2、关于gcc、make和CMake的区别

    3、CMake 入门实战

    作者: Pam

    出处: https://www.cnblogs.com/pam-sh/>

    关于作者:网安在读

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出, 原文链接 如有问题, 可邮件(mir_soh@163.com)咨询.

  • 相关阅读:
    [ActionSprit 3.0] FMS直播
    Selenium学习之==>Css Selector使用方法
    Selenium学习之==>Xpath使用方法
    Selenium学习之==>WebDriver驱动对照表
    Selenium学习之==>Selenium介绍
    HTML学习之==>JS
    HTML学习之==>DOM操作
    HTML学习之==>CSS
    HTML学习之==>HTML标签
    Python学习之==>线程&&进程
  • 原文地址:https://www.cnblogs.com/pam-sh/p/14800154.html
Copyright © 2011-2022 走看看