Adding multiple executables in CMake - c++

My code in a C++ project is organised as follows
I have several .cpp and .h files which contains my classes
I have several .cxx files which have to be compiled against the .cpp files and some external libraries.
Now, each of the .cxx files have a main() method, so I need to add a different executable for each of these files having the same name as the file.
Also, these .cxx files might not get linked to the same external libraries.
I want to write this build in CMake, in which I am kind of a newbie, how do I go about this?

My suggestion is to tackle this in two phases:
Build a library from the .cpp and .h files, using add_library
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?

I find myself in a similar situation when organizing an OpenGL project with multiple sample files where each of these files contain a main method.
The settings below will generate a separate executable per c/cpp file as well as copying required dependencies to the target bin folder.
Folder Structure
my-project
│── ch01_01.c
│── ch02_01.cpp
│── CMakeLists.txt
│── Resources
│ │── Libraries
│ │ │── glew
│ │ │ │── bin
│ │ │ │── include
│ │ │ │── lib
│ │ │── glfw
│ │ │ │── include
│ │ │ │── lib
CMakeLists.txt
cmake_minimum_required (VERSION 3.9)
project ("my-project")
include_directories(Resources/Libraries/glew/include
Resources/Libraries/glfw/include)
link_directories(Resources/Libraries/glew/lib
Resources/Libraries/glfw/lib)
link_libraries(opengl32.lib
glew32.lib
glfw3.lib)
set(CMAKE_EXE_LINKER_FLAGS "/NODEFAULTLIB:MSVCRT")
file(GLOB SOURCE_FILES *.c *.cpp)
foreach(SOURCE_PATH ${SOURCE_FILES})
get_filename_component(EXECUTABLE_NAME ${SOURCE_PATH} NAME_WE)
add_executable(${EXECUTABLE_NAME} ${SOURCE_PATH})
# Copy required DLLs to the target folder
add_custom_command(TARGET ${EXECUTABLE_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_SOURCE_DIR}/Resources/Libraries/glew/bin/glew32.dll"
"${CMAKE_BINARY_DIR}/glew32.dll")
endforeach(SOURCE_PATH ${SOURCE_FILES})
Optional Steps
In Visual Studio
Open the project with 'Open a local Folder' option in the Start Window
When adding a new file you may either:
Cancel the dialog asking to automatically add_executable to CMakeLists.txt
Disable this behavior by unchecking 'Enable automatic CMake script modification for file operations from folder view' in Tools > Options > CMake
As newly added files are not picked up automatically as CMakeLists.txt is never changed, simply regenerate the cache like so:
Project > CMake Cache (x64-Debug) > Delete Cache
Project > Generate Cache for my-project
Now you may simply right click a given c/cpp file and Set as Startup Item to be able to debug it with F5.
Environment
cmake version 3.18.20081302-MSVC_2
Microsoft Visual Studio Community 2019 Version 16.8.3
Starter Template
I put together this starter template on GitHub in case you are interested.

This CMakeLists.txt works for my OpenCV project
assuming *.cpp files are in the same directory as CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(opencv LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(OpenCV REQUIRED)
include_directories( ${OpenCV_INCLUDE_DIRS} )
file( GLOB APP_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp )
foreach( sourcefile ${APP_SOURCES} )
file(RELATIVE_PATH filename ${CMAKE_CURRENT_SOURCE_DIR} ${sourcefile})
string( REPLACE ".cpp" "" file ${filename} )
add_executable( ${file} ${sourcefile} )
target_link_libraries( ${file} ${OpenCV_LIBS} )
endforeach( sourcefile ${APP_SOURCES} )

Related

"No such file or directory" when linking shared library with CMake [duplicate]

This question already has answers here:
How to properly add include directories with CMake
(12 answers)
CMake link to external library
(6 answers)
Closed 1 year ago.
I have am trying to link a precompiled so file to my executable in cmake. I'm not sure if I am misunderstanding how to use a shared library or not but I assumed that once I compile a shared library, any application can link to it and use it's functions. When compiling the executable, I get this error:
logger.h: No such file or directory
#include "logger.h"
^~~~~~~~~~
compilation terminated.
Here is my cmake file for the executable
cmake_minimum_required(VERSION 2.8)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# set PROJECT home directory
set(PROJECT_HOME "/opt/PROJECT")
# set the project name
project(ClServer)
# add the executable
add_executable(ClServer server.cpp)
add_library(logger SHARED IMPORTED libClLogger.so)
message ("Here")
set_target_properties(logger
PROPERTIES IMPORTED_LOCATION "#{PROJECT_HOME}/base"
)
target_link_libraries(ClServer
${Logger}
)
I've also tried using find_library and adding an absolute path to the shared library using target_link_libraries, but the same error occurs.
Here is my cmake file for the logger shared library:
cmake_minimum_required(VERSION 2.8)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED True)
# set Clamor home directory
set(PROJECT_HOME "/opt/PROJECT")
# set the project name
project(ClLogger)
# set boost paths
set(Boost_NO_SYSTEM_PATHS TRUE)
if (Boost_NO_SYSTEM_PATHS)
set(BOOST_ROOT "${PROJECT_HOME}/ext/Boost/boost_1_76_0")
set(BOOST_LIBRARYDIR "${PROJECT_HOME}/ext/Boost/boost_1_76_0/stage/lib")
set(BOOST_INCLUDEDIR "${PROJECT_HOME}/ext/Boost/boost_1_76_0")
endif (Boost_NO_SYSTEM_PATHS)
# provides the linker with the appropriate directories and components
find_package(Boost 1.76.0 COMPONENTS log log_setup thread filesystem system)
if(Boost_FOUND)
include_directories(${BOOST_INCLUDEDIR})
link_directories(${BOOST_LIBRARYDIR})
endif()
message (STATUS Boost_LIBRARIES:)
message (STATUS ${Boost_LIBRARIES})
message (STATUS BOOST_INCLUDEDIR:)
message (STATUS ${BOOST_INCLUDEDIR})
# add PROJECT source code libraries
add_library(ClLogger SHARED logger.cpp)
# link the appropriate libraries
target_link_libraries(ClLogger
libpthread.so.0
${Boost_LIBRARIES}
)
# redirect binaries to base folder
set_target_properties(ClLogger
PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_HOME}/base"
LIBRARY_OUTPUT_DIRECTORY "${PROJECT_HOME}/base"
RUNTIME_OUTPUT_DIRECTORY "${PROJECT_HOME}/base"
)
To consume a shared library, you need two things. First, shared library file and second public header file. Header file will get used in compilation stage and shared library file in linking stage. Error you are seeing is a compilation failure which says it failed to find header file "logger.h". Use target_include_directories in cmake of your executable to tell cmake where to look for header logger.h.
Additionally if you want cmake to keep watch on logger.h modification, you can add logger.h as source file of your executable.
After a bit of research and testing in my local machine,
i have updated my answer with the simple example tested on eclipse CDT and cmake in WSL2.(should work on any other environment fine ).
the following example should provide an answer to the question.
cmake_test/
├── CMakeLists.txt
├── build
├── cmake_test.cpp
└── **companyLibrary**
├── CMakeLists.txt
├── build
├── include
│   └── Company.h
└── src
└── Company.cpp
from the above project hierarchy companyLibrary is the one which generates the .so (shared Library), which will be used by cmake_test.cpp source file which is at the root level of the project.
cmake_test/companyLibrary/CMakeLists.txt
cmake_minimum_required (VERSION 2.6)
project (companyLibrary_test)
set(CMAKE_BUILD_TYPE Release)
#adding the header <.h> files into the project
include_directories(include)
file(GLOB SOURCES "src/*.cpp")
add_library(testCompany SHARED ${SOURCES})
install(TARGETS testCompany DESTINATION /usr/lib)
this generates the .so file in this example (libtestCompany.so)
sudo make install
this will put .so file in /usr/lib folder
now this shared library will be used by the root project cmake_test.cpp file
cmake_test/CMakeLists.txt
cmake_minimum_required (VERSION 2.6)
project (cmake_test)
set ( PROJECT_LINK_LIBS libtestCompany.so )
link_directories( "/usr/lib" )
include_directories( "companyLibrary/include" )
add_executable( libtest cmake_test.cpp )
target_link_libraries( libtest ${PROJECT_LINK_LIBS} )
add_executable and target_link_libraries should solve the problem
as show above.
full source code availabe here on GitHub.
hope it helps :)

CMakeLists.txt for third-party C files within C++ project

