CMake ナニーレベルのチュートリアル (パート 1)

再版

著者: 蘇冰曲

リンク: https://subingwen.cn/cmake/CMake-advanced/

出典: iProgramming の Da Bing の著作権は著者に帰属します。商業転載の場合は作者に許可を、非商業転載の場合は出典を明記してください。


カラーマークダウン

ここに画像の説明を挿入します

Gitee: CMake 乳母レベルのチュートリアル - プログラミングを愛するビッグ C


1.CMakeの概要

CMake はプロジェクト構築ツールであり、クロスプラットフォームです。プロジェクト構築に関して私たちがよく知っているのは Makefile (make コマンドによるプロジェクト構築) です。VS の nmake、Linux 上の GNU make、Qt の qmake など、ほとんどの IDE ソフトウェアは make を統合しています。 makefile を実行すると、通常、makefile は現在のコンパイル プラットフォームに依存しており、makefile を作成する作業負荷は比較的大きく、依存関係を解決するときに間違いを犯しやすいことがわかります。

CMake は上記の問題を解決し、開発者がコンパイル プラットフォームに従ってプロジェクト全体のコンパイル プロセスを指定できるようにし、最終的にユーザーはコンパイルする自动生成本地化的Makefile和工程文件だけで済むmakeため、CMake は Makefile を自動的に生成するツールとみなすことができます。そのコンパイルプロセスは次の図のようになります。

画像-20230309130644912

  • 青い破線は、次を使用してmakefileプロジェクトを構築するプロセスを表します。
  • 赤い実線は、次を使用してcmakeプロジェクトを構築するプロセスを表します。

CMake の機能を紹介した後、その利点をまとめてみましょう。

  • クロスプラットフォーム

  • 大規模なプロジェクトを管理する能力

  • コンパイルのビルドプロセスとコンパイルプロセスを簡素化する

  • 拡張可能: cmake の特定の関数を含むモジュールを作成して cmake 関数を拡張できます。

2.CMakeの使用

CMake大文字、小文字、および大文字と小文字の混合コマンドをサポートします。ファイルを書き込むときに使用するツールに対応するコマンド プロンプトがある場合はCMakeLists.txt、あまり気にせず、そのままにしておいてください。

2.1 注意事項

2.1.1 コメント行

CMake#どこ行注释にでも配置できる を使用します。

# 这是一个 CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.0.0)

2.1.2 コメントブロック

CMake#[[ ]]フォームを使用してください块注释

#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)

2.1 ソースファイルのみ

