Skip to content

Instantly share code, notes, and snippets.

@socantre
Created August 24, 2015 03:45
Show Gist options
  • Star 72 You must be signed in to star a gist
  • Fork 10 You must be signed in to fork a gist
  • Save socantre/7ee63133a0a3a08f3990 to your computer and use it in GitHub Desktop.
Save socantre/7ee63133a0a3a08f3990 to your computer and use it in GitHub Desktop.
Example of using add_custom_command and add_custom_target together in CMake to handle custom build steps with minimal rebuilding: This example untars library headers for an INTERFACE library target
set(LIBFOO_TAR_HEADERS
"${CMAKE_CURRENT_BINARY_DIR}/include/foo/foo.h"
"${CMAKE_CURRENT_BINARY_DIR}/include/foo/foo_utils.h"
)
add_custom_command(OUTPUT ${LIBFOO_TAR_HEADERS}
COMMAND ${CMAKE_COMMAND} -E tar xzf "${CMAKE_CURRENT_SOURCE_DIR}/libfoo/foo.tar"
COMMAND ${CMAKE_COMMAND} -E touch ${LIBFOO_TAR_HEADERS}
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include/foo"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/libfoo/foo.tar"
COMMENT "Unpacking foo.tar"
VERBATIM
)
add_custom_target(libfoo_untar DEPENDS ${LIBFOO_TAR_HEADERS})
add_library(foo INTERFACE)
target_include_directories(foo INTERFACE "${CMAKE_CURRENT_BINARY_DIR}/include/foo")
target_link_libraries(foo INTERFACE ${FOO_LIBRARIES})
@gon1332
Copy link

gon1332 commented Mar 18, 2021

How could this be changed if I didn't know the header files included in the tar? Basically if I want LIBFOO_TAR_HEADERS to be populated in some more automatic way. I struggled with file(GLOB..) but didn't work.

@PhilippeCarphin
Copy link

What I would do is untar the thing, run a find command in my shell, copy-it and paste like this

set(LIB_FOO_HEADERS
# Paste here
)

I prefer this over using GLOB. You can read up on the GLOB vs no-GLOB in debate for CMake.

For me it boils down to the fact that if I add a file, I have to go write it somewhere in a CMakeLists.txt which ensures that when I run make, cmake will be re-run. This is not the case with globs.

Sorry if you're disappointed with my "copy-paste some shell output in your CMakeLists.txt" answer but that is what I do 😄

@gon1332
Copy link

gon1332 commented Mar 18, 2021

First of all thanks for your answer. Your proposal is the best/single working solution that I have up to now. 😄
Yeah, GLOB can hide a lot of traps.
I was trying to do the find and copy-paste trick you mentioned a bit more automatically via a custom command or target, but the problem was that I couldn't assign it's output to a CMake variable. 🤕

@PhilippeCarphin
Copy link

PhilippeCarphin commented Mar 18, 2021

There's a parameter to make the output of execute_process go into a variable (or to make the output of a custom command, I forget which) but it's not worth it.

Plus you would have to make sure that this automatic process is triggered when you add a new file anyway by remembering to run CMake when you add a file.

With the files written explicitely, you have to remember to add the file in a CMakeLIsts.txt somewhere but that will make sure the the necessary things get triggered.

@gon1332
Copy link

gon1332 commented Mar 18, 2021

I agree with you regarding the execute_process solution. I also like the idea of writing explicitly the files in my case now that you put it that way.

@krishna116
Copy link

It is a good example.
if the process logic is too complicate, I have a idea about wrap some logic code to script.cmake, then the code will be more clean.
for example:

CMakeList.txt

set(InputFileList "config.h" "apis.h")
set(OutputFileName "${CMAKE_CURRENT_BINARY_DIR}/SingleHeadFile.h")

add_custom_command(OUTPUT ${OutputFileName}
COMMAND ${CMAKE_COMMAND} -P "${PROJECT_SOURCE_DIR}/cmake/GenerateSingleFile.cmake" "${InputFileList}" "${OutputFileName}"
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
DEPENDS ${InputFileList}
COMMENT "Generate single-head-file from input-file-list."
VERBATIM
)
add_library(Foo INTERFACE ${OutputFileName})

GenerateSingleFile.cmake

-- your logic code here

@jeffersonfr
Copy link

Helps a lot. Thanks :)

@marc-hb
Copy link

marc-hb commented Sep 26, 2022

So I ended up doing this: A macro called add_something that creates a target [ combining add_custom_command + add_custom_target ]

Every time I do NOT find something like this macro in plain CMake it makes me feel like I still don't understand CMake.

But no, it's apparently CMake that does not understand the most basic feature people need.

Best page found so far:
https://samthursfield.wordpress.com/2015/11/21/cmake-dependencies-between-targets-and-files-and-custom-commands/

@pranavb-ca
Copy link

pranavb-ca commented Oct 27, 2022

Thank you for posting this. I am perplexed about one thing though. add_library and add_executable know that source files are present in ${CMAKE_CURRENT_SOURCE_DIR} so we don't need to explicitly mention it. But, add_custom_command needs the full path to a file in the current source directory. So,

add_custom_command(OUTPUT cross_compile_file.o
${CXX_CROSS_COMPILER} -c cross_compiled_file.cpp
MAIN_DEPENDENCY cross_compiled_file.cpp
VERBATIM)

results in no input files: cannot find cross_compiled_file.cpp error from the compiler. The behavior of cmake seems inconsistent here.
I guess the reasoning is that the custom_command could be anything so cmake isn't going to assume anything.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment