在很多時候,需要在cmake
中創建一些目標,如clean
、copy
等等,這就需要通過add_custom_target
來指定。同時,add_custom_command
可以用來完成對add_custom_target
生成的target
的補充。
本文實例源碼github地址:https://github.com/yngzMiao/yngzmiao-blogs/tree/master/2019Q4/20191029。
add_custom_target
如果你寫過MakeFile
,那麼一定知道,可以設定很多的目標來make
,如:
target ... : prerequisites ...
command
其中:
- target是下面的命令的目標,即下面命令是爲了
target
而生的。這個目標可以是*.o
文件,也可以是可執行文件; - prerequisites則是生成該目標所依賴的文件,如果找不到依賴的文件,下面的命令就不會執行且會中斷
make
; - command就是生成目標文件的命令,一般就是編譯命令。
那麼,如果使用CMakeLists.txt
如何也生成一個目標來make
呢?
這就是add_custom_target
的用處:增加一個沒有輸出的目標,使得它總是被構建。
add_custom_target(Name [ALL] [command1 [args1...]]
[COMMAND command2 [args2...] ...]
[DEPENDS depend depend depend ... ]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[JOB_POOL job_pool]
[VERBATIM] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS]
[SOURCES src1 [src2...]])
乍一看,該命令有很多的參數,但其實我們並不需要全部瞭解,甚至一般情況下只需要用到其中的兩三個。
例如:
cmake_minimum_required(VERSION 3.0)
project(test)
add_custom_target(CopyTask
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/config ${CMAKE_CURRENT_SOURCE_DIR}/etc
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/log.txt ${CMAKE_CURRENT_SOURCE_DIR}/etc
)
運行該CMakeLists.txt:
mkdir build && cd build
cmake ..
make CopyTask
運行結果爲:
yngzmiao@yngzmiao-virtual-machine:~/test/build$ rm -r *;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/yngzmiao/test/build
yngzmiao@yngzmiao-virtual-machine:~/test/build$ make CopyTask
Scanning dependencies of target CopyTask
Built target CopyTask
命令行的打印信息看不出什麼東西,但是如果根目錄下有config
文件夾,或者有log.txt
文件,那麼文件夾內的文件和log.txt
會被拷貝到etc
文件夾下。
初始目錄結構,如下:
|---build
|---config
|---log.log
|---CMakeLists.txt
|---log.txt
運行後目錄結構,如下:
|---build
|---config
|---log.log
|---etc
|---log.log
|---log.txt
|---CMakeLists.txt
|---log.txt
其實可以看出,這段代碼的目的就是將config
文件夾的內容和log.txt
文件複製到新的etc
文件夾內。
add_custom_target
生成一個目標CopyTask
,該目標是用來複制文件夾或者複製文件的!也就是COMMAND
中定義的操作。
其中:${CMAKE_COMMAND}是CMake的路徑,-E使CMake運行命令而不是構建,copy_directory和copy是cmake_command_line,再後面兩個就是command_line的參數。
當然,生成文件不僅僅只能是複製,還可以是其他的操作。而這些COMMAND操作,都在command_line中規定了。
至於cmake_command_line
的內容,可參考cmake
的官方資料:Run a Command-Line Tool。
該命令的其他一些參數的含義:
- ALL:表明該目標會被添加到默認的構建目標,使得它每次都被運行;
- COMMAND:指定要在構建時執行的命令行;
- DEPENDS:指定命令所依賴的文件;
- COMMENT:在構建時執行命令之前顯示給定消息;
- WORKING_DIRECTORY:使用給定的當前工作目錄執行命令。如果它是相對路徑,它將相對於對應於當前源目錄的構建樹目錄;
- BYPRODUCTS:指定命令預期產生的文件。
add_custom_command
按照官方資料翻譯的話,這個命令的用途是:將自定義構建規則添加到生成的構建系統。
什麼鬼!我猜,你一定和我一樣蒙,完全不太能夠理解是什麼意思。既然無法理解它的語言版本的解釋,那就在具體的例子中進行熟悉。
該命令主要用於兩種場景下:
生成文件
這種場景是:添加定製命令來生成文件。它的命令內容爲:
add_custom_command(OUTPUT output1 [output2 ...]
COMMAND command1 [ARGS] [args1...]``
[COMMAND command2 [ARGS] [args2...] ...]
[MAIN_DEPENDENCY depend]
[DEPENDS [depends...]]
[BYPRODUCTS [files...]]
[IMPLICIT_DEPENDS <lang1> depend1
[<lang2> depend2] ...]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[DEPFILE depfile]
[JOB_POOL job_pool]
[VERBATIM] [APPEND] [USES_TERMINAL]
[COMMAND_EXPAND_LISTS])
例如:
cmake_minimum_required(VERSION 3.0)
project(test)
add_custom_command(OUTPUT COPY_RES
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/config ${CMAKE_CURRENT_SOURCE_DIR}/etc
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/log.txt ${CMAKE_CURRENT_SOURCE_DIR}/etc
)
add_custom_target(CopyTask ALL DEPENDS COPY_RES)
add_custom_target
生成一個目標CopyTask
,該目標依賴於COPY_RES
。而對於COPY_RES
而言,它實際上是用來複制文件夾或者複製文件的!也就是COMMAND
中定義的操作。
該命令的其他一些參數的含義:
- OUTPUT:指定命令預期產生的輸出文件。如果輸出文件的名稱是相對路徑,即相對於當前的構建的源目錄路徑;
- COMMAND:指定要在構建時執行的命令行;
- DEPENDS:指定命令所依賴的文件;
- COMMENT:在構建時執行命令之前顯示給定消息;
- WORKING_DIRECTORY:使用給定的當前工作目錄執行命令。如果它是相對路徑,它將相對於對應於當前源目錄的構建樹目錄;
- DEPFILE:爲生成器指定一個.d depfile .d文件保存通常由自定義命令本身發出的依賴關係;
- MAIN_DEPENDENCY:指定命令的主要輸入源文件;
- BYPRODUCTS:指定命令預期產生的文件。
構建事件
這種場景是:爲某個目標(如庫或可執行程序)添加一個定製命令。
這種定製命令可以設置在,構建這個目標過程中的某些時機。也就是就,這種場景可以在目標構建的過程中,添加一些額外執行的命令。這些命令本身將會成爲該目標的一部分。注意,僅在目標本身被構建過程纔會執行。如果該目標已經構建,命令將不會執行。
那麼這些時機是什麼呢?如下表所示:
參數 | 含義 |
---|---|
PRE_BUILD | 在目標中執行任何其他規則之前運行 |
PRE_LINK | 在編譯源代碼之後,鏈接二進制文件或庫文件之前運行 |
POST_BUILD | 在目標內所有其他規則均已執行後運行 |
其中,PRE_BUILD
只被Visual Studio 7
及之後的版本支持,其他情況下PRE_BUILD
都被視爲PRE_LINK
。
此時的命令爲:
add_custom_command(TARGET <target>
PRE_BUILD | PRE_LINK | POST_BUILD
COMMAND command1 [ARGS] [args1...]
[COMMAND command2 [ARGS] [args2...] ...]
[BYPRODUCTS [files...]]
[WORKING_DIRECTORY dir]
[COMMENT comment]
[VERBATIM] [USES_TERMINAL])
使用方式和第一種場景類似:
cmake_minimum_required(VERSION 3.0)
project(test)
add_custom_target(CopyTask)
add_custom_command(TARGET CopyTask
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/config ${CMAKE_CURRENT_SOURCE_DIR}/etc
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/log.txt ${CMAKE_CURRENT_SOURCE_DIR}/etc
)
這段代碼的功能和上一段是一樣的,將config
文件夾的內容和log.txt
文件複製到新的etc
文件夾內。
需要注意的是,此時add_custom_command需要寫在add_custom_target之後,否則將cmake不通過。
該命令的其他一些參數的含義:
- TARGET:指定命令運行的目標;
- COMMAND:指定要在構建時執行的命令行;
- COMMENT:在構建時執行命令之前顯示給定消息;
- WORKING_DIRECTORY:使用給定的當前工作目錄執行命令。如果它是相對路徑,它將相對於對應於當前源目錄的構建樹目錄;
- BYPRODUCTS:指定命令預期產生的文件。
add_custom_command總結
其實,可以看出儘管官方給了兩種的使用情景,但是本質上沒有什麼區別,區別在於:
- 如果使用OUTPUT參數,需要在目標的構建中指定依賴於該OUTPUT;
- 如果使用TARGET參數,直接指定目標就可以了。
add_custom總結
其實,可以發現,add_custom_command
有時候略顯雞肋,因爲我們可以將所有的COMMAND
都寫到add_custom_target
中,不需要add_custom_command
來進行補充。
相關閱讀
cmake整理:在編譯時拷貝文件之add_custom_comand 和 add_custom_target