Creating c++ project using cmake and automatically adding source and header files - c++

I'm new to cmake and im trying to create a project for simulating a few libraries im writing to run on arduino. This means the directory structure of the libraries cant be changed but i can add a cmakelists file but would rather not. I need some help writing the CMakeLists file.
The simulator directory structure is:
include/
lib/
src/
CMakeLists.txt
Each library im writing must have this file structure:
TestLib/
include/VeryGoodLib.hpp
include/VeryGoodLib/sometool.hpp
src/*.cpp //All the source files
Each library is placed into the main projects lib/ directory and im hoping to make get cmake to "glob" all the libraries inside lib/ and automatically add them to the main project while also making them show up when I write #include. In this cade i would see the VeryGoodLib/ folder and VeryGoodLib.hpp header.
My current CMakeLists file is:
cmake_minimum_required(VERSION 3.0.0)
project(EasyVTOL-Sim VERSION 0.1.0)
file(GLOB_RECURSE SRC_FILES src/*.cpp)
add_executable(EasyVTOL-Sim ${SRC_FILES})
target_include_directories(EasyVTOL-Sim PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
Any simple way of globing the files and including them this way?

Related

Export custom library which uses Qt via CMake for use in another CMake project (Windows, Mingw-w64)

A bit of background on what I am trying to achieve: I already have a project that I have developed in CMake (it is collection of CMake projects: basically a state machine with a few helper libraries). Now, I want to develop a GUI in Qt that this state machine can control (and make it do stuff). However, for the life of me, I cannot figure out how to properly export the library which has the Qt classes and functions to another CMake package's executable. I am on Windows, and using the Mingw-w64 compiler and make program.
I was experimenting with 2 simple CMake projects, one which has a simple Qt form (which I just copied from and modified this repository and the corresponding youtube video given in the README), and the other which has the executable. I was trying to link the first project with the second project, however, whenever I build the autogenerated makefile, it fails in the end, saying that symbols are undefined (For example undefined reference to run_stuff::run_stuff()'). I apologize if the files are dirty, I have been trying out various stuff to no avail.
Things I have tried:
I have followed the answer here very carefully.
I have tried setting CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS to ON, as suggested by many sites (like this one) for running DLLs
I have tried making the library static (hence doing away with the need for DLLs)
I have tried setting the target_include_dirs from private to public and also commenting out that line. (not sure what that does, but okay)
However, no matter what I do, I always end with linking errors in the end, and my executable in the second project is not able to access any functions from the Qt project. Any suggestions on what I should try next? Would be really helpful if you could point out errors I am making in my CMakeLists.txt files (once again apologies for the messy code and CMakeLists)
My project CMake file looks like this:
cmake_minimum_required(VERSION 3.14)
# if (WIN32)
# set(CMAKE_SHARED_LIBRARY_PREFIX "")
# endif ()
set(CMAKE_MAKE_PROGRAM $ENV{MAKE})
set(CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo" CACHE STRING "" FORCE)
set(CMAKE_SUPPORT_WINDOWS_EXPORT_ALL_SYMBOLS ON)
set(WINDOWS_EXPORT_ALL_SYMBOLS ON)
add_subdirectory(QT6CMake)
add_subdirectory(Exec)
The library CMake file:
cmake_minimum_required(VERSION 3.14)
if (WIN32)
project(MY_PROJECT LANGUAGES CXX)
elseif(UNIX)
project(MY_PROJECT)
endif()
set(CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo" CACHE STRING "" FORCE)
#======================= INCLUSION OF Qt =======================#
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_PREFIX_PATH $ENV{QTDIR})
find_package(Qt6Core REQUIRED)
find_package(Qt6Widgets REQUIRED)
#=================== INCLUSION OF Project Files ====================#
set(FORMS_DIR "${CMAKE_SOURCE_DIR}/forms")
set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include")
set(SOURCE_DIR "${CMAKE_SOURCE_DIR}/src")
include_directories(${FORMS_DIR})
include_directories(${INCLUDE_DIR})
include_directories(${SOURCE_DIR})
file(GLOB_RECURSE SOURCES
"${FORMS_DIR}/*.ui"
"${FORMS_DIR}/*.qrc"
"${INCLUDE_DIR}/*.h"
"${SOURCE_DIR}/*.cpp"
)
#=================== SETUP EXECTUABLE ====================#
# Enable debug logging on RELWITHDEBINFO configuration
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS
$<$<CONFIG:RELWITHDEBINFO>:QT_MESSAGELOGCONTEXT>
)
# Add the forms directory to the AUTOUIC search paths
set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_AUTOUIC_SEARCH_PATHS} ${FORMS_DIR})
# Add the executable
if (WIN32)
add_executable(MY_PROJECT WIN32 ${SOURCES})
elseif(UNIX)
add_executable(MY_PROJECT ${SOURCES})
endif()
# Add the target includes for MY_PROJECT
target_include_directories(MY_PROJECT PRIVATE ${FORMS_DIR})
target_include_directories(MY_PROJECT PRIVATE ${INCLUDE_DIR})
target_include_directories(MY_PROJECT PRIVATE ${SOURCE_DIR})
#===================== LINKING LIBRARIES =======================#
target_link_libraries(MY_PROJECT Qt6::Widgets)
The Exec CMake file:
cmake_minimum_required(VERSION 3.14)
project(somexec)
add_definitions(${MY_PROJECT_DEFINITIONS})
include_directories(${MY_PROJECT_INCLUDE_DIRS})
if (WIN32)
add_executable(${PROJECT_NAME} WIN32 main.cpp)
elseif(UNIX)
add_executable(${PROJECT_NAME} main.cpp)
endif()
target_link_libraries(${PROJECT_NAME} MY_PROJECT)
Here is the codebase.
EDIT:
Finally the code seems to be working, courtesy of Guillaume Racicot's multiple edits and fixes. I am going to leave this repository public in case anyone wants to see the codebase. Also, right now, I don't understand all the CMake commands that he has used, will try to understand those as well
The CMake code from the tutorial uses very old fashioned and outdated CMake practices. It works when creating a simple project, but won't work when doing libraries. To share files between projects, you need to export the CMake targets. You can do that by creating this file first:
cmake/yourlibrary-config.cmake
include(CMakeFindDependencyMacro)
# write it like you find_package of your cmake scripts
find_dependency(Qt6 COMPONENTS Core Widgets REQUIRED)
include("${CMAKE_CURRENT_LIST_DIR}/yourlibrary-targets.cmake")
Then, add this to your main project cmake files. You should have a CMake file that looks like this:
cmake_minimum_required(VERSION 3.21)
project(yourlibrary CXX)
# First, create your library. List all the files one by one. Don't use globs
add_library(yourlibrary src/mainwindow.cpp)
# Then add an alias of your library to enable target syntax
add_library(yourlibrary::yourlibrary ALIAS yourlibrary)
# Automatically generate Qt ui and moc
set_target_properties(yourlibrary PROPERTIES
AUTOUIC ON
AUTOMOC ON
AUTOUIC_SEARCH_PATHS forms
)
# Then, include gnuinstalldir to get the platform's standard directories:
include(GNUInstallDirs)
# Then, carefully add your include directories. All of your `target_include_directories` must look like this
target_include_directories(yourlibrary PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> # include directory in your build tree
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}> # include directory when installed
)
# Then, include Qt and link qt
find_package(Qt6 COMPONENTS Core Widgets REQUIRED)
target_link_libraries(yourlibrary PUBLIC Qt6::Core Qt6::Widgets)
# Now, create the install script. We install all header files under `include/yourlibrary` to install them in `<prefix>/include/yourlibrary`:
install(
DIRECTORY include/yourlibrary
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp" PATTERN "*.h"
)
# We add `yourlibrary` target into the export set.
# The export set will contain all targets to be imported by the other project.
# It also installs the library to the install script so they are installed:
install(TARGETS yourlibrary EXPORT yourlibrary-targets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
# Now, we install the export set. This will generate a CMake file exporting all the target for other projects to use:
install(EXPORT yourlibrary-targets
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yourlibrary"
NAMESPACE yourlibrary::
)
# Now, we also export the current buildtree. Other project will be able to import the project directly from a build dir:
configure_file(cmake/yourlibrary-config.cmake yourlibrary-config.cmake COPYONLY)
export(
EXPORT yourlibrary-targets
NAMESPACE yourlibrary::
FILE "${PROJECT_BINARY_DIR}/yourlibrary-targets.cmake"
)
# The file we created earlier:
install(
FILES cmake/yourlibrary-config.cmake
DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/yourlibrary"
)
I omitted the installation of the generated headers since they are usually considered private for the project.
For the library, have a source tree looking like this:
yourlibrary
├── cmake
│   └── yourlibrary-config.cmake
├── forms
│   └── mainwindow.ui
├── include
│   └── yourlibrary
│   └── mainwindow.h
├── src
│ └── mainwindow.cpp
└── CMakeLists.txt
To use the library, you have two choices.
Either you embed it in your project using add_subdirectory(yourlibrary)
Either you build it separately and use find_package(yourlibrary REQUIRED)
You can't do both.
I usually prefer using find_package, but it require a package manager to avoid manually building all dependencies.
If you use add_subdirectory
Then remove find_package(yourlibrary REQUIRED) from your project. Simply add the directory and have your main project file look like this:
cmake_minimum_required(VERSION 3.21)
project(exec LANGUAGES CXX)
# Embed the project
add_subdirectory(yourlibrary)
add_executable(myexec main.cpp)
target_link_libraries(myexec PUBLIC yourlibrary::yourlibrary)
I assume a project tree like this:
SampleProject
├── yourlibrary
│ └── ...
├── CMakeLists.txt
└── main.cpp
If you build yourlibrary separately
First, build the library and (optionally) install it in a known location.
Now, you can build your library that uses Qt. You will need a CMakeLists.txt that looks like this:
cmake_minimum_required(VERSION 3.21)
project(myexec LANGUAGES CXX)
# also finds Qt and all
find_package(yourlibrary REQUIRED)
add_executable(myexec main.cpp)
target_link_libraries(myexec PUBLIC yourlibrary::yourlibrary)
If you install the yourlibrary library, it will simply work. If you want to use it from its build tree, simply run the CMake command with -DCMAKE_PREFIX_PATH=/path/to/yourlibrary/build. It should point to the build directory of the library, or the installation prefix if you installed it in a custom location.

CMake - Include directories outside project

So I want to include a global header file that is in a different folder. The code for the CMakeList.txt is below. In my .cpp when I include something from the local include folder it works, but not for the something that is in a different folder.
cmake_minimum_required(VERSION 2.8)
#Project name
project(Server-Client)
#Add all cpp files as source files
file(GLOB_RECURSE SOURCES "src/*.cpp")
#Build executable 'Server' with all files in SOURCES
add_executable(Server ${SOURCES})
#Include all files in include directory
include_directories("include")
target_include_directories(Server PUBLIC "../../GlobalFiles/include")
find_package(Threads REQUIRED)
#Build executable 'localization' with all files in SOURCES
target_link_libraries(Server ${CMAKE_THREAD_LIBS_INIT})
Don't use a relative paths, instead use CMAKE_CURRENT_SOURCE_DIR variable like this:
target_include_directories(Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../../GlobalFiles/include")
Other than that, it might be a good idea to use a Macro to find the global header you are looking for.

CMake + Qt Creator: Add header files to project files

If I have .h and .cpp files in the directory src, where the .cpp files include the .h files, using these commands in CMake:
aux_source_directory(src SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})
And opening that CMake file in Qt Creator, gets all the files (sources + headers) in the list of project files (the file tree on the left by default).
Now, on the other hand, if I put all the .h files in a directory include, and use this:
include_directories(include)
aux_source_directory(src SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})
The header files disappear from the project files!
How can I keep the header files in that directory, and still have them listed in Qt Creator's project files?
You shouldn't use aux_source_directory() for your task. That command is for something different. Just list the source files (or put them in a variable).
You shouldn't use include_directory() for defining include directories any more. This command will just populate the -I flag of the compiler. Define a variable with the header files and add that to the executable.
In case you don't want to list every file manually, use file(GLOB ...). But be aware of the caveats mentioned frequently all over the web with using that command.
Afterwards, tell CMake to populate the -I flag only for that executable with the include directory. That way, other targets don't get polluted by includes, they shouldn't use.
set(SOURCES
src/main.cpp
src/whatever.cpp)
set(HEADERS
include/whatever.h)
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
target_include_directories(${PROJECT_NAME} PUBLIC include)
I add my header files always explicit to avoid any surprise.
But on MacOS using QtCreator 4.2.0 and cmake 3.7.1 I can't reproduce your issue.
However I recommend to use following structure to know which files are within project and to trigger update of cmake's data during update of CMakeLists.txt.
In project/CMakeLists.txt:
add_subdirectory(src)
include_directory(include)
add_executable(foo ${SRC_LIST})
In project/src/CMakeLists.txt:
set(SRC_LIST
${SRC_LIST}
${CMAKE_CURRENT_SOURCE_DIR}/a.cpp
${CMAKE_CURRENT_SOURCE_DIR}/b.cpp
PARENT_SCOPE
)

CMakeLists.txt add subfolder which represents namespace (just for organization)

Consider the following setup:
-project
--src
---CMakeLists.txt
---main.cpp
---Application.cpp
---Application.hpp
---subfolder
----SomeClass.cpp
----SomeClass.hpp
--bin
And consider this CMakeLists.txt
project(SampleProject)
cmake_minimum_required(VERSION 2.8)
aux_source_directory(. SRC_LIST)
# Include directories
INCLUDE_DIRECTORIES("subfolder")
# Executable
add_executable(${PROJECT_NAME} ${SRC_LIST})
Now, as long as I had all my classes in the same folder (src) everything worked perfectly fine.
But now I want to restructure my application a bit. I want to build a folder-hierarchy that represents the namespaces.
Of course in my includes I would then use
#include "subfolder/SomeClass.hpp"
but it doesn't work that way. I had a look at the manpage but there are so many options in CMake and it's often talking about standalone libraries that have their own CMakeLists.txt... I'm not that far yet. I just want to add a subfolder, that's all.
Until now I've used QMake for my C++ projects, but I wanted to dive into CMake now.
Are there any useful tutorials out there? I've found a few, but they they don't cover the basics.
The recommened way is to use a CMakeLists.txt for every subdirectory in existence. If you want to have subdirectories and organize them without having to create multiple CMakeLists.txt files, you can create one in the main directory with those contents:
project(SampleProject)
cmake_minimum_required(VERSION 2.8)
include_directories(src)
file(GLOB_RECURSE SRC_LIST *.c* *.h*)
# Executable
add_executable(${PROJECT_NAME} ${SRC_LIST})
Using aux_source_directory is used mainly for template related things. Also it's common practice to use a toplevel CMakeLists.txt, which includes the further files, has common project settings etc.:
<project>
|
+- CMakeLists.txt
|
+- src/
|
+-- CMakeLists.txt
|
+-- main.cpp
|
…
So this would look like:
CMakeLists.txt (Project dir):
project(SampleProject)
cmake_minimum_required(VERSION 2.8)
include_directories(src) # Add 'src' to include paths
subdirs(src) # Includes the 'src' directory and its cmake file
# ...
Now you can use the include path as expected.
CMakeLists.txt (src dir):
# Better add src files this way:
add_executable(${PROJECT_NAME} main.cpp Application.cpp)
subdirs(subfolder) # TODO: handle subfolder
The subfolder can be added through an additional library target, that is linked to your executable. Usually there's another CMakeLists.txt file in there.
Also make sure your cmake cache is updated; best you recreate it.

Nested CMake projects multiple definition error when adding header-only library

I have a C++ library organized like:
Library/
CMakeLists.txt
main.cpp
src/
file1.cpp
include/
file1.h
Domains/
CMakeLists.txt
BlackJack/
CMakeLists.txt
src/
foo1.cpp
include/
foo1.h
...
libs/
tapkee/
CMakeLists.txt
include/
foo.hpp
....
Each library(i.e. Domains) contains linking and simple code for organizational purposes:
project(Domains)
cmake_minimum_required(VERSION 2.8)
add_subdirectory(IDomain)
add_subdirectory(Blackjack)
add_library(Domains STATIC)
target_link_libraries(Domains IDomain BLACKJACK)
Each sub-library (i.e. Blackjack) has a really simple CMakeLists.txt that just compiles the library (add_library).
This has been working just fine, until now.
I'm adding a third-party library (http://tapkee.lisitsyn.me/). This is a header-only library. It looks like CMake doesn't support this, but I found some work-arounds. I create a empty dummy.cpp file. Here is the CMakeLists for the third-party library:
project (TAPKEE)
file(GLOB TAPKEE_INCLUDE
"include/tapkee/*.hpp"
"include/tapkee/callbacks/*.hpp"
"include/tapkee/defines/*.hpp"
"include/tapkee/external/*.hpp"
"include/tapkee/neighbors/*.hpp"
"include/tapkee/parameters/*.hpp"
"include/tapkee/routines/*.hpp"
"include/tapkee/traits/*.hpp"
"include/tapkee/utils/*.hpp"
)
add_library(${PROJECT_NAME} header_only_BS.cpp ${TAPKEE_INCLUDE})
Here is the weird thing: My main.cpp and anything in the root src directory can include this tapkee library just fine, but none of the sub-libraries can (i.e. BlackJack). I get thousands of redefinition linker errors for the functions within tapkee.
Here is an example of the top-level CMakeLists:
project(ManifoldLearning)
#Root
set(SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src)
set(INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include)
#Domains
set(BLACKJACK_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Domains/Blackjack/include)
set(TAPKEE_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libs/tapkee/include/)
include_directories(${INCLUDE_DIR} ${SRC_DIR})
include_directories(${TAPKEE_INCLUDE_DIR})
include_directories(${BLACKJACK_INCLUDE_DIR})
add_subdirectory(Domains)
#add_subdirectory(libs/tapkee)
add_executable(${PROJECT_NAME} ${SRC_LIST} ${HEADER_LIST})
target_link_libraries(${PROJECT_NAME} Domains)
I've been stuck on this for days, and any help/advice would be appreciated.
Update: I've removed the inclusion of the tapkee CMakeLists.txt (add_subdirectory) as per Antonio's direction. I still get redefinition errors, which I find strange. It's now not compiled anywhere and only included in BlackJack/src/foo1.cpp