CMake筆記:add_custom_command不執(zhí)行

0x00. 前言

在網(wǎng)上看別人做一些手工教程視頻姜性,經(jīng)常能看到這樣的評(píng)論:

腦子:我感覺(jué)我會(huì)了瞪慧。
手:你行你來(lái)。

之前一直通過(guò)編譯腳本去尋找代碼入口部念,感覺(jué)我已經(jīng)懂得CMake的語(yǔ)法了弃酌,直到今天寄己要寫(xiě)一個(gè)腳本去編譯一個(gè)工程才發(fā)現(xiàn)氨菇,事情并不簡(jiǎn)單:腳本并沒(méi)有按照我期望的去執(zhí)行。

此工程需要用到Protocol Buffer妓湘,因此當(dāng)代碼構(gòu)建的時(shí)候需要使用使用Protocol Buffer編譯器去編譯.proto文件獲得對(duì)應(yīng)的生成文件查蓉。理論上,想要達(dá)到這個(gè)目的榜贴,我們只需要在CMakeLists.txt中使用add_custom_command命令就可以可以生成對(duì)應(yīng)的構(gòu)建規(guī)則豌研。但出人意料的是,這條命令并沒(méi)有被執(zhí)行唬党,也就是說(shuō)鹃共,并沒(méi)有編譯.proto文件的規(guī)則生成,因此當(dāng)最終使用Make去構(gòu)建工程的時(shí)候驶拱,沒(méi)能通過(guò).proto文件得到對(duì)應(yīng)的源代碼及汉。

0x01. 踩雷

整個(gè)命令的使用如下面的代碼所示,作用就是將位${REPO_ROOT}/protobuf/onnx-operators-ml.proto以及${REPO_ROOT}/protobuf/onnx-ml.proto這兩個(gè)文件編譯成C++頭文件以及源文件屯烦,并存放到{REPO_ROOT}/src目錄下坷随,其中${REPO_ROOT}是項(xiàng)目的根目錄,例如在我的例子中為/home/sunny/workspace/model-tool/

set(PROTOBUF_PROTOC_EXECUTABLE ${REPO_ROOT}/build/third_party/protobuf/cmake/protoc)

list(APPEND PROTO_FILES 
     "${REPO_ROOT}/protobuf/onnx-operators-ml.proto"
     "${REPO_ROOT}/protobuf/onnx-ml.proto")

set(output_dir ${REPO_ROOT}/include)
set(protoc_include ${REPO_ROOT}/protobuf)

foreach(fil ${PROTO_FILES})
    get_filename_component(abs_fil ${fil} ABSOLUTE)
    get_filename_component(fil_we ${fil} NAME_WE)

    list(APPEND ${srcs_var} "${output_dir}/${fil_we}.pb.cc")
    list(APPEND ${hdrs_var} "${output_dir}/${fil_we}.pb.h")

    add_custom_command(
        OUTPUT "${output_dir}/${fil_we}.pb.cc"
                "${output_dir}/${fil_we}.pb.h"
        COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --cpp_out    ${output_dir} -I${protoc_include} ${abs_fil}
        DEPENDS ${abs_file}
        COMMENT "Running C++ protocol buffer compiler on ${fil}" VERBATIM )
endforeach()

官方文檔中該命令的簽名有兩個(gè)形式驻龟,在開(kāi)源的項(xiàng)目中經(jīng)澄旅迹看到的是下面這個(gè)形式:

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])

從中可以看到,只有OUTPUT以及COMMAND這兩個(gè)參數(shù)是必須的翁狐,也就是說(shuō)类溢,正常情況下只要正確提供了這兩個(gè)參數(shù)的值,在構(gòu)建的時(shí)候肯定會(huì)執(zhí)行這條命令生成的規(guī)則去編譯.proto露懒。但是在我確認(rèn)提供的參數(shù)都沒(méi)問(wèn)題的情況下闯冷,這條命令依舊沒(méi)有按照預(yù)期工作。

這就非常奇怪了懈词,坦白的講蛇耀,我的例子中的命令就是從ONNX Runtime中拷貝過(guò)來(lái),只不過(guò)將一些變量的值修改成了指向我本地機(jī)器中的文件而已坎弯,它在別人的工程中能執(zhí)行纺涤,為什么到了我這就不好使了呢?把可選的參數(shù)嘗試了一遍抠忘,仍然木有結(jié)果撩炊。

我終于意識(shí)到,這樣蠻干是不行的崎脉,即便瞎貓碰上死耗子偶然嘗試對(duì)了一種組合拧咳,我依舊不知道它為什么又行了,回頭再需要編寫(xiě)其他命令的時(shí)候一樣抓瞎囚灼。還是需要去文檔中尋找答案骆膝。