2.1.1 同じ部屋に住む

  1. 準備作業: テストを容易にするために、ローカル コンピューター上にいくつかのテスト ファイルを準備しました。

    • 追加c

      #include <stdio.h>
      #include "head.h"
      
      int add(int a, int b)
      {
          return a+b;
      }
      
    • サブc

      #include <stdio.h>
      #include "head.h"
      
      // 你好
      int subtract(int a, int b)
      {
          return a-b;
      }
      
    • たくさん。c

      #include <stdio.h>
      #include "head.h"
      
      int multiply(int a, int b)
      {
          return a*b;
      }
      
    • 部門C

      #include <stdio.h>
      #include "head.h"
      
      double divide(int a, int b)
      {
          return (double)a/b;
      }
      
    • head.h

      #ifndef _HEAD_H
      #define _HEAD_H
      // 加法
      int add(int a, int b);
      // 减法
      int subtract(int a, int b);
      // 乘法
      int multiply(int a, int b);
      // 除法
      double divide(int a, int b);
      #endif
      
    • main.c

      #include <stdio.h>
      #include "head.h"
      
      int main()
      {
          int a = 20;
          int b = 12;
          printf("a = %d, b = %d\n", a, b);
          printf("a + b = %d\n", add(a, b));
          printf("a - b = %d\n", subtract(a, b));
          printf("a * b = %d\n", multiply(a, b));
          printf("a / b = %f\n", divide(a, b));
          return 0;
      }
      
  2. 上記のファイルのディレクトリ構造は次のとおりです。

    $ tree
    .
    ├── add.c
    ├── div.c
    ├── head.h
    ├── main.c
    ├── mult.c
    └── sub.c
    
  3. CMakeLists.txtファイルを追加

    上記のソース ファイルが配置されているディレクトリに、次のような新しいファイル CMakeLists.txt を追加します。

    cmake_minimum_required(VERSION 3.0)
    project(CALC)
    add_executable(app add.c div.c main.c mult.c sub.c)
    

    次に、CMakeLists.txt ファイルに追加された 3 つのコマンドを順番に紹介します。

    • cmake_minimum_required:使用する cmake の最小バージョンを指定します

      • オプション、必須ではありません。追加しない場合は警告が表示される場合があります
    • project: プロジェクト名を定義し、プロジェクトのバージョン、プロジェクトの説明、Web ページのアドレス、およびサポートされている言語を指定できます (デフォルトではすべての言語がサポートされています)。これらが必要ない場合は無視できます。プロジェクト名を指定するだけです。

      # PROJECT 指令的语法是:
      project(<PROJECT-NAME> [<language-name>...])
      project(<PROJECT-NAME>
             [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
             [DESCRIPTION <project-description-string>]
             [HOMEPAGE_URL <url-string>]
             [LANGUAGES <language-name>...])
      
    • add_executable: プロジェクトを定義すると、実行可能プログラムが生成されます

      add_executable(可执行程序名 源文件名称)
      
      • ここでの実行可能プログラム名は、projectプロジェクト名とは関係ありません。

      • ;使用可能なスペースまたは間隔が複数ある場合、ソース ファイル名は 1 つまたは複数にすることができます。

        # 样式1
        add_executable(app add.c div.c main.c mult.c sub.c)
        # 样式2
        add_executable(app add.c;div.c;main.c;mult.c;sub.c)
        
  4. CMakeコマンドを実行する

    cmakeCMakeLists.txt ファイルを編集した後、コマンドを実行できます。

    # cmake 命令原型
    $ cmake CMakeLists.txt文件所在路径
    
    $ tree
    .
    ├── add.c
    ├── CMakeLists.txt
    ├── div.c
    ├── head.h
    ├── main.c
    ├── mult.c
    └── sub.c
    
    0 directories, 7 files
    robin@OS:~/Linux/3Day/calc$ cmake .
    

    コマンドを実行すると、CMakeLists.txt内のコマンドが実行されるため、コマンドのパス指定に誤りがないかcmake必ずご確認ください。cmake

    コマンドを実行した後、ソース ファイルが配置されているディレクトリにさらにファイルがあるかどうかを確認します。

    $ tree -L 1
    .
    ├── add.c
    ├── CMakeCache.txt            #  new add file
    ├── CMakeFiles                   #  new add dir
    ├── cmake_install.cmake     #  new add file
    ├── CMakeLists.txt
    ├── div.c
    ├── head.h
    ├── main.c
    ├── Makefile                        # new add file
    ├── mult.c
    └── sub.c
    

    対応するディレクトリにmakefileファイルが生成されていることが確認できます。makeこの時点でコマンドを実行してプロジェクトをビルドし、必要な実行可能プログラムを取得します。

    $ make
    Scanning dependencies of target app
    [ 16%] Building C object CMakeFiles/app.dir/add.c.o
    [ 33%] Building C object CMakeFiles/app.dir/div.c.o
    [ 50%] Building C object CMakeFiles/app.dir/main.c.o
    [ 66%] Building C object CMakeFiles/app.dir/mult.c.o
    [ 83%] Building C object CMakeFiles/app.dir/sub.c.o
    [100%] Linking C executable app
    [100%] Built target app
    
    # 查看可执行程序是否已经生成
    $ tree -L 1
    .
    ├── add.c
    ├── app					# 生成的可执行程序
    ├── CMakeCache.txt
    ├── CMakeFiles
    ├── cmake_install.cmake
    ├── CMakeLists.txt
    ├── div.c
    ├── head.h
    ├── main.c
    ├── Makefile
    ├── mult.c
    └── sub.c
    

    最後に、実行可能プログラムがappコンパイルされます (名前CMakeLists.txtは で指定されます)。

2.1.2 VIPルーム

CMakeLists.txt上記の例から、ファイルが存在するディレクトリでコマンドが実行されると、cmakeいくつかのディレクトリとファイル ( 包括 makefile 文件) が生成されることがわかります。コマンドmakefile文件が実行されるとmake、プログラムはいくつかの中間ファイルとコンパイル プロセス中に実行可能ファイルが作成されます。これにより、プロジェクト ディレクトリ全体が乱雑に見えるため、管理や保守が容易ではありません。現時点では、プロジェクトのソース コードとは関係のない生成されたファイルを、たとえば、このディレクトリに次のような名前を付けますbuild

$ mkdir build
$ cd build
$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/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/bin/c++
-- Check for working CXX compiler: /usr/bin/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/robin/Linux/build

ここでcmakeコマンドはbuildディレクトリ内で実行されますが、CMakeLists.txtファイルはbuildディレクトリの上位ディレクトリにあるため、cmakeコマンドの後に指定するパスは..、つまりカレントディレクトリの上位ディレクトリになります。

コマンドを実行するとbuildディレクトリにmakefileファイルが生成されます

$ tree build -L 1
build
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
└── Makefile

1 directory, 3 files

このように、ディレクトリ内でコマンドをbuild実行してプロジェクトをコンパイルすると、生成された関連ファイルが自然にディレクトリに保存されます。このようにして、渡さ生成されたすべてのファイルはプロジェクトのソース ファイルから完全に分離され、全員が自分の家に戻って自分の母親を探します。makebuildcmakemake

2.2 個人的なカスタマイズ

2.2.1 変数の定義

上記の例では、合計5つのソースファイルが用意されていますが、これら5つのソースファイルを繰り返し利用することを想定すると、その都度名前を直接書き出すのは大変なので、その際に変数を定義する必要があります。名前に対応する文字列が保存され、 cmake で変数を定義するために使用する必要がありますset

# SET 指令的语法是:
# [] 中的参数为可选项, 如不需要可以不写
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
  • VAR:変数名
  • VALUE:変数
# 方式1: 各个源文件之间使用空格间隔
# set(SRC_LIST add.c  div.c   main.c  mult.c  sub.c)

# 方式2: 各个源文件之间使用分号 ; 间隔
set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)
add_executable(app  ${SRC_LIST})

