Adding Multiple Executables in Cmake

Adding multiple executables in CMake

My suggestion is to tackle this in two phases:

  1. Build a library from the .cpp and .h files, using add_library
  2. Iterate through all your .cxx files and create an executable from each, using add_executable and foreach

Build the library

This could be something as simple as

file( GLOB LIB_SOURCES lib/*.cpp )
file( GLOB LIB_HEADERS lib/*.h )
add_library( YourLib ${LIB_SOURCES} ${LIB_HEADERS} )

Build all the executables

Simply loop over all the .cpp files and create separate executables.

# If necessary, use the RELATIVE flag, otherwise each source file may be listed 
# with full pathname. RELATIVE may makes it easier to extract an executable name
# automatically.
# file( GLOB APP_SOURCES RELATIVE app/*.cxx )
file( GLOB APP_SOURCES app/*.cxx )
foreach( testsourcefile ${APP_SOURCES} )
# I used a simple string replace, to cut off .cpp.
string( REPLACE ".cpp" "" testname ${testsourcefile} )
add_executable( ${testname} ${testsourcefile} )
# Make sure YourLib is linked to each app
target_link_libraries( ${testname} YourLib )
endforeach( testsourcefile ${APP_SOURCES} )

Some warnings:

  • file( GLOB ) is usually not recommended, because CMake will not automatically rebuild if a new file is added. I used it here, because I do not know your sourcefiles.
  • In some situations, source-files may be found with a full pathname. If necessary, use the RELATIVE flag for file(GLOB ...).
  • Manually setting the source-files requires a change to CMakeLists.txt, which triggers a rebuild. See this question for the (dis-)advantages of globbing.
  • I generated the testname using a string( REPLACE ... ). I could have used get_filename_component with the NAME_WE flag.

Concerning "general" CMake info, I advise you to read some of the broad "CMake Overview" questions already asked here on stackoverflow. E.g.:

  • https://stackoverflow.com/questions/2186110/cmake-tutorial
  • What are the dusty corners a newcomer to CMake will want to know?

How can I build two executables that share a main function in CMake?

You already have a src/CMakeLists.txt, so you are part of the way there. Put your overall build settings -- dependencies, C standard version, global compiler flags -- in the top-level CMakeLists.txt. In the subdirectories, put only your CMake commands for the executables, or whatever target makes sense locally.

(As an aside, define "enormous"? I've got top-level CMakeLists here that are anywhere between 200-950 lines, but I've seen 3000-line monstrosities as well)

Personally, from the minimal sketch of the source layout here, I'd do:

  • src/CMakeLists.txt does an add_subdirectory() command for each board, e.g. add_subdirectory(board/board_a) . If you like, set() a variable to a list of board names and you can iterate over it.
  • In each board's subdirectory, create a library -- shared, static, or OBJECT -- named after the board, with the sources for that board. For instance add_library(board_a OBJECT board.c)
  • Back in src/CMakeLists.txt again, for each board, add an executable with the source from app/ and link to the library defined for the board, like
    add_executable(exe_board_a app/source.c)
    target_link_library(exe_board_a PRIVATE board_a)
    If there are special considerations for that executable, set them there as well. Compile flags can be obtained from the library targets (use STATIC then, not OBJECT).

This moves most of the long-lists-of-sources and potentially compile flags to the per-board CMakeLists.txt and turns the intermediate level into a long list of "make this executable".

CMake share library with multiple executables

You should use project() command in subdirectories only if this subproject is intended to be built both as standalone and as a part of toplevel project. This is the case for LLVM and Clang, for example: Clang can be compiled separately, but when LLVM build system detects Clang source, it includes its targets too.

In your case you don't need subprojects. To compile only app1 or app2 target issue make app1/make app2 in projects build dir.

CMake: Build Multiple Executables in one Project with Static Library

My solution is to use add_subdirectory with relative patch to shared_lib directory. I don't think that this is a perfect solution it has its caveats:

  • Logic very similar to a header guard must be added to library CMakeLists.txt to prevent from defining targets multiple times.
  • Each CMakeList.txt file must know the relative path to the library, if one want to move library all CMakeLists must be updated.

Let's assume that the directory structure looks like this:

root/
CMakeLists.txt
shared_lib/
CMakeLists.txt
inc/
foo.h
src/
foo.c
exec1/
CMakeLists.txt
main.c
exec2/
CMakeLists.txt
main.c

root/CMakeList.txt

cmake_minimum_required(VERSION 2.6)

add_subdirectory(shared_lib)

add_subdirectory(exec1)
add_subdirectory(exec2)

I have decided that shared_lib/CMakeLists.txt will export a variable named SHARED_DIR_INCLUDE_DIR. This approach helps to decouple things a little bit.

root/exec1/CMakeLists.txt

cmake_minimum_required(VERSION 2.6)

add_subdirectory(./../shared_lib shared_lib)

include_directories(${SHARED_LIB_INCLUDE_DIR})

set(SRCS main.c)
add_executable(exec1 ${SRCS})
target_link_libraries(exec1 shared_lib)

if() in the fourth line solves the issue with target's multiple definition in case the CMakeLists file is added multiple times. The second and the third lines exports the include directory for library in SHARED_LIB_INCLUDE_DIR

root/shared_lib/CMakeLists.txt

cmake_minimum_required(VERSION 2.6)

set(SHARED_LIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/inc)

set(SHARED_LIB_INCLUDE_DIR ${SHARED_LIB_INCLUDE_DIR} PARENT_SCOPE)

if(TARGET shared_lib)

message("shared_lib is already defined")

else()

include_directories(${SHARED_LIB_INCLUDE_DIR})

set(LIB_SRCS ./src/foo.c)

add_library(shared_lib STATIC ${LIB_SRCS})

endif()

Running multiple executables with a single command in CMake and collecting the resulting output in a .txt file

How can I gather the executable targets FirstProgram and SecondProgram together so I can easily run them?

add_custom_target(run_them_together_easily
COMMAND $<TARGET_FILE:FirstProgram>
COMMAND $<TARGET_FILE:SecondProgram>
)

How can I capture the output of these programs and write them to a file?

A "proper way" way would be to add another cmake script that does that:

 # capture_command_output_to_file.cmake
# Arguments: COMMAND and OUTPUTFILE
execute_process(
COMMAND ${COMMAND}
OUTPUT_VARIABLE result
)
file(WRITE ${OUTPUTFILE} ${result})
# TODO: Fail if execute_process fails, for example

And then call that script with arguments from your cmake:

 add_custom_target(capture_output_of_FirstProgram_and_save_it
COMMAND ${CMAKE_COMMAND}
-D COMMAND=$<TARGET_FILE:FirstProgram>
-D OUTPUTFILE=${CMAKE_CURRENT_BINARY_DIR}/first_program_output.txt
-P ${CMAKE_CURRENT_SOURCE_DIR}/capture_command_output_to_file.cmake
)

But if you know you will work with shell-ish generator you can just use shell redirection (which will be not portable).


Personally I use a main Makefile that is used only as a alias/shortcut targets to easy build&test&deploy&whatever the project and I keep multiple cmake configuration there, all targets in Makefile are phony and the real execution just travels inside cmake binary dir. I use make only because of automatic autocompletion in shell of target names. But I do that because I am working solely in linux environments.



For the first part of this answer, would it be possible to add the commands from a list of targets

set(list_of_targets FirstProgram SecondProgram)
# you could wrap this in some macro
# create arguments to be passed to add_custom_target
set(add_custom_target_args)
foreach(i IN LISTS list_of_targets)
list(APPEND add_custom_target_args COMMAND $<TARGET_FILE:${i}>)
endforeach()
# then call it
add_custom_target(run_them_together_easily
${add_custom_target_args}
)

How can I use cmake to compile multiple source files and generate multiple executable files named after these file names

This is a bit of an odd request. Usually I'd suggest manually writing add_executable where needed since it's more maintainable.

In CMake, there isn't really a good way to collect all files in a directory. You can use file(GLOB ...) to grab all files; but this gets done at configure time, and if you introduce new sources then CMake won't detect the new source and won't reconfigure automatically or build the new sources without explicitly being told to reconfigure.

If you're able to discretely list each source, it would be better. But otherwise what you are requesting can be done with a combination of a foreach through each source file using get_filename_component to get the file name and passing it to add_executable

set(source_files src/a.cpp src/b.cpp src/c.cpp ...)

# Loop through each source file
foreach(source_file IN LISTS source_files)
# Get the name of the file without the extension (e.g. 'a' from src/a.cpp'
get_filename_component(target_name ${source_file} NAME_WE)

# Create an executable with the above name, building the above source
add_executable("${target_name}" "${source_file}"
endforeach()

If discretely listing the source files isn't possible, you can use file(GLOB...) or file(GLOB_RECURSE):

file(GLOB source_files "src/*.cpp")

But again; this prevents automatically detecting when new sources get added, and I don't recommend it.



Related Topics



Leave a reply



Submit