My C++ project doggo has a doggo/external/ directory for third-party code. Currently it contains gtest and a CMakeLists.txt:
# Google gtest for unit testing.
add_subdirectory(gtest)
message("gtest include dir: ${gtest_SOURCE_DIR}")
include_directories(${gtest_SOURCE_DIR})
My top-level doggo/CMakeLists.txt contains the line add_subdirectory(external) to find and build the third-party libraries. Everything works like a charm -- I can include gtest with #include <gtest/gtest.h>. Now I'd like to add the randomkit C library to doggo/external/, as is done here: randomkit from numpy.
How can I get randomkit to build in my doggo/external/ dir? What should the doggo/external/CMakeLists.txt look like?
I should then be able to include the C headers for use in my x.cpp files by including the headers inside an extern "C" { ... } block (details here).
UPDATE: How do I install randomkit here?
I've included a CMakeLists.txt entry like that above but for randomkit, and the directory looks like,
external
├── CMakeLists.txt
├── gtest
│  └── ...
└── randomkit
├── CMakeLists.txt
├── distributions.c
├── distributions.h
├── randomkit.c
└── randomkit.h
and the randomkit/CMakeLists.txt:
project(randomkit)
file(GLOB SOURCES "*.c")
add_library(randomkit SHARED ${SOURCES})
INSTALL(
DIRECTORY ${CMAKE_SOURCE_DIR}/
DESTINATION "/usr/local/"
#DESTINATION ""
FILES_MATCHING PATTERN "*.h*")
(second DESTINATION commented out to show I tried that as well)
Yet when I run the build steps for my top-level project doggo I get an error trying to #include <randomkit/distributions.h>:
doggo/src/random_fooz.cpp:10:37: fatal error: randomkit/distributions.h: No such file or directory
UPDATE 2: doggo/CMakeLists.txt:
project(doggo)
# Find and build third-party libraries
add_subdirectory(external)
# Add source dirs to the search path so cmake can find headers
include_directories(${CMAKE_SOURCE_DIR}/include/)
# Collect source files and build
file(GLOB_RECURSE doggo_srcs ${CMAKE_SOURCE_DIR}/src/*.cpp)
add_library(doggo ${doggo_srcs})
# Setup executables
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_SOURCE_DIR}/bin/)
add_subdirectory(exec)
# Tests
add_subdirectory(test)
In the randomkit/CMakeLists.txt write:
project(randomkit)
file(GLOB SOURCES "*.c")
add_library(randomkit SHARED ${SOURCES})
target_include_directories(randomkit PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
INSTALL(
DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/
DESTINATION "include" # this a the subdirectory with ${CMAKE_INSTALL_PREFIX}
FILES_MATCHING PATTERN "*.h*")
In the main CMakeLists.txt, you do:
add_library(doggo ${doggo_srcs})
target_link_libraries(doggo PUBLIC randomkit)
target_include_directories(doggo PUBLIC ${CMAKE_SOURCE_DIR}/include/)
Don’t use include_directories.
Now, because the randomkit target has the PUBLIC property with the right include directories, those include directories will be automatically used when building the doggo library. And again, because the doggo library has include directories and libraries in its public interface, executables that you link to doggo will automatically be linked to these libraries, and find their include files.
Note that the INSTALL command in randomkit/CMakeLists.txt is only executed when you actually run the install target. When building, the include files must be found in the source tree.

CPack: Interface include directories are missing in archive

I am using CMake 3.10.1 and trying to use CPack to generate archives for a library and I cannot get it to add the interface include directory to the archive.
The library and the generated export files are added as expected, however the include directory (added using target_include_directories(... PUBLIC ...) is missing entirely.
The CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project(Test VERSION 1.0.0 LANGUAGES CXX)
add_library(${PROJECT_NAME} SHARED foo.cpp) #add sources and executable
target_include_directories(${PROJECT_NAME} PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc>
$<INSTALL_INTERFACE:inc>
)
install(TARGETS ${PROJECT_NAME}
EXPORT ${PROJECT_NAME}
INCLUDES DESTINATION inc
PUBLIC_HEADER DESTINATION inc
LIBRARY DESTINATION lib
)
install(EXPORT ${PROJECT_NAME} DESTINATION .)
include(CPack)
The contents of my source dir:
├── CMakeLists.txt
├── foo.cpp
└── inc
└── foo.h
The contents of the tgz generated by cpack -G TGZ .
├── lib
│   └── libTest.so
├── Test.cmake
└── Test-noconfig.cmake
Any ideas why it could be missing the inc directory?
Generator-like expression $<INSTALL_INTERFACE> used in the target_include_directories() command by itself doesn't install corresponded directory. You need to install this directory manually (with install(FILES) or install(DIRECTORY)).
Expression $<INSTALL_INTERFACE> specifies interface include directory for the target in the config file, which exports install tree (see install(EXPORT) command).
Expression $<BUILD_INTERFACE> specified interface include directory for the target in the project itself, and in the config file which exports build tree (see EXPORT() command).
But these expressions doesn't enforce $<BUILD_INTERFACE> directory to be copied into $<INSTALL_INTERFACE> one on installation. As opposite, content of this directories usually differs: aside from header files for outer use, installed into $<INSTALL_INTERFACE> directory, a directory $<BUILD_INTERFACE> may contain header files for project's internal use, which are not installed.

Can't debug GoogleTest when use static library

I put the gtest src in to my project folder like below and wish to debug the testcase in VS.
│ CMakeLists.txt
│
├─google test source code
│
├─src
│ │ CMakeLists.txt
│ └─ CObject.h
│
└─UnitTest
TCObject.cpp
Everything is fine, I use the Cmake to generate the VS project file and open it in Visual Studio 2015.The CmakeLists.txt is like below
PROJECT (BVH)
cmake_minimum_required(VERSION 3.00)
SET(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR})
INSTALL(DIRECTORY src/shaders DESTINATION build/bin)
INSTALL(DIRECTORY scene DESTINATION build/bin)
ADD_SUBDIRECTORY(src bin)
################################
# GTest
################################
ADD_SUBDIRECTORY (googletest)
ENABLE_TESTING()
INCLUDE_DIRECTORIES(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR})
################################
# Unit Tests
################################
# Add test cpp file
INCLUDE_DIRECTORIES($ENV{GLM})
ADD_EXECUTABLE( runUnitTests UnitTest/TCObject.cpp )
# Link test executable against gtest & gtest_main
TARGET_LINK_LIBRARIES(runUnitTests gtest gtest_main )
ADD_TEST( runUnitTests runUnitTests )
And here is my testcase
#include<gtest/gtest.h>
#include"../src/CObject.h"
#include<glm/glm.hpp>
TEST(TestCObject,TestInitial){
CCube *cube=new CCube(glm::vec3(1,2,3),2);
EXPECT_EQ(2,cube->bounds.pMax[0]);
}
Problem 1:I can only run my testcase if I set the gtest's and gtest_main's runtime library to MDd instead of it's own MTd, I don't know if there is any problem in the future
Problem 2:If I choose to set a breakpoint and debug it, the window is just flash and disappear.Only if I change the cmake option BUILD_SHARED_LIBRARIES of gtest and copy the dll file into the place where my executable testfile locate, then I can debug my testcase
But the problem is I must copy the dll file everytime. So I wonder if there's a way to debug in the static libraries (Problem 1) or I can set the target space of gtest to reach to the place where my testfile locate (Problem 2).

CMake and absolute header paths

I'm trying to use CMake to build my C++ project and I have a problem in the header paths.
Since I'm using a lot of classes organized in several directories, all my include statements are with absolute paths (so no need to use "../../") but when try to make the CMake-generated Makefile it just doesn't work.
Does anyone know how to specify in CMakeLists.txt that all the includes are with absolute paths?
My output when trying to make
~/multiboost/BanditsLS/GenericBanditAlgorithmLS.h:45:25: Utils/Utils.h: No such file or directory
~/multiboost/BanditsLS/GenericBanditAlgorithmLS.h:46:35: Utils/StreamTokenizer.h: No such file or directory
My CMakeLists.txt file :
#The following command allows the use of the "file" command
cmake_minimum_required(VERSION 2.6)
#The declaration of the project
project(multiboost)
#This allows recursive parsing of the source files
file(
GLOB_RECURSE
source_files
*
)
list(REMOVE_ITEM source_files ./build/* )
#This indicates the target (the executable)
add_executable(
multiboost
${source_files} #EXCLUDE_FROM_ALL build/
)
You need something like this in CMakeLists.txt:
SET(BASEPATH "${CMAKE_SOURCE_DIR}")
INCLUDE_DIRECTORIES("${BASEPATH}")
set the correct include path: suppose your Utils directory is in /exp/appstat/benbou/multiboost, then cmake (well actually, gcc) has to know this:
include_directories( /exp/appstat/benbou/multiboost )
or it might be more convenient to pass this as an option which is passed on the command line:
include_directories( ${MyProjectRoot} )
cmake -DMyProjectRoot=/exp/appstat/benbou/multiboost