首次接觸 CMake,見識了 CMakeLists.txt 的強大后,趕緊整理出來分享一下。
參考資料:《Cmake 3.6 W3Cschool參考手冊》
本文講述了一個 CMake 項目,在從單文件到多文件,單目錄到多子目錄的代碼演化過程中,CMakeLists.txt 腳本的編寫變化。
本文內(nèi)容都是在 Linux 系統(tǒng)上操作的,如果在 VS Code 和 Visual Status 2019 上操作的話,也是一樣的。
單文件的CMake項目
例子1
創(chuàng)建如下 CMake 項目
目錄結(jié)構(gòu)
[root@localhost cmaketest]# tree
.
├── CMakeLists.txt
└── main.cpp
0 directories, 2 files
[root@localhost cmaketest]#
創(chuàng)建CMake項目
如上,新建一個文件夾存放 CMake 項目,這里是 cmaketest 目錄。在目錄下新建兩個文件:main.cpp、CMakeLists.txt 。
在 main.cpp 中寫上如下代碼(當然你也可以寫其它的,心隨你動,哈哈)
#include <iostream>
int Func_Add(int num1, int num2)
{
int nSum = num1 + num2;
return nSum;
}
int main()
{
int numSum = Func_Add(2, 3);
std::cout << "2 + 3 = " << numSum << std::endl;
return 0;
}
在 CMakeLists.txt 中寫上如下命令
cmake_minimum_required (VERSION 3.8)
PROJECT(calcSum)
ADD_EXECUTABLE(calcSum main.cpp)
這樣一個簡單的 CMake 項目就新建好了。
CMakeLists.txt 中命令解釋:
- cmake_minimum_required (VERSION 3.8)
設置項目所需的 cmake 最低版本為 cmake 3.8 版本。 - PROJECT(calcSum)
為整個項目設置一個名字,這里起名為 calcSum。 - ADD_EXECUTABLE(calcSum main.cpp)
添加一個目標可執(zhí)行文件 calcSum 到項目中,這個可執(zhí)行文件是使用后面指定的 main.cpp 文件生成的。
構(gòu)建和編譯CMake項目
在項目路徑下新建一個 build 目錄,用來存放構(gòu)建和編譯過程中生成的文件。
進入 build 目錄后編譯驗證如下:
[root@localhost cmaketest]# mkdir build
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.7s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 50%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ls
calcSum CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
[root@localhost build]# ./calcSum
2 + 3 = 5
[root@localhost build]#
這就是單個文件的 CMake 項目中 CMakeLists.txt 的編寫。
多個文件的CMake項目
我們開發(fā)一個項目,代碼文件肯定不只有一個 main.cpp 那么簡單??隙〞泻芏嘣次募?。這里先說一下有兩個文件:main.cpp、util.h 的 CMake 項目。
例子2
目錄結(jié)構(gòu)
[root@localhost cmaketest]# tree
.
├── build
├── CMakeLists.txt
├── main.cpp
└── util.h
0 directories, 3 files
[root@localhost cmaketest]#
修改項目
將第一個例子中 main.cpp 中的 Func_Add() 函數(shù)的代碼提取到 util.h 中,main.cpp 中保留剩下的。其它不變。如下
util.h 文件中的代碼:
#ifndef __UTIL_H__
#define __UTIL_H__
#include <iostream>
int Func_Add(int num1, int num2)
{
int nSum = num1 + num2;
return nSum;
}
#endif
main.cpp 文件中的代碼:
#include "util.h"
int main()
{
int numSum = Func_Add(2, 3);
std::cout << "2 + 3 = " << numSum << std::endl;
return 0;
}
編譯項目
現(xiàn)在,不用修改 CMakeLists.txt 文件,直接編譯的話也是OK的:
[root@localhost cmaketest]# rm -rf build/*
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.4s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 50%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ls
calcSum CMakeCache.txt CMakeFiles cmake_install.cmake Makefile
[root@localhost build]# ./calcSum
2 + 3 = 5
原因想必大家也都知道,在預處理的時候,會將 util.h 頭文件中的內(nèi)容整合到 main.cpp 中,這就相當于和例子1一樣了。
但是我們?nèi)粘i_發(fā)時,很多時候都不會將函數(shù)的實現(xiàn),即函數(shù)體放到頭文件中,頭文件中只放函數(shù)的聲明,.cpp 源文件中才放函數(shù)的實現(xiàn)。即如下。
例子3
目錄結(jié)構(gòu):
[root@localhost cmaketest]# tree
.
├── build
├── CMakeLists.txt
├── main.cpp
├── util.cpp
└── util.h
1 directory, 4 files
[root@localhost cmaketest]#
修改項目
新增一個 util.cpp 文件如下:
#include "util.h"
int Func_Add(int num1, int num2)
{
int nSum = num1 + num2;
return nSum;
}
頭文件 util.h 中代碼修改為:
#ifndef __UTIL_H__
#define __UTIL_H__
#include < iostream >
int Func_Add(int num1, int num2);
#endif
編譯項目
[root@localhost cmaketest]# rm -rf build/*
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.5s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 50%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[100%] Linking CXX executable calcSum
CMakeFiles/calcSum.dir/main.cpp.o:在函數(shù)‘main’中:
main.cpp:(.text+0x13):對‘Func_Add(int, int)’未定義的引用
collect2: 錯誤:ld 返回 1
make[2]: *** [calcSum] 錯誤 1
make[1]: *** [CMakeFiles/calcSum.dir/all] 錯誤 2
make: *** [all] 錯誤 2
[root@localhost build]#
可見,我們構(gòu)建項目后,使用 make 命令編譯時,會報錯:main.cpp:(.text+0x13):對‘Func_Add(int, int)’未定義的引用。
這是由于,我們在 CMakeLists.txt 中語句 ADD_EXECUTABLE(calcSum main.cpp) 只指定了一個依賴源文件 main.cpp, 但是現(xiàn)在的話,要想生成 calcSum 可執(zhí)行文件,需要依賴 main.cpp、util.cpp 兩個源文件才可以。
修改CMakeLists.txt
cmake_minimum_required (VERSION 3.8)
PROJECT(calcSum)
ADD_EXECUTABLE(calcSum main.cpp util.cpp)
上面我們將 util.cpp 源文件也指定在依賴項中,用空格分割。
再編譯項目
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/util.cpp.o
[ 66%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ./calcSum
2 + 3 = 5
[root@localhost build]#
發(fā)現(xiàn)編譯成功了。ADD_EXECUTABLE() 的第一個參數(shù)是目標可執(zhí)行文件的名字,后面的為依賴源文件,當編譯需要依賴多個源文件時,可以將依賴的源文件加在后面,源文件和源文件之間用空格分割。
單個子目錄的CMake項目
例子4
我們的項目不可能所有的文件都放在同一個主目錄下,實際開發(fā)中項目都會分很多子目錄,比如下面的
目錄結(jié)構(gòu)
[root@localhost cmaketest]# tree
.
├── build
├── CMakeLists.txt
├── main.cpp
└── src
? ? ? ?├── util.cpp
? ? ? ?└── util.h
2 directories, 4 files
[root@localhost cmaketest]#
修改項目
在項目主目錄下新建一個 src 目錄,然后將文件 util.h 和 util.cpp 文件移動到 src 目錄下。
編譯項目
[root@localhost cmaketest]# rm -rf build/*
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.5s)
CMake Error at CMakeLists.txt:5 (ADD_EXECUTABLE):
Cannot find source file:
util.cpp
Tried extensions .c .C .c++ .cc .cpp .cxx .cu .mpp .m .M .mm .ixx .cppm .h
.hh .h++ .hm .hpp .hxx .in .txx .f .F .for .f77 .f90 .f95 .f03 .hip .ispc
CMake Error at CMakeLists.txt:5 (ADD_EXECUTABLE):
No SOURCES given to target: calcSum
CMake Generate step failed. Build files cannot be regenerated correctly.
[root@localhost build]#
發(fā)現(xiàn)在執(zhí)行 cmake 命令構(gòu)建項目時就報錯了:
CMake Error at CMakeLists.txt:5 (ADD_EXECUTABLE):
? ? Cannot find source file:
? ? ? ? util.cpp
無法找到依賴的源文件 util.cpp,這是由于將 util.cpp 文件移動到 src 后,并沒有在 CMakeLists.txt 中修改依賴源文件的路徑,所以 cmake 編譯器在主目錄下查找時無法找到。
修改CMakeLists.txt文件
cmake_minimum_required (VERSION 3.8)
PROJECT(calcSum)
ADD_EXECUTABLE(calcSum main.cpp src/util.cpp)
構(gòu)建項目
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]#
發(fā)現(xiàn)可以成功建立了。接著我們執(zhí)行 make 編譯項目。
編譯項目
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
/home/fenghx/myCMakePro/cmaketest/main.cpp:1:18: 致命錯誤:util.h:沒有那個文件或目錄
#include "util.h"
^
編譯中斷。
make[2]: *** [CMakeFiles/calcSum.dir/main.cpp.o] 錯誤 1
make[1]: *** [CMakeFiles/calcSum.dir/all] 錯誤 2
make: *** [all] 錯誤 2
[root@localhost build]#
這里又報錯了,無法找到 util.h 頭文件??上攵鸵婚_始找不到 util.cpp 是一樣。我們試著將頭文件加到依賴項中,看行不行。
修改CMakeLists.txt文件
cmake_minimum_required (VERSION 3.8)
PROJECT(calcSum)
ADD_EXECUTABLE(calcSum main.cpp src/util.cpp src/util.h)
重新編譯
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
/home/fenghx/myCMakePro/cmaketest/main.cpp:1:18: 致命錯誤:util.h:沒有那個文件或目錄
#include "util.h"
^
編譯中斷。
make[2]: *** [CMakeFiles/calcSum.dir/main.cpp.o] 錯誤 1
make[1]: *** [CMakeFiles/calcSum.dir/all] 錯誤 2
make: *** [all] 錯誤 2
[root@localhost build]#
發(fā)現(xiàn)這樣還是無法找到 util.h 頭文件,那應該如何才能讓編譯器找到這個頭文件呢,無非就是告訴編譯器這個頭文件的路徑,這就需要用到:include_directories() 了
- include_directories(路徑名稱)
添加一個頭文件路徑到項目構(gòu)建中去。
修改CMakeLists.txt文件
cmake_minimum_required (VERSION 3.8)
PROJECT(calcSum)
INCLUDE_DIRECTORIES(src/)
ADD_EXECUTABLE(calcSum main.cpp src/util.cpp)
如上,我們將相對路徑 src/ 添加到了項目的構(gòu)件中,然后再編譯項目。
編譯項目
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[ 66%] Building CXX object CMakeFiles/calcSum.dir/src/util.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ./calcSum
2 + 3 = 5
[root@localhost build]#
發(fā)現(xiàn)此時就已經(jīng)可以編譯生成可執(zhí)行文件了。
子目錄中有很多的源文件的CMake項目
如果子目錄中有很多的 .cpp 源文件,單單把這么多的源文件都寫到依賴項中,就很費勁了,密密麻麻可讀性也不好,而且如果要新建 .cpp 源文件的話,豈不是每次添加都得編輯一下 CMakeLists.txt 文件,太麻煩了,有沒有辦法解決這個事情呢?
有辦法:
- aux_source_directory(路徑名稱 變量名稱)
搜索指定目錄下的所有源文件,并將他們保存在給定的變量中。
修改CMakeLists.txt文件
cmake_minimum_required (VERSION 3.8)
PROJECT(calcSum)
INCLUDE_DIRECTORIES(src/)
AUX_SOURCE_DIRECTORY(src/ SUB_SOURCE_FILE)
ADD_EXECUTABLE(calcSum main.cpp ${SUB_SOURCE_FILE})
如上,AUX_SOURCE_DIRECTORY() 會搜索 src/ 目錄下的所有源文件,將它們保存到指定的變量 SUB_SOURCE_FILE 中。之后我們直接在 ADD_EXECUTABLE() 中使用這個變量就可以了,使用變量的方法為:==${變量名}==。
編譯項目
[root@localhost cmaketest]# rm -rf build/*
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.5s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[ 66%] Building CXX object CMakeFiles/calcSum.dir/src/util.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ./calcSum
2 + 3 = 5
[root@localhost build]#
可見,在使用 make 編譯的時候確實找到 util.cpp 源文件了。這樣就不用我們每次都要添加依賴的源文件了。是不是很帥。
多個子目錄的CMake項目
再來想想,項目中一般不會只有一個子目錄,往往有很多個子目錄,如果子目錄很多,那只使用 AUX_SOURCE_DIRECTORY() 的話顯然要寫很多行,并且變量也要定義很多,不太好,那該怎么辦呢?有沒有傳遞多個目錄的方法呢?
有,使用 file() 文件操作命令。
- file(GLOB 變量名 路徑1 路徑2 .....)
按照一定規(guī)則搜索給定路徑列表中的所有文件,將文件名都保存在給定的變量中。
修改CMakeLists.txt文件
cmake_minimum_required (VERSION 3.8)
PROJECT(calcSum)
INCLUDE_DIRECTORIES(src/)
FILE(GLOB SOURCE_FILES
${PROJECT_SOURCE_DIR}/*.cpp
${PROJECT_SOURCE_DIR}/src/*.cpp
)
ADD_EXECUTABLE(calcSum ${SOURCE_FILES})
這里的變量 PROJECT_SOURCE_DIR 是 CMake 中的一個變量,代表的是當前項目的根目錄。上面命令是將根目錄下的所有 .cpp 文件和 src/ 目錄下的所有 .cpp 文件都搜索出來,存放到變量 SOURCE_FILES 中,這樣,在命令 ADD_EXECUTABLE() 中就可以直接使用變量 SOURCE_FILES ,而不需要編寫依賴的源文件了。
編譯項目
[root@localhost cmaketest]# rm -rf build/*
[root@localhost cmaketest]# cd build/
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.4s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[ 66%] Building CXX object CMakeFiles/calcSum.dir/src/util.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ./calcSum
2 + 3 = 5
[root@localhost build]#
發(fā)現(xiàn)真的是可以的,完美解決不爽的問題。
需要指定編譯器版本的CMake項目
修改項目
如果將 main.cpp 改為如下:
#include "util.h"
int main()
{
auto numSum = Func_Add(2, 3);
std::cout << "2 + 3 = " << numSum << std::endl;
return 0;
}
我用 auto 自動識別類型定義了變量 numSum,來接受 Func_Add() 返回的值。
編譯項目
[root@localhost cmaketest]# cd build/
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
/home/fenghx/myCMakePro/cmaketest/main.cpp: 在函數(shù)‘int main()’中:
/home/fenghx/myCMakePro/cmaketest/main.cpp:5:7: 錯誤:‘numSum’不是一個類型名
auto numSum = Func_Add(2, 3);
^
/home/fenghx/myCMakePro/cmaketest/main.cpp:6:29: 錯誤:‘numSum’在此作用域中尚未聲明
std::cout << "2 + 3 = " << numSum << std::endl;
^
make[2]: *** [CMakeFiles/calcSum.dir/main.cpp.o] 錯誤 1
make[1]: *** [CMakeFiles/calcSum.dir/all] 錯誤 2
make: *** [all] 錯誤 2
[root@localhost build]#
這里就會報錯,原因是 auto 關鍵字是 C++11 標準里面的,而在不指定 C++ 標準版本的情況下,編譯器是不會執(zhí)行 C++11 標準的。
那如何來設置 C++11 標準呢?有辦法,如下:
- set(CMAKE_CXX_STANDARD 11)
設置C++標準為 C++11
修改CMakeLists.txt文件
cmake_minimum_required (VERSION 3.8)
SET(CMAKE_CXX_STANDARD 11)
PROJECT(calcSum)
INCLUDE_DIRECTORIES(src/)
FILE(GLOB SOURCE_FILES
${PROJECT_SOURCE_DIR}/*.cpp
${PROJECT_SOURCE_DIR}/src/*.cpp
)
ADD_EXECUTABLE(calcSum ${SOURCE_FILES})
編譯項目
[root@localhost build]# rm -rf ./*
[root@localhost build]# ls
[root@localhost build]# cmake ..
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (1.4s)
-- Generating done (0.0s)
-- Build files have been written to: /home/fenghx/myCMakePro/cmaketest/build
[root@localhost build]# make
[ 33%] Building CXX object CMakeFiles/calcSum.dir/main.cpp.o
[ 66%] Building CXX object CMakeFiles/calcSum.dir/src/util.cpp.o
[100%] Linking CXX executable calcSum
[100%] Built target calcSum
[root@localhost build]# ./calcSum
2 + 3 = 5
[root@localhost build]#
如上,完美解決問題。
需要連接動態(tài)庫的CMake項目
另外,如果你要鏈接動態(tài)庫,那么就要使用 target_link_libraries()
- target_link_libraries(目標可執(zhí)行文件名 動態(tài)庫名)
指定要鏈接給目標可執(zhí)行文件的動態(tài)庫
更多的 CMakeLists.txt 編寫技巧,請參考《Cmake 3.6 W3Cschool參考手冊》