2.2.2 使用する C++ 標準を指定する

C++ プログラムを作成する場合、C++11、C++14、C++17、C++20 などの新機能が使用される場合があるため、コンパイル時にコンパイル コマンドでどの機能を使用するかを指定する必要があります。標準:

$ g++ *.cpp -std=c++11 -o app

上記の例では、パラメータで-std=c++11c++11 標準コンパイラを使用することを指定しています。C++ 標準は、 というマクロに相当しますDCMAKE_CXX_STANDARDCMake で C++ 標準を指定するには 2 つの方法があります。

  1. CMakeLists.txt の set コマンドで指定

    #增加-std=c++11
    set(CMAKE_CXX_STANDARD 11)
    #增加-std=c++14
    set(CMAKE_CXX_STANDARD 14)
    #增加-std=c++17
    set(CMAKE_CXX_STANDARD 17)
    
  2. cmakeコマンドの実行時にこのマクロの値を指定します。

    #增加-std=c++11
    cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=11
    #增加-std=c++14
    cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=14
    #增加-std=c++17
    cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=17
    

    上記例では、CMake以降のパスは実際の状況に応じて適宜変更する必要があります。

2.2.3 出力パスの指定

CMake での実行可能プログラム出力のパスの指定も、 と呼ばれるマクロに対応しておりEXECUTABLE_OUTPUT_PATH、その値は引き続きsetコマンドによって設定されます。

set(HOME /home/robin/Linux/Sort)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
  • 1行目: 絶対パスを格納する変数を定義します。
  • 2行目:結合したパスの値をEXECUTABLE_OUTPUT_PATHマクロ に設定
    • このパスにサブディレクトリが存在しない場合は、手動で作成しなくても自動的に生成されます。

cmakeコマンドで生成したmakefileを基に実行プログラムを取得し、makeコマンドで実行するため、ここで実行プログラムの生成パスを指定する際に相対パス「./xxx/xxx」を使用した場合、このパス内の./ Makefile ファイルが配置されているディレクトリに対応します。

2.3 ファイルの検索

プロジェクト内にソースファイルが多数ある場合、CMakeLists.txtファイルを記述するときにプロジェクトディレクトリ内のすべてのファイルを 1 つずつ一覧表示することは不可能であり、あまりにも面倒で現実的ではありません。したがって、CMake はファイルを検索するためのコマンドを提供しており、aux_source_directoryそのコマンドまたはfileコマンドを使用できます。

2.3.1 方法 1

CMake でコマンドを使用してaux_source_directory、特定のパスの下を検索します所有源文件。コマンドの形式は次のとおりです。

aux_source_directory(< dir > < variable >)
  • dir: 検索するディレクトリ
  • variable:dirディレクトリから検索したソースファイルのリストをこの変数に格納します
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
# 搜索 src 目录下的源文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
add_executable(app  ${SRC_LIST})

2.3.2 方法 2

プロジェクト内にソースファイルが多数存在する場合、CMakeLists.txtプロジェクトディレクトリ内のファイルを一つ一つ列挙してファイルを記述するのは不可能であり、非常に面倒です。したがって、CMake はファイルを検索するコマンドを提供しますfile(当然,除了搜索以外通过 file 还可以做其他事情)

file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
  • GLOB: 指定したディレクトリ内で検索された条件を満たすすべてのファイル名のリストを生成し、変数に格納します。
  • GLOB_RECURSE: 指定したディレクトリを再帰的に検索し、検索された条件を満たすファイル名のリストを生成し、変数に格納します。

カレントディレクトリのsrcディレクトリ内のすべてのソースファイルを検索し、変数に格納します。