好在砾淌,最終我還是從文檔中悟出了答案。

0x02. 解惑

其實(shí)在官方的文檔中一開(kāi)始就說(shuō)的很明白了谭网,只不過(guò)當(dāng)時(shí)著急汪厨,并沒(méi)有認(rèn)真看對(duì)整個(gè)命令的綜述,而是著急忙慌地去看應(yīng)該怎么去構(gòu)造每個(gè)參數(shù)的值愉择。官方文檔中是這么說(shuō)的:

This defines a command to generate specified OUTPUT file(s). A target created in the same directory (CMakeLists.txt file) that specifies any output of the custom command as a source file is given a rule to generate the file using the command at build time.……In makefile terms this creates a new target in the following form:

OUTPUT: MAIN_DEPENDENCY DEPENDS
        COMMAND

看到這一段話劫乱,我已經(jīng)知道在我的項(xiàng)目中為什么這個(gè)命令不好使了:只有當(dāng)構(gòu)建的目標(biāo)以add_custome_command生成的OUTPUT文件為源代碼的情況下,add_custome_command中指定的命令才會(huì)才會(huì)執(zhí)行锥涕。到目前為止衷戈,我并沒(méi)有在CMakeLists.txt中生成目標(biāo)文件的時(shí)候使用到諸如model-ml.pb.h, model-ml.pb.cc這些文件,也就是說(shuō)當(dāng)構(gòu)建我的代碼的時(shí)候层坠,根本就用不到model-ml.pb.h, model-ml.pb.cc殖妇,既然用不到,那生成它們干啥呢破花?因此“聰明”的構(gòu)建系統(tǒng)就不去執(zhí)行編譯.proto的命令了谦趣。

我們知道,Makefile文件由一系列規(guī)則(rules)構(gòu)成座每,規(guī)則的形式如下所示:

<target> : <prerequisites> 
[tab]  <commands>

根據(jù)我的項(xiàng)目里CMakeLists.txt中的內(nèi)容前鹅,會(huì)生成一個(gè)Makefile文件(Ubuntu中默認(rèn)情況下),其形式大概如下:

model_tool: main.cpp onnx-ml.pb.cc
         C++ -o model_tool main.cpp onnx-ml.pb.cc

onnx-ml.pb.cc: onnx-ml.proto
       protoc --cpp_out ./include -I./protobuf/ ./protobuf/onnx-ml.proto

為了生成model_tool峭梳,需要先生成onnx-ml.pb.cc舰绘,因此需要先執(zhí)行protoc命令。而如果我再CMakeLists.txt中并沒(méi)有將onnx-ml.pb.cc指定為生成model_tool的源文件之一葱椭,所生成的Makefile便會(huì)如下面所示 捂寿,此時(shí)規(guī)則1對(duì)規(guī)則2就不存在依賴(lài)關(guān)系,因此protoc就不會(huì)執(zhí)行了孵运。

model_tool: main.cpp
         C++ -o model_tool main.cpp

onnx-ml.pb.cc: onnx-ml.proto
       protoc --cpp_out ./include -I./protobuf/ ./protobuf/onnx-ml.proto

想讓model_tool對(duì)onnx-ml.pb.cc形成依賴(lài)也很簡(jiǎn)單秦陋,只要在將onnx-ml.pb.cc作為值傳個(gè)最終生成model_tool的命令add_executable就行,如下所示:

list(APPEND CXX_SRCS ${REPO_ROOT}/src/main.cpp
     ${REPO_ROOT}/include/onnx-ml.pb.cc)
add_executable(model_tool ${CXX_SRCS}

我一開(kāi)始就是因?yàn)闆](méi)將onnx-ml.pb.cc也列為生成model_tool的源文件掐松,才導(dǎo)致add_custom_command沒(méi)有效果踱侣。至于main.cpp中是不是真的引用了onnx-ml.pb.cc的內(nèi)容,Who care?

0x03 總結(jié)

作為總結(jié)大磺,這里展示一個(gè)小Demo,文件結(jié)構(gòu)如下:

demo/
  CMakeLists.txt
  main.cpp
  source.txt
  utils.h

其中每個(gè)文件中的內(nèi)容如下:

// main.cpp
#include "utils.h"

int main(int argc, char **argv) {
    greeting("Sunny");

    return 0;
}

// utils.h
#ifndef MY_OWN_DEADER__
#define MY_OWN_HEADER__

#include <iostream>
#include <string>
void greeting(std::string who);

