CMake building library and executable once and linking it - c++

I'm trying to build two projects using cmake at same-time.
My folder structure is as follows:
project
├── CMakeLists.txt
├── build
├── out
├── lib
├── yanthra_engine
│  ├── CMakeLists.txt
│   └── ...
└───sandbox
├── CMakeLists.txt
└── ...
main CmakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
project(yanthra_console VERSION 0.1 DESCRIPTION "A 3d Game Engine.")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fexceptions")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Release;Debug" CACHE STRING "Build type selections" FORCE)
add_subdirectory(yanthra_engine)
add_subdirectory(sandbox)
yanthra_engine/CMakeLists.txt
set(THIRD_PARTY_DIR "../../../third-party")
set(MAIN_SOURCE_DIR "../../main/src")
include_directories(${THIRD_PARTY_DIR}/SDL/include)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib )
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../lib)
file(GLOB_RECURSE CPP_HEADERS ${MAIN_SOURCE_DIR}/*.hpp)
file(GLOB_RECURSE CPP_SOURCES ${MAIN_SOURCE_DIR}/*.cpp)
add_library(
yanthra
SHARED
${CPP_HEADERS}
${CPP_SOURCES}
)
set (CMAKE_SHARED_LINKER_FLAGS "-F../yanthra_engine/Frameworks -framework SDL2 -framework OpenGL")
sandbox/CMakeLists.txt
set(THIRD_PARTY_DIR "../../main")
set(MAIN_SOURCE_DIR "./src")
include_directories(${THIRD_PARTY_DIR}/include)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../out)
file(GLOB_RECURSE CPP_HEADERS ${MAIN_SOURCE_DIR}/includes/*.h)
file(GLOB_RECURSE CPP_SOURCES ${MAIN_SOURCE_DIR}/*.cpp)
file(GLOB_RECURSE LIB ${MAIN_SOURCE_DIR}/../lib/*.dylib)
add_executable(
yanthra_sandbox
${CPP_HEADERS}
${CPP_SOURCES}
)
set_target_properties(
yanthra_sandbox
PROPERTIES
LINK_FLAGS
"-F../yanthra_engine/Frameworks -framework SDL2 -framework OpenGL"
)
target_link_libraries(yanthra_sandbox PRIVATE ${LIB})
I would like to know if I'm generating library file in each build mode, how will I link it with it's corresponding executable, given the fact that each mode builds its output to to its own folder i.e for library its lib/Debug (for debug mode) and for executable its out/Debug.

You don't seem to link the link the yanthra target. You should do this though, since this will automatically choose the library compiled with the current configuration.
sandbox/CMakeLists.txt
...
target_link_libraries(yanthra_sandbox PRIVATE yanthra ${LIB})
...
As for importing the other libs: It would be preferrable to use find_library. This should automatically set the link options and make adding them for yanthra_sandbox unnecessary.
yanthra_engine/CMakeLists.txt
...
list(APPEND CMAKE_FRAMEWORK_PATH "${CMAKE_CURRENT_SOURCE_DIR}/Frameworks")
find_library(SDL2_LIB SDL2 REQUIRED)
find_library(OPEN_GL_LIB OpenGL REQUIRED)
target_link_library(yanthra PUBLIC "${SDL2_LIB}" "${OPEN_GL_LIB}")
This should allow you to remove the compiler flags from both targets. If there are no dependencies in the lib directory you could also remove the search for the libraries on the file system/

Related

How to include external modules headers when FetchContent_Declare is used?

I want to use modern CMake project structure convention in my new project to follow good practices and to be consistent with many other projects, but I'm facing problem with include path.
My directories structure looks as follows:
.
├── build
├── CMakeLists.txt
├── extern
│ └── CMakeLists.txt
└── src
├── CMakeLists.txt
└── main.cpp
I want to use FetchContent_Declare instead of git submodules to manage the third-party libraries.
The root CMakeLists.txt:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
set(ProjectName "MyApp")
project(${ProjectName})
add_subdirectory(extern)
add_subdirectory(src)
The extern CMakeLists.txt:
cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
include(FetchContent)
FetchContent_Declare(
extern_spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.8.2)
FetchContent_GetProperties(extern_spdlog)
if(NOT extern_spdlog_POPULATED)
FetchContent_Populate(extern_spdlog)
add_subdirectory(
${extern_spdlog_SOURCE_DIR}
${extern_spdlog_BINARY_DIR}
)
endif()
The src CMakeLists.txt:
cmake_minimum_required(VERSION 3.16)
set(BINARY ${CMAKE_PROJECT_NAME})
file(GLOB_RECURSE SOURCES LIST_DIRECTORIES true *.h *.cpp)
set(SOURCES ${SOURCES})
add_executable(${BINARY}_run ${SOURCES})
add_library(${BINARY}_lib STATIC ${SOURCES})
target_link_libraries(${BINARY}_run extern_spdlog)
And the main.cpp:
#include <spdlog/spdlog.h>
int main(int argc, char* argv[]) {
spdlog::info("Welcome to spdlog!");
return 0;
}
CMake executes and the spdlog library is cloned properly and can be found in the build directory at: build/_deps/extern_spdlog-src.
If I call make in the build directory then I get fatal error: spdlog/spdlog.h: No such file or directory.
How to add include directories for the external libraries used in the application?
I was initially confused about this as well. It doesn't help that the names of the FetchContent targets often match the names of the imported targets. What worked for me was similar (though not identical) to what the first comment states. I used target_link_libraries(target spdlog::spdlog) (if you want to use the pre-compiled library or spdlog::spdlog_header_only if you want to use the header source only version.

add_custom_target not executing with multiple executable in a CMakeLists file

I got a situation where another Makefile is executed from a CMakeLists.txt file.
Here is my root directory structure.
.
├── client.cpp
├── CMakeLists.txt
├── proto_lib
│   ├── Makefile
│   └── math.proto
└── server.cpp
This is a simple grpc test code where I am trying to compile a proto files using a Makefile create a static library. And then link that library to the final targets in the CMakefile. Makefile is working fine. CMakefile is also working fine with one executable.
Here is the CMakefile:
cmake_minimum_required(VERSION 3.5)
project(client-server)
message(STATUS "Compiling Network Function : ${PROJECT_NAME}")
set(CMAKE_CXX_STANDARD 11)
message(STATUS "Project Directory: ${PROJECT_SOURCE_DIR}")
set(CMAKE_BINARY_DIR ${CMAKE_SOURCE_DIR}/build)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
set(LIB_FILE ${CMAKE_CURRENT_SOURCE_DIR}/proto_lib/libmylib.a)
add_custom_target(libmylib.a ALL
COMMAND make
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/proto_lib
COMMENT "grpc headers and libraries are generating..."
)
add_executable(server server.cpp)
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -g")
set(BASIC_LIBRARIES "-lpthread")
set(CMAKE_LINK_WHAT_YOU_USE ON)
set(LIBDIR "/usr/local/lib")
find_library(proto_location NAMES protobuf PATHS ${LIBDIR})
find_library(grpc_location NAMES grpc++ PATHS ${LIBDIR})
find_library(grpc_reflection_location NAMES grpc++_reflection PATHS ${LIBDIR})
find_library(dl_location NAMES dl PATHS ${LIBDIR})
find_library(grpc_location2 NAMES grpc PATHS ${LIBDIR})
if(proto_location)
message(STATUS "protobuf library found at " ${proto_location})
endif()
if(grpc_location)
message(STATUS "grpc++ found at " ${grpc_location})
endif()
if(grpc_reflection_location)
message(STATUS "grpc_reflection found at " ${grpc_reflection_location})
endif()
if(dl_location)
message(STATUS "dl found at " ${dl_location})
endif()
if(grpc_location2)
message(STATUS "grpc found at " ${grpc_location2})
endif()
target_link_libraries(server ${proto_location}
${LIB_FILE}
${grpc_location}
${grpc_reflection_location}
${dl_location}
${grpc_location2}
${BASIC_LIBRARIES}
)
When I enter the command make after CMake . in the root directory.
it starts with following output...
Scanning dependencies of target libmylib.a
[ 33%] grpc headers and libraries are generating...
[ 33%] Built target libmylib.a
etc.. etc..
and it succeeds.(The internal Makefile creates header files and static objects using proto and grpc libraries. After that server.cpp can include the headers from inside the proto_lib/ folder and CMake build succeeds)
But when I add one more executable at the end of CMakeLists.txt,
add_executable(client client.cpp)
target_link_libraries(client ${proto_location}
${LIB_FILE}
${grpc_location}
${grpc_reflection_location}
${dl_location}
${grpc_location2}
${BASIC_LIBRARIES}
)
and then cmake . && make
does not start with internal Makefile build. Instead, it starts with client build.
Scanning dependencies of target client
[ 20%] Building CXX object CMakeFiles/client.dir/client.cpp.o
/home/deka/Academic/gemini/learning/client_server_async/advanced1/client.cpp:10:10: fatal error: proto_lib/math.grpc.pb.h: No such file or directory
#include "proto_lib/math.grpc.pb.h"
Because proto_lib/Makefile is not executed first in this case therefore math.grpc.pb.h is not generated and that is what the above error is showing.
My question is: Why adding one more executable does not trigger the internal Makefile build at the start?
Thanks!

how to make GUI Qt project with cmake in Qt Creator with .ui files

I want to created a project that uses cmake as it's build system sn QTCreator
.
├── CMakeLists.txt
├── CMakeLists.txt.user
├── main.cpp
├── notepad.cpp
├── notepad.h
└── notepad.ui
here is my cmake file:-
cmake_minimum_required(VERSION 2.8)
project(Notepad)
set(CMAKE_CXX_FLAGS " ${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic -std=gnu++14")
find_package(Qt5Widgets REQUIRED)
set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
add_executable(${PROJECT_NAME} main.cpp notepad.cpp)
target_link_libraries(Notepad Qt5::Widgets)
When I try to build it I get following error:-
AutoUic subprocess error
------------------------
uic failed for
"/home/jucyref/Development/C++/Test/notepad/notepad.ui"
included by
"/home/jucyref/Development/C++/Test/notepad/notepad.cpp"
Command
-------
/usr/bin/uic -o /home/jucyref/Development/C++/Test/build-notepad-C_C-Default/Notepad_autogen/include/ui_notepad.h /home/jucyref/Development/C++/Test/notepad/notepad.ui
Output
------
File '/home/jucyref/Development/C++/Test/notepad/notepad.ui' is not valid
.QtCreator's Design mode.it is disabled by default .what should I do. I am not very familiar with cmake and qt
You are missing UI headers in add_executable
more details on https://wiki.qt.io/Using_CMake_build_system
set ( SOURCES
..cpp
)
set ( MOC_HEADERS
..h
)
set ( UIS
notepad.ui
)
set ( RESOURCES
..qrc
)
QT5_WRAP_UI( UI_HEADERS ${UIS} )
add_executable( PROJECT_NAME ${SOURCES} ${MOC_SRCS} ${RES_SOURCES} ${UI_HEADERS} )

cmake the following imported targets are referenced, but are missing

I have a repository with two libraries (liba and libb) whereas liba
depends on libb. They are part of a single repository and are built using a single cmake "context". The file structure is shown below:
├── CMakeLists.txt
├── liba
│ ├── CMakeLists.txt
│ ├── internal
│ │ └── private.hh
│ ├── module.cc
│ ├── module.hh
└── libb
├── CMakeLists.txt
├── other.cc
└── other.hh
Everything compiles and installs without any issues. Although, if I try to create a new project that depends on liba. Like so:
cmake_minimum_required(VERSION 3.5)
find_package(Threads REQUIRED)
find_package(OpenCV REQUIRED)
find_package(liba REQUIRED)
add_executable(exec exec.cc)
target_link_libraries(exec PRIVATE is::liba)
I get the following error:
CMake Error at CMakeLists.txt:5 (find_package):
Found package configuration file:
/home/hodor/is-sdk/lib/cmake/liba/libaConfig.cmake
but it set liba_FOUND to FALSE so package "liba" is considered to be NOT
FOUND. Reason given by package:
The following imported targets are referenced, but are missing: is::libb
What I am missing here? Repository link for context
CMakeLists that generates liba:
cmake_minimum_required(VERSION 3.5)
include(GNUInstallDirs)
find_package(OpenCV REQUIRED core)
find_package(Threads REQUIRED)
list(APPEND liba_public_headers
"module.hh"
)
list(APPEND liba_private_headers
"internal/private.hh"
)
list(APPEND liba_sources
"module.cc"
${liba_public_headers}
${liba_private_headers}
)
add_library(liba ${liba_sources})
target_include_directories(
liba
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}> # for headers when building
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}> # for generated files in build mode
$<INSTALL_INTERFACE:include/is/liba> # for clients in install mode
PRIVATE
${OpenCV_INCLUDE_DIRS}
)
target_link_libraries(liba
PRIVATE
is::libb
${OpenCV_LIBRARIES}
Threads::Threads
)
set_property(TARGET liba PROPERTY CXX_STANDARD 11)
install(
TARGETS liba EXPORT libaTargets
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
install(FILES ${liba_public_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/is/liba)
install(
EXPORT libaTargets
FILE libaConfig.cmake
NAMESPACE is::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/liba
)
CMakeLists that generates libb:
cmake_minimum_required(VERSION 3.5)
include(GNUInstallDirs)
list(APPEND libb_public_headers
"other.hh"
)
list(APPEND libb_sources
"other.cc"
${libb_public_headers}
)
add_library(libb ${libb_sources})
target_include_directories(
libb
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}> # for headers when building
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}> # for generated files in build mode
$<INSTALL_INTERFACE:include/is/libb> # for clients in install mode
)
install(
TARGETS libb EXPORT libbTargets
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}"
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}"
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
install(FILES ${libb_public_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/is/libb)
install(
EXPORT libbTargets
FILE libbConfig.cmake
NAMESPACE is::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/libb
)
add_library(is::libb ALIAS libb)
Top level CMakeLists that includes liba and libb:
cmake_minimum_required(VERSION 3.5)
add_subdirectory(liba)
add_subdirectory(libb)
After watching C++Now 2017: Daniel Pfeifer “Effective CMake"
I now realized what was missing in my configuration. As mentioned by #Tsyvarev the problem was about libaConfig.cmake.
When exporting a library with dependencies you should export mylibraryTargets.cmake, like so:
install(
EXPORT libaTargets
FILE libaTargets.cmake
NAMESPACE is::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/liba
)
And then manually (a bit disappointed here I have to say) write a mylibraryConfig.cmake with all the dependencies like so:
include(CMakeFindDependencyMacro)
find_dependency(libb)
find_dependency(OpenCV)
find_dependency(Threads)
include("${CMAKE_CURRENT_LIST_DIR}/libaTargets.cmake")

Framework manage libraries

I have a framework wich depends on multiple third party libraries. I would like to share my framework easily. For example the user needs only my includes and add my lib to use my framework, and not all the dependencies.
I use CMake to create my libs, but I am still trying to understand how it works.
The hierarchy of the project
test
├── CMakeLists.txt
├── libA
│   ├── CMakeLists.txt
│   ├── libA.cpp
│   ├── libA.hpp
├── libB
│   ├── CMakeLists.txt
│   ├── libB.cpp
│   ├── libB.hpp
└── test
├── CMakeLists.txt
└── main.cpp
The libB depends on the libA, and I would like to add only the libB to make the project works.
The content of the main CMakeLists.txt located in test/:
cmake_minimum_required (VERSION 2.8.11)
project (C CXX)
include(CheckCXXCompilerFlag)
add_subdirectory("libA")
add_subdirectory("libB")
add_subdirectory("test")
The content of the main CMakeLists.txt located in test/libA:
cmake_minimum_required (VERSION 2.8.11)
project (A CXX)
include(CheckCXXCompilerFlag)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++14")
file(GLOB SOURCES "*.cpp")
add_library(A STATIC ${SOURCES})
The content of the main CMakeLists.txt located in test/libB:
cmake_minimum_required (VERSION 2.8.11)
project (B CXX)
include(CheckCXXCompilerFlag)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++14")
set(CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS} "-static")
include_directories("../libA")
link_directories("../libA")
file(GLOB SOURCES "*.cpp")
add_library(B STATIC ${SOURCES})
target_link_libraries(B A)
The content of the main CMakeLists.txt located in test/test:
cmake_minimum_required (VERSION 2.8.11)
project (C CXX)
include(CheckCXXCompilerFlag)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++14")
include_directories(../libA)
include_directories(../libB)
link_directories(../build/libB)
link_directories(../build/libA)
add_executable(C main.cpp)
target_link_libraries(C B)
If I run the main CMake all works fine, the standalone is well generated.
But if I want to create only the exe by going into test/test and running "cmake . && make", I have an undefined reference to addL(int, int). To make it works I have to add target_link_libraries(C A) at the end of the CMakeLists. Is it normal ? Is it possible to add only the main library without its dependencies ?
Command invocation
target_link_libraries(C B)
have different meanings in your use-cases.
In "all" use-case 'B' is (previously) defined as a CMake target, so CMake knows location of the library (link_directories is not used in that case), and automatically propagates all dependencies of library 'B' to executable 'C'.
In "test-only" use case there is no CMake target named 'B', so it is just transformed to linker option -lB. Linker searches appropriate library file under link directories (link_directories is needed in that case). But information about 'B' dependencies is lost, so automatic linking with A isn't performed.
Your project may provide a standalone CMake script which can be included by a user of your library. The script should define all needed dependencies. Common "types" of such scripts are FindXXX.cmake and XXXConfig.cmake, which can be included via find_package(XXX) command.