file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
  • CMAKE_CURRENT_SOURCE_DIRマクロは、現在アクセスされている CMakeLists.txt ファイルが配置されているパスを示します。

  • 検索するファイル パスとタイプには二重引用符を追加するかどうかを指定できます。

    file(GLOB MAIN_HEAD "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
    

2.4 ヘッダー ファイルをインクルードする

プロジェクトのソース ファイルをコンパイルする場合、コンパイラがコンパイル プロセス中にこれらのヘッダー ファイルを見つけてコンパイルをスムーズに完了できるように、ソース ファイルに対応するヘッダー ファイルのパスを指定することが必要になることがよくあります。CMake に含めるディレクトリの設定も非常に簡単で、次の 1 つのコマンドで実行できますinclude_directories

include_directories(headpath)

たとえば、ソース ファイルがいくつかあり、それらのディレクトリ構造は次のとおりです。

$ tree
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h
└── src
    ├── add.cpp
    ├── div.cpp
    ├── main.cpp
    ├── mult.cpp
    └── sub.cpp

3 directories, 7 files

CMakeLists.txtファイルの内容は次のとおりです。

cmake_minimum_required(VERSION 3.0)
project(CALC)
set(CMAKE_CXX_STANDARD 11)
set(HOME /home/robin/Linux/calc)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin/)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(app  ${SRC_LIST})

このうち 6 行目はヘッダー ファイルへのパスを指定しており、PROJECT_SOURCE_DIRマクロに対応する値は cmake コマンドを使用するときに続くディレクトリ (通常はプロジェクトのルート ディレクトリ) です。

2.5 ダイナミックライブラリまたはスタティックライブラリの作成

場合によっては、実行可能プログラムを生成するために作成したソース コードをコンパイルする必要はありませんが、サードパーティで使用するための静的ライブラリまたは動的ライブラリを生成する必要があります。以下では、cmake でこれら 2 種類のライブラリ ファイルを生成する方法について説明します。

2.5.1 静的ライブラリの作成

  • 静的

cmake で静的ライブラリを作成する場合、使用する必要があるコマンドは次のとおりです。

add_library(库名称 STATIC 源文件1 [源文件2] ...) 

Linux では、静的ライブラリの名前は 3 つの部分に分かれています: lib+ 库名字+ .a。ここではライブラリの名前を指定するだけで済み、他の 2 つの部分はファイルの生成時に自動的に入力されます。

Windows のライブラリ名は Linux の形式とは異なりますが、名前を指定するだけで済みます。

src以下にディレクトリがあります。ディレクトリ内のソース ファイルを静的ライブラリにコンパイルして使用する必要があります。

$ tree
.
├── build
├── CMakeLists.txt
├── include               # 头文件目录
│   └── head.h
├── main.cpp            # 用于测试的源文件
└── src                      # 源文件目录
    ├── add.cpp
    ├── div.cpp
    ├── mult.cpp
    └── sub.cpp

上記のディレクトリ構造によれば、CMakeLists.txtファイルは次のように記述できます。

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(calc STATIC ${SRC_LIST})

これにより、最終的に、対応する静的ライブラリ ファイルが生成されますlibcalc.a

2.5.2 動的ライブラリの作成

  • 共有

cmake でダイナミック ライブラリを作成する場合、使用する必要があるコマンドは次のとおりです。

add_library(库名称 SHARED 源文件1 [源文件2] ...) 

Linux では、ダイナミック ライブラリ名は 3 つの部分に分かれています: lib+ 库名字+ .so。ここでは、ライブラリの名前を指定するだけで済みます。他の 2 つの部分は、ファイルの生成時に自動的に入力されます。

Windows のライブラリ名は Linux の形式とは異なりますが、名前を指定するだけで済みます。

上記のディレクトリ構造によれば、CMakeLists.txtファイルは次のように記述できます。

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(calc SHARED ${SRC_LIST})

これにより、最終的に、対応するダイナミック ライブラリ ファイルが生成されますlibcalc.so

2.5.3 出力パスの指定

方法 1 - 動的ライブラリの場合

生成されたライブラリファイルは、実行可能プログラムと同様に出力パスを指定できます。由于在Linux下生成的动态库默认是有执行权限的, したがって、実行可能プログラムを生成するのと同じ方法で生成されるディレクトリを指定できます。

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库生成路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(calc SHARED ${SRC_LIST})

この方法では、実際にsetコマンドでマクロのEXECUTABLE_OUTPUT_PATHパスを設定します。このパスが実行可能ファイルが生成されるパスになります。

方法 2 - 両方に適用されます