#endif // #define MY_OWN_HEADER__

// source.txt
#include <iostream>
#include <string>

#include "utils.h"

void greeting(std::string who) {
    std::cout<< "Hello " << who << std::endl;
}

此時(shí)探膊,如果CMakeLists.txt的內(nèi)容如下所示杠愧,則會(huì)執(zhí)行cat source.txt > test_file.cpp這條命令生成test_file.cpp,編譯得以通過(guò):

# CMakeLists.txt
cmake_minimum_required(VERSION 3.5)

project(demo VERSION 0.1 LANGUAGES C CXX)

add_custom_command(OUTPUT test_file.cpp
                    COMMAND cat source.txt > test_file.cpp
                    DEPENDS source.txt 
                    COMMENT "Just copy file contents")

add_executable(demo main.cpp test_file.cpp)

而如果CMakeLists.txt的內(nèi)容如下所示逞壁,則cat source.txt > test_file.cpp便不會(huì)執(zhí)行流济,在鏈接階段就會(huì)因?yàn)槿鄙?code>test_file.cpp中的函數(shù)實(shí)現(xiàn)而失斎衤唷:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.5)

project(demo VERSION 0.1 LANGUAGES C CXX)

add_custom_command(OUTPUT test_file.cpp
                    COMMAND cat source.txt > test_file.cpp
                    DEPENDS source.txt 
                    COMMENT "Just copy file contents")

add_executable(demo main.cpp)

唯一的區(qū)別就是有沒(méi)有在add_executable命令中指明demo對(duì)test_file.cpp的依賴(lài)。

歡1迎2關(guān)3注4個(gè)5人6微7信8公9眾10號(hào):愛(ài)碼士1024

0x03. References

[1] https://cmake.org/cmake/help/latest/command/add_custom_command.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绳瘟,一起剝皮案震驚了整個(gè)濱河市雕憔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌糖声,老刑警劉巖斤彼,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蘸泻,居然都是意外死亡琉苇,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)悦施,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)并扇,“玉大人,你說(shuō)我怎么就攤上這事抡诞∏钣迹” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵昼汗,是天一觀的道長(zhǎng)俩莽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)乔遮,這世上最難降的妖魔是什么扮超? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮蹋肮,結(jié)果婚禮上出刷,老公的妹妹穿的比我還像新娘。我一直安慰自己坯辩,他們只是感情好馁龟,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著漆魔,像睡著了一般坷檩。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上改抡,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天矢炼,我揣著相機(jī)與錄音,去河邊找鬼阿纤。 笑死句灌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播胰锌,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼骗绕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了资昧?” 一聲冷哼從身側(cè)響起酬土,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎格带,沒(méi)想到半個(gè)月后撤缴,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡践惑,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年腹泌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片尔觉。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡凉袱,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出侦铜,到底是詐尸還是另有隱情专甩,我是刑警寧澤,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布钉稍,位于F島的核電站涤躲,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏贡未。R本人自食惡果不足惜种樱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望俊卤。 院中可真熱鬧嫩挤,春花似錦、人聲如沸消恍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)狠怨。三九已至约啊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間佣赖,已是汗流浹背恰矩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茵汰,地道東北人枢里。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蹂午,于是被迫代替她去往敵國(guó)和親栏豺。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 由于項(xiàng)目需要用到thrift豆胸,而每次修改.thrift文件后奥洼,需要重新進(jìn)到別的目錄生成thrift的.cpp及.h...
    hijiang閱讀 24,208評(píng)論 0 2
  • CMake 全稱(chēng)“cross platform make”,是開(kāi)源晚胡、跨平臺(tái)的自動(dòng)化構(gòu)建系統(tǒng)灵奖。CMake 由 Kit...
    神齊閱讀 4,084評(píng)論 0 6
  • 搬運(yùn)自本人 CSDN 博客:https://blog.csdn.net/ajianyingxiaoqinghan/...
    琦小蝦閱讀 15,323評(píng)論 0 11
  • 編譯環(huán)境 通過(guò)過(guò)Android Studio內(nèi)置的SDK Manager下載NDK和CMake; 把環(huán)境變量AND...
    溫柔可愛(ài)小阿邴閱讀 2,902評(píng)論 0 0
  • 久違的晴天估盘,家長(zhǎng)會(huì)瓷患。 家長(zhǎng)大會(huì)開(kāi)好到教室時(shí),離放學(xué)已經(jīng)沒(méi)多少時(shí)間了遣妥。班主任說(shuō)已經(jīng)安排了三個(gè)家長(zhǎng)分享經(jīng)驗(yàn)擅编。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,495評(píng)論 16 22