CMake中执行shell命令之execute_process、add_custom_target和add_custom_command
背景
以下情況可能需要在CMake中執行shell腳本:
- cmake未提供的功能而實際構建中又需要時,如獲取Linux發行版本
- 項目構建時需要執行腳本才能完成,如boost構建過程
有的需要shell腳本的返回值,而有的不需要,這個關系不大。本文主要關注的是在cmake中執行shell腳本的方法。
主要涉及三個命令:execute_process、add_custom_target和add_custom_command。
execute_process
通過execute_process方法可以執行多個子進程。
原型如下:
execute_process(COMMAND <cmd1> [<arguments>][COMMAND <cmd2> [<arguments>]]...[WORKING_DIRECTORY <directory>][TIMEOUT <seconds>][RESULT_VARIABLE <variable>][RESULTS_VARIABLE <variable>][OUTPUT_VARIABLE <variable>][ERROR_VARIABLE <variable>][INPUT_FILE <file>][OUTPUT_FILE <file>][ERROR_FILE <file>][OUTPUT_QUIET][ERROR_QUIET][COMMAND_ECHO <where>][OUTPUT_STRIP_TRAILING_WHITESPACE][ERROR_STRIP_TRAILING_WHITESPACE][ENCODING <name>][ECHO_OUTPUT_VARIABLE][ECHO_ERROR_VARIABLE][COMMAND_ERROR_IS_FATAL <ANY|LAST>])命令COMMAND會并行執行,每個子進程的標準輸出映射到下一個進程的標準輸入上,所有進程共用standard error管道。
各選項說明如下:
- COMMAND: 子進程的命令行,直接使用操作系統api執行。可以提供多個command,它們會并行執行。如果需要多個命令順序執行,可以調用execute_process多次
- WORKING_DIRECTORY:在該目錄下執行COMMAND命令
- TIMEOUT:超時時間,過了這個時間,所有子進程會被終止,RESULT_VARIABLE會被設置為“timeout”
- RESULT_VARIABLE:最后一個子進程的返回值(正常是0,異常是其他整數),或者描述發生錯誤的字符串
- RESULTS_VARIABLE:對應于每個子進程的返回值,使用分號分割的列表
- OUTPUT_VARIABLE:對應于standard output的內容
- ERROR_VARIABLE:對應于standard error的內容
- INPUT_FILE:第一個子進程的standard input
- OUTPUT_FILE:最后一個子進程的standard output
- ERROR_FILE:所有子進程的standard error
- OUTPUT_QUIET/ERROR_QUIET:忽略standard output 和 standard error
- COMMAND_ECHO:重顯命令到指定的標準設備,如STDERR、STDOUT、NONE。使用CMAKE_EXECUTE_PROCESS_COMMAND_ECHO變量來修改它的行為
- OUTPUT_STRIP_TRAILING_WHITESPACE/ERROR_STRIP_TRAILING_WHITESPACE:刪除空白字符
- ENCODING:在windows系統上指定進程輸出時的解碼方式,默認是utf-8,其他平臺會忽略該參數
- ECHO_OUTPUT_VARIABLE/ECHO_ERROR_VARIABLE:輸出將被復制,它將被發送到配置的變量中,也會在標準輸出或標準錯誤中,3.18版本支持
- COMMAND_ERROR_IS_FATAL:觸發致命錯誤并終止進程執行,方式取決于參數。ANY表示任意命令執行失敗都觸發,LAST表示最后一個進程執行失敗才觸發,3.19版本支持
示例如下:
cmake_minimum_required(VERSION 3.2)project(cmake_test)execute_process(COMMAND echo "hello world"WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}TIMEOUT 3RESULT_VARIABLE result_varOUTPUT_VARIABLE output_varERROR_VARIABLE error_varOUTPUT_STRIP_TRAILING_WHITESPACEERROR_STRIP_TRAILING_WHITESPACE)message(STATUS "result: ${result_var}") message(STATUS "output: ${output_var}") message(STATUS "error: ${error_var}")輸出如下:
-- result: 0 -- output: hello world -- error:如果要執行一個shell腳本,只需要把echo命令替換為如:bash a.sh 即可。
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...]])目標沒有輸出文件,總是被認為是過時的,可以使用 add_custom_command() 命令生成依賴的文件供 DEPENDS 參數使用。
常用參數說明如下:
- Name:目標名稱
- ALL:說明該目標需要添加到默認目標的構建中,所以命令每次都會被執行。注意Name不能是ALL
- COMMAND:構建時執行的命令,如果指定了多個COMMAND,它們將按順序執行,但不一定組成有狀態shell或批處理腳本。(要運行完整的腳本,可以使用configure_file命令或GENERATE命令來創建它,然后指定一個command來啟動它。)
- COMMENT:注釋信息,會在命令執行前打印出來
- DEPENDS:通常以同一CMakeLists.txt文件中的add_custom_command()命令生成的文件作為依賴,目標構建后依賴會被更為最新
- SOURCES:生成目標所需要的額外的源文件,它們會被添加到IDE項目文件中
- WORKING_DIRECTORY:執行命令的目標,如果是相對目錄,則以當前源文件目錄為基準
從以上說明可以看出,可以直接使用add_custom_target執行shell命令,并且使用DEPENDS可以讓各個目標之間產生關聯。
通常和add_custom_command命令配合使用來產生DEPENDS。
比如我們在編譯boost庫時,需要執行shell命令,示例如下:
add_custom_target(build_boost_libsCOMMAND ./bootstrap.sh --prefix=/usr/local/boostCOMMAND ./b2 link=static runtime-link=static threading=multi --with-system --with-thread --with-filesystemWORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/boost/" COMMENT "begin build boost libs...")這樣,我們自定義了目標build_boost_libs,它是通過兩行命令來構建完成的。
關于add_custom_command,下面介紹。
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])使用命令生成指定的輸出文件。具體參數不再說明,詳情可參考后文資料。
使用示例:
add_custom_command(OUTPUT out.cCOMMAND someTool -i ${CMAKE_CURRENT_SOURCE_DIR}/in.txt-o out.cDEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/in.txtVERBATIM)add_library(myLib out.c)這樣,在生成myLib庫時依賴out.c,而out.c由add_custom_command生成,每次in.txt的變動都會導致add_custom_command中命令的執行。
add_custom_command指定的DEPENDS可以是某個target(通過add_library/add_executable/add_custom_target創建),或者直接是某個文件。
如果add_custom_command命令不指定DEPENDS的話,那么只要沒有這個OUTPUT的文件,都會生成自己并執行command。
構建事件
為庫、可執行文件等目標添加自定義命令,可以在構建目標前或者構建目標后執行一些命令。
要執行的命令會成為目標的一部分,并且只在目標構建時執行,如果目標已經構建完成,這些命令也不會執行。
原型:
add_custom_command(TARGET <target>PRE_BUILD | PRE_LINK | POST_BUILDCOMMAND command1 [ARGS] [args1...][COMMAND command2 [ARGS] [args2...] ...][BYPRODUCTS [files...]][WORKING_DIRECTORY dir][COMMENT comment][VERBATIM] [USES_TERMINAL][COMMAND_EXPAND_LISTS])這樣就為指定的target關聯了要執行的命令,target必須在當前目錄里定義。
命令執行的時機:
- PRE_BUILD:在所有規則執行前執行
- PRE_LINK:在源文件編譯后且鏈接前執行
- POST_BUILD:在所有規則執行后執行命令
其他參數不再說明。
示例:
# 在目標構建完成后執行一些操作 add_executable(myExe myExe.c) add_custom_command(TARGET myExe POST_BUILDCOMMAND someHasher -i "$<TARGET_FILE:myExe>"-o "$<TARGET_FILE:myExe>.hash"VERBATIM)add_custom_target vs add_custom_command
add_custom_target有依賴文件時,經常和add_custom_command的生成文件模式搭配使用。
它們之間的關系比較曖昧,這里說明一下。
當add_custom_target所要生成的target依賴add_custom_command所生成的文件時,這個文件就是一個紐帶。
add_custom_command命令輸出的OUTPUT文件和命令里的command之間的關系是:每當這個文件需要被重新生成時,都會執行這段command。
這個文件會不會被生成,取決于構建的target是否depends這個output文件。
這個文件會不會被重新生成,取決于這個output文件depends的東西變了沒。
上面也有點繞,舉例說明一下:
add_custom_command(OUTPUT config_bootstrapCOMMAND ./bootstrap.sh --prefix=/usr/localWORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/boost/" COMMENT "begin config_bootstrap")add_custom_target(build_boost_libsCOMMAND ./b2 link=static runtime-link=static threading=multi --with-system --with-thread --with-filesystemWORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/boost/" DEPENDS config_bootstrapCOMMENT "begin build_boost_libs")執行流程為:
這個流程與MakeFile決定是否重新編譯目標是一個道理,它會自動識別模塊間的依賴關系,并自己構建需要構建的模塊。
寫cmake的過程,也是告訴cmake模塊間依賴關系的過程。
小結
cmake提供了對執行自定義命令的支持,可以很方便地使用它們執行shell命令。
但它們使用上有一些區別,比如有的會無條件每次執行,有的則依賴于依賴文件是否被更新。
具體使用哪種模式,就看需求了。
參考資料
execute_process
add_custom_target
add_custom_command
總結
以上是生活随笔為你收集整理的CMake中执行shell命令之execute_process、add_custom_target和add_custom_command的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: echart实现custom自定义色块功
- 下一篇: 【CMake】cmake的add_cus