Linux で生成された静的ライブラリにはデフォルトで実行権限がないため、静的ライブラリを生成するパスを指定するときにマクロを使用することはできません。代わりにマクロを使用する必要がありますEXECUTABLE_OUTPUT_PATHLIBRARY_OUTPUT_PATHこのマクロは、静的ライブラリ ファイルと動的ライブラリ ファイルの両方に適用できます。

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 生成动态库
#add_library(calc SHARED ${SRC_LIST})
# 生成静态库
add_library(calc STATIC ${SRC_LIST})

2.6 ライブラリファイルをインクルードする

プログラムを作成するプロセスでは、システムによって提供されるいくつかの動的ライブラリ、または自分で作成した動的ライブラリまたは静的ライブラリ ファイルを使用できます。また、cmake は動的ライブラリをロードするための関連コマンドも提供します。

2.6.1 リンク静的ライブラリ

src
├── add.cpp
├── div.cpp
├── main.cpp
├── mult.cpp
└── sub.cpp

次に、src上記のディレクトリ内のファイルをadd.cpp、div.cpp、mult.cpp、sub.cpp静的ライブラリ ファイルにコンパイルしますlibcalc.a

コマンドによる静的リンク ライブラリの作成と使用

テストのディレクトリ構造は次のとおりです。

$ tree 
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h
├── lib
│   └── libcalc.a     # 制作出的静态库的名字
└── src
    └── main.cpp

4 directories, 4 files

cmake で静的ライブラリをリンクするコマンドは次のとおりです。

link_libraries(<static lib> [<static lib>...])
  • パラメータ1 : リンクするスタティックライブラリ名を指定
    • フルネームでも可libxxx.a
    • lib頭をつまんで( )、尻尾を外した.a後の名前( )ともなるxxx
  • パラメータ 2-N : リンクする他の静的ライブラリの名前

静的ライブラリがシステムによって提供されていない場合 (自分で作成するか、サードパーティが提供する静的ライブラリを使用する)、静的ライブラリが見つからない可能性があります。このとき、静的ライブラリのパスを指定することもできます。

link_directories(<lib path>)

このようにして、変更されたCMakeLists.txtファイルの内容は次のようになります。

cmake_minimum_required(VERSION 3.0)
project(CALC)
# 搜索指定目录下源文件
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(calc)
add_executable(app ${SRC_LIST})

コードの8行目を追加すると、パラメーターで指定されたパスに従って静的ライブラリを見つけることができます。

2.6.2 動的ライブラリのリンク

プログラムを作成する過程では、静的ライブラリをプロジェクトに導入するだけでなく、標準またはサードパーティの動的ライブラリも使用することが多くなりますが、動的ライブラリと静的ライブラリでは作成方法、使用方法、読み込み方法が異なります。 , ここでは詳細は説明しませんが、疑問がある場合はLinuxの静的ライブラリと動的ライブラリを参照してください。

cmakeダイナミック ライブラリをリンクするコマンドは次のとおりです

target_link_libraries(
    <target> 
    <PRIVATE|PUBLIC|INTERFACE> <item>... 
    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...) 
  • target : ダイナミック ライブラリをロードするファイルの名前を指定します

    • ファイルはソース ファイルである可能性があります
    • このファイルはダイナミック ライブラリ ファイルである可能性があります
    • ファイルは実行可能ファイルである可能性があります
  • PRIVATE|PUBLIC|INTERFACE : 動的ライブラリのアクセス権限。デフォルトは次のとおりです。PUBLIC

    • 各ダイナミック ライブラリ間に依存関係がない場合は、設定を行う必要はなく、3 つに違いはありません。通常は指定する必要はなく、デフォルトの PUBLIC を使用します。

    • 动态库的链接具有传递性ダイナミック ライブラリ A がダイナミック ライブラリ B と C をリンクし、ダイナミック ライブラリ D がダイナミック ライブラリ A をリンクする場合、ダイナミック ライブラリ D はダイナミック ライブラリ B と C をリンクすることと同等であり、ダイナミック ライブラリ B と C で定義されているメソッドを使用できます。

      target_link_libraries(A B C)
      target_link_libraries(D A)
      
      • PUBLIC: public の背後にあるライブラリは前のターゲットにリンクされ、内部のシンボルもエクスポートされて第三者に提供されます。

      • PRIVATE: private の背後にあるライブラリは、前のターゲットにリンクされて終了するだけであり、サードパーティがどのライブラリを調整したかを検出することはできません。

      • INTERFACE: インターフェイスの後に導入されたライブラリは前のターゲットにリンクされず、シンボルのみがエクスポートされます。

リンクシステムダイナミックライブラリ

動的ライブラリのリンクは、静的ライブラリのリンクとはまったく異なります。

  • 静的ライブラリは、実行可能プログラムを生成するリンク段階で実行可能プログラムにパッケージ化されるため、実行可能プログラムが起動し、静的ライブラリがメモリにロードされます。
  • 動的ライブラリは、実行可能プログラム生成のリンク段階では実行可能プログラムにパッケージ化されません。実行可能プログラムが開始され、動的ライブラリ内の関数が呼び出されるときに、動的ライブラリはメモリにロードされます。

