The CMake logo, licensed under CC BY 2.0

CMake, custom targets, and commands

Notes published the
5 - 6 minutes to read, 1178 words
Categories: build system
Keywords: add_custom_command add_custom_target build system cmake

CMake offers both add_custom_command and add_custom_target for executing user-defined actions, but from the documentation, it is not always clear how to use them, and when one is more appropriate than another.

TLDR

  • use add_custom_command(TARGET if action depends on a target (and output probably not used by someone else)

  • use add_custom_command(OUTPUT if the output needs to be consumed by someone else

  • add_custom_target(foo is always out-of-date. No one should depend on such targets. It could be used for automating some user-defined actions (like executing Cppcheck or test suite, copying some files that are needed sometimes…​), or providing a nicer API for the end-user.

Minimal example with add_custom_command

cmake_minimum_required(VERSION 3.10)
project(foo)

add_executable(foo main.cpp)

add_custom_command(TARGET foo
    PRE_BUILD
    COMMAND ${CMAKE_COMMAND} -E echo hello
    COMMENT "This command will be executed before building foo"
    VERBATIM
    # no support for depends
    # no support for output
)

# never executed, unless someone depends on OUTPUT
add_custom_command(
    OUTPUT ${CMAKE_BINARY_DIR}/file.txt
    COMMAND ${CMAKE_COMMAND} -E copy_if_different file.txt ${CMAKE_BINARY_DIR}/
    DEPENDS file.txt
    COMMENT "This command will never be executed unless someone depends on output"
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} # instead of using absolute paths
)

In this example, add_executable is used for creating an executable. add_custom_command(TARGET foo is for bundling the execution of an external command together with the target foo, in this case, it’s a simple output to the console. Notice that this signature of add_custom_command does not support DEPENDS and OUTPUT, thus its dependencies must be at least those of foo, and it cannot be used for creating other targets that depend on it.

On the other hand add_custom_command(OUTPUT supports both DEPENDS and OUTPUT. Thus it will not be executed if no one depends on it (even when executing the target all), and it can be used for creating dependent targets.

Minimal example with add_custom_target

cmake_minimum_required(VERSION 3.10)
project(foo)

add_executable(foo main.cpp)

add_custom_target(bar
    COMMAND ${CMAKE_COMMAND} -E echo hello
    VERBATIM
    # no support for output
)

Similar to add_custom_command(TARGET, it does not support OUTPUT, but it supports DEPENDS. Thus add_custom_target is generally not a good choice if another target needs to depend on it, but it can depend on other targets and commands without issues.

This is probably the main reason why someone should avoid add_custom_target for building code. Thus add_custom_target is always executed when invoked (even if no file changed), and all targets that depend on it too!

This makes it unsuitable, for example, for a code generator, as other targets depending on it would always need to be rebuilt.

Some use-cases

There is not much more left to explain, the TLDR sums it up nicely

  • use add_custom_command(TARGET if action depends on a target (and output probably not used by someone else)

  • use add_custom_command(OUTPUT if the output needs to be consumed by someone else

  • add_custom_target(foo is always out-of-date. No one should depend on such targets. It could be used for automating some user-defined actions (like executing Cppcheck or test suite, copying some files that are needed sometimes…​)

I tried to come up with some use cases to show what is better to use in which situation.

It seems that for most tasks add_custom_command(OUTPUT is the right choice ™. add_custom_command(TARGET has similar use cases, but makes it impossible to reuse the output of the custom command (if there is any).

add_custom_target can be used for "user-defined" operations, that might depend on external resources or are (or should be not) not reproducible for other reasons.

Code generator with add_custom_command

This is the main use-case I’ve used add_custom_command so far:

cmake_minimum_required(VERSION 3.10)
project(foo)

add_executable(foo main.cpp)
target_link_libraries(foo bar)

set(GEN_SRC
    ${CMAKE_BINARY_DIR}/bar.hpp
    ${CMAKE_BINARY_DIR}/bar.cpp
)
add_custom_command(
    OUTPUT ${GEN_SRC}
    COMMAND ${CMAKE_COMMAND} -E copy bar.in.hpp ${CMAKE_BINARY_DIR}/bar.hpp
    COMMAND ${CMAKE_COMMAND} -E copy bar.in.cpp ${CMAKE_BINARY_DIR}/bar.cpp
    DEPENDS bar.in.hpp bar.in.cpp
    COMMENT "Generate source code"
    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} # instead of using absolute file paths
)
add_library(bar ${GEN_SRC})

While it is possible to mix the generated and hand-written source code, I prefer keeping them as separate targets. It is generally easier to disable compiler warnings for a whole library, as generally there is no interest in fixing those for generated code (it is better to improve the code generator), similarly to external libraries.

Manually execute static analysis

CMake added support for cppcheck, clang-tidy, and possibly other static analyzers. Nevertheless, there could be some hand-written static analyzers that do a very specific verification.

While add_custom_target seems the right choice, as there might be no output, doing the analysis a second time if no file changes is just a waste of time. It might be better to save/redirect the output to a file.

if(MSBUILD)
    set(PROJMAP ${CMAKE_BINARY_DIR}/${PROJ}.sln) # or create compile_commands.json with something else
else()
    set(PROJMAP ${CMAKE_BINARY_DIR}/compile_commands.json)
endif()
add_custom_command(
    OUTPUT ${CMAKE_BINARY_DIR}/cppcheck.xml
    COMMAND ${CPPCHECK} --inconclusive --xml --project=${PROJMAP} --enable=all --output-file ${CMAKE_BINARY_DIR}/cppcheck.xml
    COMMENT "Generate source code"
)

For the end-user, it is possible to use add_custom_target to provide an easier name to invoke

add_custom_target(check-with-cppcheck
    DEPENDS ${CMAKE_BINARY_DIR}/cppcheck.xml
)

as check-with-cppcheck does nothing, and nothing should depend on it (as other targets should depend on ${CMAKE_BINARY_DIR}/cppcheck.xml), it is not an issue if it is always out-of-date.

For example, another user-defined action that could depend on ${CMAKE_BINARY_DIR}/cppcheck.xml, is opening the output with the appropriate program

add_custom_target(check-with-cppcheckgui
    ${CPPCHECK_GUI} ${CMAKE_BINARY_DIR}/cppcheck.xml
    DEPENDS ${CMAKE_BINARY_DIR}/cppcheck.xml
)

In this case, check-with-cppcheckgui does something (executing an external program), but it is fine, as the program is used interactively by the user.

User-defined actions with add_custom_target

While I’ve used make to partially automate tasks, like creating an appropriate environment, downloading files, executing tests, opening documents, etc. etc. I’ve never used CMake until now for such tasks.

A possible example would be opening the browser to the generated or official documentation, thus something like

if(BROWSER)
    add_custom_target(open-gen-documentation
        ${BROWSER} path/to/gen-doxygen/index.html
        COMMENT "open documentation with firefox"
    )
    add_custom_target(open-spec
        ${BROWSER} path/or/url/to/specification
        COMMENT "open specification with firefox"
    )
endif()

while it might be a nice idea, this can increase the configuration time, unless the browser is already used for something else, as CMake needs to find it while configuring the project.


Do you want to share your opinion? Or is there an error, some parts that are not clear enough?

You can contact me anytime.


哆哆女性网女生网名气质最新机械厂起名字大全周易在线八字排盘r2v 电子签名河南省永城市地图seo关键词优化怎么样测给孩子起名七月生的宝宝起名字党片观后感老公出轨后我该起个什么网名掉牙齿 解梦半是蜜糖半是伤演员表双胞胎可以起什么名字广德网站优化公司许姓男宝宝起名大全德鲁伊名字美甲美睫美容店起什么名字网站设计样式表父母唯其疾之忧读后感如何男孩起个英文名字菏泽网站建设设计费用慕起名男孩发现隐形的翅膀读后感姓吕男人起名周易姓名测分名字测试免费红瓦黑瓦读后感周易算卦八字算命上海建设网站价格政府网站建设需要什么深圳侦探seo淀粉肠小王子日销售额涨超10倍罗斯否认插足凯特王妃婚姻不负春光新的一天从800个哈欠开始有个姐真把千机伞做出来了国产伟哥去年销售近13亿充个话费竟沦为间接洗钱工具重庆警方辟谣“男子杀人焚尸”男子给前妻转账 现任妻子起诉要回春分繁花正当时呼北高速交通事故已致14人死亡杨洋拄拐现身医院月嫂回应掌掴婴儿是在赶虫子男孩疑遭霸凌 家长讨说法被踢出群因自嘲式简历走红的教授更新简介网友建议重庆地铁不准乘客携带菜筐清明节放假3天调休1天郑州一火锅店爆改成麻辣烫店19岁小伙救下5人后溺亡 多方发声两大学生合买彩票中奖一人不认账张家界的山上“长”满了韩国人?单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#青海通报栏杆断裂小学生跌落住进ICU代拍被何赛飞拿着魔杖追着打315晚会后胖东来又人满为患了当地回应沈阳致3死车祸车主疑毒驾武汉大学樱花即将进入盛花期张立群任西安交通大学校长为江西彩礼“减负”的“试婚人”网友洛杉矶偶遇贾玲倪萍分享减重40斤方法男孩8年未见母亲被告知被遗忘小米汽车超级工厂正式揭幕周杰伦一审败诉网易特朗普谈“凯特王妃P图照”考生莫言也上北大硕士复试名单了妈妈回应孩子在校撞护栏坠楼恒大被罚41.75亿到底怎么缴男子持台球杆殴打2名女店员被抓校方回应护栏损坏小学生课间坠楼外国人感慨凌晨的中国很安全火箭最近9战8胜1负王树国3次鞠躬告别西交大师生房客欠租失踪 房东直发愁萧美琴窜访捷克 外交部回应山西省委原副书记商黎光被逮捕阿根廷将发行1万与2万面值的纸币英国王室又一合照被质疑P图男子被猫抓伤后确诊“猫抓病”

哆哆女性网 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化