zoukankan      html  css  js  c++  java
  • cmake的一些基本概念及源码结构

    一、generator

    1、generator的类型

    在每次调用cmake(可执行文件)的时候,会创建一个对应的cmake(源码中的cmake类)实例,并调用这个它的Run接口。从这个类的定义可以看到,它的成员中只有一个
    std::unique_ptr<cmGlobalGenerator> GlobalGenerator;
    实例指针,所以说单次构建只有一个GlobalGenerator。而这个具体是VisualStudio、UnixMakefile、XCode等,这个通常在cmake编译的时候就已经确定。例如,windows下默认是VS、Mach下默认为XCode、而LInux下默认为UnixMakefile。当然,不使用默认的情况下可以通过命令行选项中的-G指定
    -G <generator-name> = Specify a build system generator.
    在cmake的help文档中,还可以看到当前可执行文件支持的所有generator类型以及当前的缺省类型。
    Generators

    The following generators are available on this platform (* marks default):
    * Unix Makefiles = Generates standard UNIX makefiles.
    Green Hills MULTI = Generates Green Hills MULTI files
    (experimental, work-in-progress).
    Ninja = Generates build.ninja files.

    2、cmake类部分代码


    /** \brief Represents a cmake invocation.
    *
    * This class represents a cmake invocation. It is the top level class when
    * running cmake. Most cmake based GUIs should primarily create an instance
    * of this class and communicate with it.
    *
    * The basic process for a GUI is as follows:
    *
    * -# Create a cmake instance
    * -# Set the Home directories, generator, and cmake command. this
    * can be done using the Set methods or by using SetArgs and passing in
    * command line arguments.
    * -# Load the cache by calling LoadCache (duh)
    * -# if you are using command line arguments with -D or -C flags then
    * call SetCacheArgs (or if for some other reason you want to modify the
    * cache), do it now.
    * -# Finally call Configure
    * -# Let the user change values and go back to step 5
    * -# call Generate

    * If your GUI allows the user to change the home directories then
    * you must at a minimum redo steps 2 through 7.
    */

    class cmake
    {
    ……
    std::stack<std::string> CheckInProgressMessages;

    std::unique_ptr<cmGlobalGenerator> GlobalGenerator;
    ……
    }

    3、cmake对脚本的读取

    当globalgenerator执行Configure的时候,它首先(毫无意外的)读取并解析cmake自定义的脚本文件,相关结构为cmMakefile类型
    void cmGlobalGenerator::Configure()
    {
    ……
    auto dirMfu = cm::make_unique<cmMakefile>(this, snapshot);
    auto* dirMf = dirMfu.get();
    this->Makefiles.push_back(std::move(dirMfu));
    dirMf->SetRecursionDepth(this->RecursionDepth);
    this->IndexMakefile(dirMf);

    this->BinaryDirectories.insert(
    this->CMakeInstance->GetHomeOutputDirectory());

    // now do it
    this->ConfigureDoneCMP0026AndCMP0024 = false;
    dirMf->Configure();
    dirMf->EnforceDirectoryLevelRules();

    ……
    }

    二、cmMakefile

    1、Configure流程

    这里可以看到,脚本文件使用的是CMakeLists.txt,接下来会在构建文件夹下创建一个对应的CMakeFiles文件夹。这也意味着:每个有CMakeLists.txt的文件夹,在构建文件夹中必定有一个对应的/CMakeFiles文件夹。所以,CMakeLists.txt所在文件夹的结构决定了构建输出文件夹的框架结构。
    cmake-3.20.6\Source\cmMakefile.cxx
    void cmMakefile::Configure()
    {
    std::string currentStart = cmStrCat(
    this->StateSnapshot.GetDirectory().GetCurrentSource(), "/CMakeLists.txt");

    // Add the bottom of all backtraces within this directory.
    // We will never pop this scope because it should be available
    // for messages during the generate step too.
    this->Backtrace = this->Backtrace.Push(currentStart);

    BuildsystemFileScope scope(this);

    // make sure the CMakeFiles dir is there
    std::string filesDir = cmStrCat(
    this->StateSnapshot.GetDirectory().GetCurrentBinary(), "/CMakeFiles");
    cmSystemTools::MakeDirectory(filesDir);

    assert(cmSystemTools::FileExists(currentStart, true));
    this->AddDefinition("CMAKE_PARENT_LIST_FILE", currentStart);

    cmListFile listFile;
    if (!listFile.ParseFile(currentStart.c_str(), this->GetMessenger(),
    this->Backtrace)) {
    return;
    }
    ……
    }

    三、LocalGenerator

    1、对应的概念

    这个LocalGenerator从概念上理解比较直观,简言之,它对应一个本地构建工具识别的脚本文件。对于Unix这种类型,也就是一个Makefile语法格式的文件。
    cmake-3.20.6\Source\cmGlobalGenerator.cxx
    void cmGlobalGenerator::CreateLocalGenerators()
    {
    this->LocalGeneratorSearchIndex.clear();
    this->LocalGenerators.clear();
    this->LocalGenerators.reserve(this->Makefiles.size());
    for (const auto& m : this->Makefiles) {
    auto lg = this->CreateLocalGenerator(m.get());
    this->IndexLocalGenerator(lg.get());
    this->LocalGenerators.push_back(std::move(lg));
    }
    }

    2、文件名

    这种本地文件,对于unix系统来说,默认就是Makefile,这意味着:对于每个CMakeLists.txt文件,它的构建文件夹中不仅有一个CMakeFiles文件夹,还会有一个Makefile文件。
    void cmLocalUnixMakefileGenerator3::WriteLocalMakefile()
    {
    // generate the includes
    std::string ruleFileName = "Makefile";
    ……
    }

    四、target

    1、从target到cmGeneratorTarget

    一个CMakeLists.txt文件中,可以通过add_library、add_executable命令添加任意多的构建目标。这个在Makefile的语法中,对应一个单独的构建目标。所以,这些target就是在遇到CMakeLists.txt中的add_library、add_executable时创建。
    cmake-3.20.6\Source\cmGlobalGenerator.cxx
    void cmGlobalGenerator::CreateGeneratorTargets(
    TargetTypes targetTypes, cmMakefile* mf, cmLocalGenerator* lg,
    std::map<cmTarget*, cmGeneratorTarget*> const& importedMap)
    {
    if (targetTypes == AllTargets) {
    for (auto& target : mf->GetTargets()) {
    cmTarget* t = &target.second;
    lg->AddGeneratorTarget(cm::make_unique<cmGeneratorTarget>(t, lg));
    }
    }

    for (cmTarget* t : mf->GetImportedTargets()) {
    lg->AddImportedGeneratorTarget(importedMap.find(t)->second);
    }
    }

    2、从target到文件夹

    从这个地方可以看到,每个target在文件夹系统中对应一个${target}.dir格式的文件夹。
    cmake-3.20.6\Source\cmGeneratorTarget.cxx
    std::string cmGeneratorTarget::GetSupportDirectory() const
    {
    std::string dir = cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(),
    "/CMakeFiles/", this->GetName());
    #if defined(__VMS)
    dir += "_dir";
    #else
    dir += ".dir";
    #endif
    return dir;
    }

    3、每个目标使用的本地Makefile

    可以看到,每个目标对应一个build.make文件,这个文件是Makefile格式文件。
    cmake-3.20.6\Source\cmMakefileTargetGenerator.cxx
    void cmMakefileTargetGenerator::CreateRuleFile()
    {
    // Create a directory for this target.
    this->TargetBuildDirectory =
    this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
    this->TargetBuildDirectoryFull =
    this->LocalGenerator->ConvertToFullPath(this->TargetBuildDirectory);
    cmSystemTools::MakeDirectory(this->TargetBuildDirectoryFull);

    // Construct the rule file name.
    this->BuildFileName = cmStrCat(this->TargetBuildDirectory, "/build.make");
    this->BuildFileNameFull =
    cmStrCat(this->TargetBuildDirectoryFull, "/build.make");

    // Construct the rule file name.
    this->ProgressFileNameFull =
    cmStrCat(this->TargetBuildDirectoryFull, "/progress.make");
    ……
    }

    五、举个栗子

    1、源代码project结构

    tsecer@harry: tree
    .
    ├── build
    └── src
    ├── CMakeLists.txt
    ├── foo
    │   └── bar
    │   ├── bar.cpp
    │   └── CMakeLists.txt
    └── main.cpp

    4 directories, 4 files
    tsecer@harry: cat src/CMakeLists.txt
    cmake_minimum_required(VERSION 2.8)
    project(tsecer)
    add_executable(main main.cpp)
    add_subdirectory(foo/bar)
    tsecer@harry: cat src/foo/bar/CMakeLists.txt
    cmake_minimum_required(VERSION 2.8)
    add_library(simple bar.cpp)
    add_library(complex bar.cpp)
    tsecer@harry:

    2、在build文件夹加执行cmake

    tsecer@harry: cd build/
    tsecer@harry: cmake ../src/
    -- The C compiler identification is GNU 7.3.1
    -- The CXX compiler identification is GNU 7.3.1
    -- Check for working C compiler: /usr/lib64/ccache/cc
    -- Check for working C compiler: /usr/lib64/ccache/cc - works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /usr/lib64/ccache/c++
    -- Check for working CXX compiler: /usr/lib64/ccache/c++ - works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /home/harry/study/cmake.gen.layout/build
    tsecer@harry: tree
    .
    ├── CMakeCache.txt
    ├── CMakeFiles
    │   ├── 3.17.5
    │   │   ├── CMakeCCompiler.cmake
    │   │   ├── CMakeCXXCompiler.cmake
    │   │   ├── CMakeDetermineCompilerABI_C.bin
    │   │   ├── CMakeDetermineCompilerABI_CXX.bin
    │   │   ├── CMakeSystem.cmake
    │   │   ├── CompilerIdC
    │   │   │   ├── a.out
    │   │   │   ├── CMakeCCompilerId.c
    │   │   │   └── tmp
    │   │   └── CompilerIdCXX
    │   │   ├── a.out
    │   │   ├── CMakeCXXCompilerId.cpp
    │   │   └── tmp
    │   ├── cmake.check_cache
    │   ├── CMakeDirectoryInformation.cmake
    │   ├── CMakeOutput.log
    │   ├── CMakeTmp
    │   ├── main.dir
    │   │   ├── build.make
    │   │   ├── cmake_clean.cmake
    │   │   ├── DependInfo.cmake
    │   │   ├── depend.make
    │   │   ├── flags.make
    │   │   ├── link.txt
    │   │   └── progress.make
    │   ├── Makefile2
    │   ├── Makefile.cmake
    │   ├── progress.marks
    │   └── TargetDirectories.txt
    ├── cmake_install.cmake
    ├── foo
    │   └── bar
    │   ├── CMakeFiles
    │   │   ├── CMakeDirectoryInformation.cmake
    │   │   ├── complex.dir
    │   │   │   ├── build.make
    │   │   │   ├── cmake_clean.cmake
    │   │   │   ├── cmake_clean_target.cmake
    │   │   │   ├── DependInfo.cmake
    │   │   │   ├── depend.make
    │   │   │   ├── flags.make
    │   │   │   ├── link.txt
    │   │   │   └── progress.make
    │   │   ├── progress.marks
    │   │   └── simple.dir
    │   │   ├── build.make
    │   │   ├── cmake_clean.cmake
    │   │   ├── cmake_clean_target.cmake
    │   │   ├── DependInfo.cmake
    │   │   ├── depend.make
    │   │   ├── flags.make
    │   │   ├── link.txt
    │   │   └── progress.make
    │   ├── cmake_install.cmake
    │   └── Makefile
    └── Makefile

    13 directories, 46 files
    tsecer@harry:

    3、生成构建文件夹结构

    可以看到
    a、生成文件夹的整体结构是源文件的结构的镜像:在源文件中,foo/bar文件夹在构建文件夹中也有。
    b、每个包含CMakeLists.txt文件夹,在对应的构建文件夹中有Makefile和CMakeFiles文件夹。分别对应这个CMakeLists.txt中所有构建指令和构建输出。
    c、在bar文件夹下的CMakeLists.txt中有两个目标:simple和complex,它们在CMakeFiles文件夹中有两个独立的、对应的simple.dir和complex.dir文件夹。这两个文件夹中有该目标的构建入口build.make文件

  • 相关阅读:
    微信OpenID获取
    2015总结及2016目标
    python start
    csv到mysql数据库如何分割
    读书 --- 老码识途
    读书--编写高质量代码 改善C#程序的157个建议2
    读书--编写高质量代码 改善C#程序的157个建议
    BinarySearch
    在aspx中写c#
    AWS 2020 Innovate所有视频
  • 原文地址:https://www.cnblogs.com/tsecer/p/15799504.html
Copyright © 2011-2022 走看看