そのため、cmakeリンク先のダイナミックライブラリを指定する際には、应该将命令写到生成了可执行文件之后:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 添加并指定最终生成的可执行程序名
add_executable(app ${SRC_LIST})
# 指定可执行程序要链接的动态库名字
target_link_libraries(app pthread) 

target_link_libraries(app pthread):

  • app:最終的に生成される実行可能プログラムの名前に対応します

  • pthread: 実行可能プログラムによってロードされるダイナミック ライブラリです。このライブラリはシステムによって提供されるスレッド ライブラリです。通常、完全名は切り詰められ ( libpthread.solib)、末尾が付けられます (.so)。

サードパーティの動的ライブラリをリンクする

さて、私は自分でダイナミックライブラリを生成しました。対応するディレクトリ構造は次のとおりです。

$ tree 
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h            # 动态库对应的头文件
├── lib
│   └── libcalc.so        # 自己制作的动态库文件
└── main.cpp              # 测试用的源文件

3 directories, 4 files 

テストファイルでmain.cppは、自作のダイナミックライブラリlibcalc.soとシステムが提供するスレッドライブラリの両方を使用することを想定しており、このときCMakeLists.txtファイルは次のように記述できます。

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)
add_executable(app ${SRC_LIST})
target_link_libraries(app pthread calc) 

6行目pthread、calc実行可能プログラムappがリンクするダイナミックライブラリの名前です。実行可能プログラムappが生成され、ファイルが実行されると、次のエラー メッセージが表示されます。

$ ./app 
./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory 

これは、実行可能プログラムが起動した後、ダイナミック ライブラリをロードするのですが、ダイナミック ライブラリがロードできない問題を解決するために、ダイナミック ライブラリがcalcどこに配置されているかわからないため、ロードに失敗します。の前の実行可能プログラムでは、コマンドを通じてリンクされるダイナミック ライブラリの場所を指定します。このコマンドは、スタティック ライブラリの場所を指定するためにも使用されます。

link_directories(path)

したがって、変更されたCMakeLists.txtファイルは次のようになります。

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 指定源文件或者动态库对应的头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 指定要链接的动态库的路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 添加并生成一个可执行程序
add_executable(app ${SRC_LIST})
# 指定要链接的动态库
target_link_libraries(app pthread calc) 

link_directoriesダイナミックライブラリのパスを指定すると、生成された実行形式プログラムを実行するときにダイナミックライブラリが見つからないという問題は発生しません

注意: 動的ライブラリまたは静的ライブラリ ファイルにリンクするには、target_link_libraries コマンドを使用します。

2.7 ログ

CMake では、ユーザーを使用してメッセージを表示できます。コマンドの名前は次のとおりですmessage

message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
  • (无): 重要なお知らせ
  • STATUS: 重要ではないニュース
  • WARNING: CMake 警告、実行は続行されます
  • AUTHOR_WARNING: CMake 警告 (開発)、実行は続行されます
  • SEND_ERROR: CMake エラー。実行は続行しますが、生成ステップはスキップします
  • FATAL_ERROR: CMake エラーのため、すべての処理を終了します

CMake のコマンド ライン ツールは、メッセージを stdout に表示しSTATUS、他のすべてのメッセージを stderr に表示します。CMake の GUI は、ログ領域にすべてのメッセージを表示します。

CMake の警告メッセージとエラー メッセージのテキストは、単純なマークアップ言語を使用して表示されます。テキストはインデントされず、長さを超える行は折り返され、段落間の区切り文字として新しい行が使用されます。

# 输出一般日志信息
message(STATUS "source path: ${PROJECT_SOURCE_DIR}")
# 输出警告信息
message(WARNING "source path: ${PROJECT_SOURCE_DIR}")
# 输出错误信息
message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}") 

2.8 変数の操作

2.8.1 追加

プロジェクト内のソース ファイルがすべて同じディレクトリにあるとは限りませんが、最終的な実行可能ファイルまたはライブラリ ファイルを生成するには、これらのソース ファイルを一緒にコンパイルする必要があります。コマンドを使用して各ディレクトリ内のソース ファイルを検索する場合file、最後に文字列の結合操作を行う必要があります。文字列の結合にはsetコマンドまたはコマンドを使用できますlist

セット スプライシングを使用する

文字列のスプライシングに set を使用する場合、対応するコマンド形式は次のとおりです。

set(变量名1 ${变量名1} ${变量名2} ...)

上記のコマンドは、実際には第 2 パラメータ以降の文字列をすべて結合し、最終的にその結果を第 1 パラメータに格納するものですが、第 1 パラメータにデータがある場合、元のデータは上書きされます。

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
set(SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}") 

リストのスプライシングを使用する

文字列の結合に list を使用する場合、対応するコマンド形式は次のとおりです。

list(APPEND <list> [<element> ...])

listこのコマンドの機能はsetそれよりも強力です。文字列の連結はその機能の 1 つにすぎません。そのため、最初のパラメータの位置で実行したい操作を指定する必要があります。つまり、データの追加を意味します。次のパラメータAPPENDset、同じ。

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
list(APPEND SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}") 

CMake では、setコマンドを使用して作成できますlistA はlist内部的には で分割された文字列のグループです分号;たとえば、set(var a b c d e)このコマンドは を作成しますlist:a;b;c;d;eが、変数値を出力すると最終的には になりますabcde

set(tmp1 a;b;c;d;e)
set(tmp2 a b c d e)
message(${tmp1})
message(${tmp2})

出力結果:

abcde
abcde

2.8.2 文字列の削除

ディレクトリを検索すると、fileディレクトリ内のすべてのソース ファイルが取得されますが、次のようなソース ファイルの一部は必要なものではありません。

$ tree
.
├── add.cpp
├── div.cpp
├── main.cpp
├── mult.cpp
└── sub.cpp

0 directories, 5 files

現在のディレクトリには 5 つのソース ファイルがあり、その中にはmain.cppテスト ファイルがあります。他の人が使用できるように電卓関連のソース ファイルからダイナミック ライブラリを生成したい場合、必要なのはadd.cpp、div.cp、mult.cpp、sub.cppこれら 4 つのソース ファイルだけです。このとき、検索されたデータを除外する必要main.cppが。この機能を実現するには、次のようにすることもできます。list

list(REMOVE_ITEM <list> <value> [<value> ...])

上記のコマンド プロトタイプから、最初のパラメーターが変更されていることを除けば、データの削除と追加は似ていることがわかりますREMOVE_ITEM

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/*.cpp)
# 移除前日志
message(STATUS "message: ${SRC_1}")
# 移除 main.cpp
list(REMOVE_ITEM SRC_1 ${PROJECT_SOURCE_DIR}/main.cpp)
# 移除后日志
message(STATUS "message: ${SRC_1}") 

ご覧のとおり、第8行削除するファイルの名前を指定するだけですlistただし、file コマンドを使用してソース ファイルを検索すると、ファイルの絶対パスが取得されることに注意してください (リスト内の各ファイルに対応するパスは項目であり、それらはすべて絶対パスです)。削除するときに削除します ファイルの絶対パスを指定する必要があります。指定しないと、削除操作は成功しません。

このコマンドには他にも機能がありますlistが、一般的に使用されるものではないため、ここでは例を挙げて個別に紹介しません。

  1. リストの長さを取得します。

    list(LENGTH <list> <output variable>)
    
    • LENGTH:サブコマンド LENGTH はリストの長さを読み取るために使用されます

    • <list>: 現在の操作のリスト

    • <output variable>: リストの長さを格納するために使用される新しく作成された変数。

  2. リスト内の指定したインデックスにある要素を読み取ります。インデックスは複数指定できます。

    list(GET <list> <element index> [<element index> ...] <output variable>)
    
    • <list>: 現在の操作のリスト

    • <element index>: リスト要素のインデックス

      • 番号付けは 0 から始まり、インデックス 0 の要素がリストの最初の要素になります。
      • インデックスは、-1リストの最後の要素、-2つまりリストの最後から 2 番目の要素などを意味する負の数にすることもできます。
      • インデックス (正または負に関係なく) がリストの長さを超えると、実行時にエラーが報告されます。
    • <output variable>: 指定されたインデックス要素の戻り結果を格納する新しく作成された変数 (リストでもあります)。

  3. コネクタ (文字列) を使用してリスト内の要素を連結し、文字列を形成します

    list (JOIN <list> <glue> <output variable>)
    
    • <list>: 現在の操作のリスト

    • <glue>:指定されたコネクタ(文字列)

    • <output variable>: 返された文字列を格納するために新しく作成された変数

  4. 指定された要素がリスト内に存在するかどうかを調べます。見つからない場合は、-1 を返します。

    list(FIND <list> <value> <output variable>)
    
    • <list>: 現在の操作のリスト

    • <value>: リスト内で検索する必要がある要素

    • <output variable>: 新しく作成された変数

      • <list>listに存在する場合は<value><value>リスト内のインデックスを返します。

      • 見つからない場合は -1 を返します。

  5. リストに要素を追加する

    list (APPEND <list> [<element> ...])
    
  6. リスト内の指定された位置に複数の要素を挿入します

    list(INSERT <list> <element_index> <element> [<element> ...])
    
  7. リストのインデックス 0 に要素を挿入します。

    list (PREPEND <list> [<element> ...])
    
  8. リストから最後の要素を削除します

    list (POP_BACK <list> [<out-var>...])
    
  9. リストから最初の要素を削除します

    list (POP_FRONT <list> [<out-var>...])
    
  10. 指定された要素をリストから削除します

    list (REMOVE_ITEM <list> <value> [<value> ...])
    
  11. 指定されたインデックスにある要素をリストから削除します

    list (REMOVE_AT <list> <index> [<index> ...])
    
  12. 重複した要素をリストから削除する

    list (REMOVE_DUPLICATES <list>)
    
  13. リストの反転

    list(REVERSE <list>)
    
  14. リストの並べ替え

    list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
    
    • COMPARE:並べ替え方法を指定します。次の値が利用可能です。

      • STRING: アルファベット順に並べ替えます。これがデフォルトの並べ替え方法です。
      • FILE_BASENAME: 一連のパス名の場合、basename がソートに使用されます。
      • NATURAL:自然数順に並べ替えます
    • CASE: 大文字と小文字を区別するかどうかを示します。次の値が利用可能です。

      • SENSITIVE: 大文字と小文字を区別して並べ替えます。これがデフォルト値です。
      • INSENSITIVE: 大文字と小文字を区別せずに並べ替えます。
    • ORDER:並び順を指定します。次の値が利用可能です。

      • ASCENDING: 昇順にソート、デフォルト値

      • DESCENDING:降順に並べ替えます

2.9 マクロ定義

プログラムをテストするときは、以下に示すように、コードにいくつかのマクロ定義を追加し、これらのマクロを使用してコードが有効になるかどうかを制御できます。

#include <stdio.h>
#define NUMBER  3

int main()
{
    int a = 10;
#ifdef DEBUG
    printf("我是一个程序猿, 我不会爬树...\n");
#endif
    for(int i=0; i<NUMBER; ++i)
    {
        printf("hello, GCC!!!\n");
    }
    return 0;
} 

マクロの判定はプログラムの7行目で行いDEBUGます マクロが定義されている場合は8行目にログが出力されます マクロが定義されていない場合は8行目はコメントアウトに相当するため、プログラムでは参照できませんend. 入力と出力をログに記録します (このマクロは上記のコードでは定義されていません)。

テストをより柔軟にするために、このマクロをコード内で定義するのではなく、テスト中に定義することができます。1 つの方法は、gcc/g++次のようにコマンドで指定することです。

$ gcc test.c -DDEBUG -o app

gcc/g++コマンドのパラメーターを使用して、-D定義するマクロの名前を指定します。これは、コード内でその名前を使用してマクロを定義するのと同じですDEBUG

CMakeで同様のことを行うこともでき、対応するコマンドは次のように呼ばれますadd_definitions

add_definitions(-D宏名称)

上記のソースファイルに1つ書きますCMakeLists.txt。内容は次のとおりです。

cmake_minimum_required(VERSION 3.0)
project(TEST)
# 自定义 DEBUG 宏
add_definitions(-DDEBUG)
add_executable(app ./test.c) 

このようにして、上記コードの8行目のログを出力することができます。

3. 定義済みマクロ

次のリストは、CMake一般的に使用されるいくつかのマクロをまとめたものです。

大きい 関数
PROJECT_SOURCE_DIR cmake コマンドの直後のディレクトリは、通常、プロジェクトのルート ディレクトリです。
PROJECT_BINARY_DIR cmakeコマンドが実行されるディレクトリ
CMAKE_CURRENT_SOURCE_DIR 現在処理されている CMakeLists.txt が配置されているパス
CMAKE_CURRENT_BINARY_DIR ターゲットコンパイルディレクトリ
EXECUTABLE_OUTPUT_PATH ターゲットのバイナリ実行可能ファイルの保存場所を再定義します [静的ライブラリには適していません]
ライブラリ_出力_パス ターゲットリンクライブラリファイルの格納場所を再定義する
PROJECT_NAME PROJECT ディレクティブを通じて定義されたプロジェクト名を返します。
CMAKE_BINARY_DIR プロジェクトの実際のビルド パス。ビルドがbuildディレクトリで実行されると仮定すると、取得されるパスはこのディレクトリのパスになります。

著者: 蘇冰曲

リンク: https://subingwen.cn/cmake/CMake-advanced/

出典: iProgramming の Da Bing の著作権は著者に帰属します。商業転載の場合は作者に許可を、非商業転載の場合は出典を明記してください。

おすすめ

転載: blog.csdn.net/qq_47355554/article/details/132644405