I have the following layout which contains multiple projects separate directories (It's a CPP project):
Demo
├── Lib
│ ├── CMakeLists.txt
│ ├── inc
│ └── lib
│ ├── crypto_ic.so
│ └── crypto.so
├── Proj1
│ ├── App1
│ └── CMakeLists.txt
└── Proj2
├── App2
└── CMakeLists.txt
I created one separate third-party-lib.bb file to install crypto_ic.so,crypto.so into /usr/lib/ in the target using do_install_append.
All applications are depending on third-party libraries. There are no parent cmake for proj1,proj2 those are independent projects built by *.bb files using yocto.
Now I need to integrate the third party library for all the applications. I created one interface library for the third party headers and using find_package I am able to find the headers and the compilation is working fine.
CMakeLists.txt(Lib)
cmake_minimum_required(VERSION 3.8)
project(crypto VERSION 1.0.0 LANGUAGES CXX)
include(GNUInstallDirs)
find_package(Boost REQUIRED)
add_library(crypto INTERFACE)
target_link_libraries(crypto INTERFACE
Boost::boost
)
target_include_directories(crypto INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/inc/>
$<INSTALL_INTERFACE:include>
)
# Create and Install *-config.cmake,*-version.cmake files
# Install exported targets, i.e. *-targets.cmake
# Install header files
CMakeLists.txt(Proj1)
cmake_minimum_required(VERSION 3.8)
project(App1 VERSION 1.0.0 LANGUAGES CXX)
include(GNUInstallDirs)
find_package(crypto REQUIRED)
add_executable(run Run.cpp)
target_link_libraries(run PUBLIC
crypto
)
But the problem is while linking I am getting a linker error.
Run.cpp: undefined reference to 'Intialize(std::string hashName)'
Initialize function definition is available in the shared library.
Any thoughts on how to fix the issue and how to remove the hardcode path to the library in CMake?.
I am new to CMake and not sure this is a good approach to follow, only reason I created the Interface library is to install the headers in target and find the package. I can modify it if there are some better solutions?
Edit:
I found the issue in the cmake, I didn't link the shared library files (*.so) in the application that's the reason I am getting the linker error. But I am not getting how to link that from the target?
Related
I'm writing a C++ library which has the following structure:
my_project
├── include
├── lib
├── src
│ ├── sourceA.cpp
│ └── sourceB.cpp
├── test
│ ├── test.cpp
│ └── CMakeLists.txt
└── CMakeLists.txt
Now I have multiple questions:
How can I layout CMake files in a way so that I can build a test app that uses it from within the library? Here's what I've got so far:
test/CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project(supabase VERSION 0.2.0.0 LANGUAGES C CXX)
add_subdirectory(..)
add_executable(test test.cpp)
target_link_libraries(test PRIVATE mylib)
MakeLists.txt
add_library(mylib)
target_include_directories(mylib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include");
But CMake complains that
directory "C:/dev/myproject" is not a subdirectory of "C:/dev/myproject/test"
So I could of course create a top level CMakeLists.txt that contains the project information and builds test.cpp. BUT: Of course don't want other developers to build the test routines when they just want to build the lib. Which leads me to question number..
Is there a standardised way to layout the project structure so that it satisfies the following requirements:
I can build testscripts from within library and use it at the same time
Other devs can just include my top level CMakeLists.txt and build the library without baggage.
What is best practice here?
I want to create a .dll library with all its dependencies packed inside the .dll.
However, there seems to be no easy way to achieve that with Cmake. My setup:
cmake_minimum_required(VERSION 3.0.0)
project(Main VERSION 0.1.0)
add_library(Main SHARED Main.cpp)
find_package(libzippp REQUIRED)
target_link_libraries(Main PRIVATE libzippp::libzippp)
This will produce both Main.dll but also libzippp.dll.
I would like to have libzippp.dll packed (statically linked) into Main.dll.
Of course you can't pack one DLL into another. You have to make libzippp a static library in the first place. To do this, build libzippp with BUILD_SHARED_LIBS set to NO at the CMake command line. Then libzippp::libzippp will be a static library when you go to find_package it.
This is easy enough to show steps for:
$ git clone git#github.com:ctabin/libzippp.git
$ cmake -S libzippp -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=NO -DCMAKE_INSTALL_PREFIX=$PWD/local -DLIBZIPPP_BUILD_TESTS=NO
$ cmake --build build --target install
$ tree local
local/
├── include
│ └── libzippp
│ └── libzippp.h
├── lib
│ └── libzippp_static.a
└── share
└── libzippp
├── FindLIBZIP.cmake
├── libzipppConfig.cmake
├── libzipppConfigVersion.cmake
├── libzipppTargets.cmake
└── libzipppTargets-release.cmake
I'm not very familiar with CMake and still find it quite confusing. I have a project that has a server and client that I want to be able to run independent of each other but that builds together into the same directory (specifically the top level project build directory kind of like how games have the server launcher and game launcher in the same directory) Currently it just creates a builds directory in each sub project, so one in client, one in server etc.
This is my current project structure
.
├── CMakeLists.txt
├── builds
│ ├── debug
│ └── release
├── client
│ ├── CMakeLists.txt
│ ├── assets
│ └── source
│ └── Main.cpp
├── documentation
├── libraries
│ ├── glfw-3.3.7
│ └── glm
├── server
│ ├── CMakeLists.txt
│ └── source
│ └── Main.cpp
└── shared
├── PlatformDetection.h
├── Utility.h
├── events
└── platform
├── linux
├── macos
└── windows
This is my root CMake file
cmake_minimum_required(VERSION 3.20)
project(Game VERSION 1.0.0)
add_subdirectory(libraries/glfw-3.3.7)
add_subdirectory(client)
add_subdirectory(server)
Client CMake file
cmake_minimum_required(VERSION 3.20)
project(Launcher LANGUAGES CXX VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 23)
set(SOURCE_FILES source/Main.cpp ../shared/events/Event.h ../shared/Utility.h
source/Client.cpp source/Client.h ../shared/PlatformDetection.h ../shared/events/EventManagementSystem.cpp
../shared/events/EventManagementSystem.h)
set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
include_directories(${CMAKE_SOURCE_DIR}/libraries/glm)
include_directories(${CMAKE_SOURCE_DIR}/libraries/glfw-3.3.7/include/GLFW)
include_directories(${CMAKE_SOURCE_DIR}/shared)
add_executable(Launcher ${SOURCE_FILES})
target_link_libraries(Launcher LINK_PUBLIC glfw)
Server CMake file
cmake_minimum_required(VERSION 3.20)
project(ServerLauncher LANGUAGES CXX VERSION 1.0.0)
set(CMAKE_CXX_STANDARD 23)
set(SOURCE_FILES source/Main.cpp ../shared/events/Event.h ../shared/Utility.h
../shared/PlatformDetection.h ../shared/events/EventManagementSystem.cpp
../shared/events/EventManagementSystem.h)
include_directories(${CMAKE_SOURCE_DIR}/libraries/glm)
include_directories(${CMAKE_SOURCE_DIR}/shared)
add_executable(ServerLauncher ${SOURCE_FILES})
How can I make the client and server build into the same directory? And can these cmake file structures be improved at all? They seem quite messy and all over the place to me though that may just be due to my unfamiliarity with CMake.
You cannot have multiple subdirectories use the same build directory, but that doesn't seem what you're trying to achieve.
Assuming you don't set the variable CMAKE_RUNTIME_OUTPUT_DIRECTORY anywhere in your project, and you don't specify the RUNTIME_OUTPUT_DIRECTORY target property for any of your targets by some other means, you could simply set the variable in the toplevel CMakeLists.txt before using add_subdirectory:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin)
add_subdirectory(...)
...
Note that for distributing the program you should be using install() logic:
Client CMakeLists.txt
...
install(TARGETS Launcher RUNTIME)
Server CMakeLists.txt
...
install(TARGETS ServerLauncher RUNTIME)
Note that you may need to add logic for installing dependencies.
Using those install commands allows you to use
cmake --install <build dir> --prefix <install dir>
to install the programs locally in the default directory for binaries on the system. Furthermore it's the basis for packaging your project using cpack.
I was learning CMake for a project and feel confused when linking libraries.
I find it easier to ask with an example, as using terms I am not familiar could be misleading.
The questions are (also commented in the example codes)
how can I know what are the library name I can link to my target? Can I tell that from the build file structure, or I need to look into CMakeLists.txt of the external libraries. (e.g. the gtest-main and libhermes in the example)
How can I use a "grandchild" library that is part of the external library? (e.g. the jsi library in the example)
Example
Let's say I am writing some tests with dependency on the Hermes Library. I then write a CMakeList.txt file like
cmake_minimum_required(VERSION 3.13)
project(mytests_cxx)
set(CMAKE_CXX_STANDARD 14)
include(FetchContent)
FetchContent_Declare(
hermes
URL https://github.com/facebook/hermes/archive/d63feeb46d26fe0ca7e789fc793f409e5158b27f.zip
)
FetchContent_MakeAvailable(hermes)
enable_testing()
add_executable(
hello_test
./test.cpp
)
find_library (
LIB_HERMES
hermes # <- how can I know what is the name?
)
find_library (
LIB_JSI
jsi # <- How could I use a "grandchild" dependency, it is defined here (https://github.com/facebook/hermes/tree/main/API/jsi/jsi)
)
target_link_libraries(
hello_test
gtest_main <- Hermes seems to use gtest, how could make sure gtest_main is available?
LIB_HERMES
LIB_JSI
)
include(GoogleTest)
gtest_discover_tests(hello_test)
Running cmake to build
cmake -S . -B build -DHERMES_BUILD_SHARED_JSI=ON // I see an option defined in heremes'CMakeLists.txt but not sure if I am using it right.
the directory in build
tree ./build -L 2
./build
├── CMakeCache.txt
├── CMakeFiles
│ ├── 3.23.1
│ ├── CMakeDirectoryInformation.cmake
│ ├── CMakeError.log
│ ├── CMakeOutput.log
│ ├── CMakeRuleHashes.txt
│ ├── CMakeTmp
│ ├── Makefile.cmake
│ ├── Makefile2
│ ├── Progress
│ ├── TargetDirectories.txt
│ ├── cmake.check_cache
│ ├── hello_test.dir
│ └── progress.marks
├── CTestTestfile.cmake
├── ImportHermesc.cmake
├── Makefile
├── _deps
│ ├── hermes-build
│ ├── hermes-src
│ └── hermes-subbuild
├── bin
│ └── hermes-lit
├── cmake_install.cmake
├── compile_commands.json
└── hello_test[1]_include.cmake
then I run cmake --build build and see this error
/.../test.cpp:8:10: fatal error: 'jsi/jsi.h' file not found
#include <jsi/jsi.h>
Your question is too broad. Either ask a question about the list of libraries in cmake, or ask a question about how to compile and link with hermes, or ask a question about a specific problem with your build about including a third party. These should be 3 separate questions on this site.
how can I know what are the library name I can link to my target?
There are two things that you can target_link_libraries - you can link with libraries available on your system and link with targets defined in cmake.
You can list libraries available on your system by traversing all paths outputted by ld --verbose and listing all lib*.so files in those directories. Or https://unix.stackexchange.com/questions/43187/how-do-i-get-a-list-of-shared-library-filenames-under-linux or similar. See man ld.so.
You can list all targets defined in cmake. How do I list the defined make targets from the command line? .
These lists combined consist of all possible libraries names that you can link with.
Can I tell that from the build file structure
I do not understand. No.
I need to look into CMakeLists.txt of the external libraries
Yes, usually you want to look into any external libraries CMakeLists.txt at least to check for malicious code.
You usually look into documentation of external libraries. And, because source code is a form of documentation in open-source projects, you would look into the source code, yes.
How can I use a "grandchild" library that is part of the external library?
There are multiple ways for including external projects. Cases were outlined in Correct way to use third-party libraries in cmake project .
That said, not all CMake projects work properly with add_subdirectory() (which is called by FetchContent_MakeAvailable) - there are many cases where the third-party projects CMakeLists.txt may not work with parent project. In such cases, I would go with compiling and installing the project into a subdirectory for example before running CMake or with ExternalProject_Add, and then only including the result of compilation with hand-crafted find_library and target_include_directories from the installation directory.
Overall, including a third party is not always an easy task, and yes it requires you to become intimate with the specifics of a third party project and how it works and how it was written and decide how you want it to include in your project.
I have for some time had some problems incorporating modern cmake into a project I've been working fr some while and seem to be stuck on how i should define the the individual CMakeLists, and the top-level CMakeList.
My project structure is pretty simple.
├── build
└── src
├── include
│ ├── database
│ │ ├── database.cpp
│ │ └── database.h
│ ├── match
│ │ ├── match.h
│ │ └── mathc.cpp
│ ├── record
│ │ ├── lib
│ │ ├── record.cpp
│ │ └── record.h
│ └── spectogram
│ ├── spectogram.cpp
│ └── spectrogram.h
└── main.cpp
main.cpp are linked to all the includes, and some of the includes should know the presence of other includes, meaning, I should be able to include match.h in
database.h. Some third-party libs are also going to be used, in this case I am using portaudio, download and installed using externalproject_add, which should only be visible for the include which holds the library, in this case record, should only see this.
But how I should define the individual CMakeList, is currently unknown.
I've scouted the net for a proper way of setting this up, but cannot seem to find one that I understand.
How do i define the CMakeLists for this project, and how do i make sure that the includes, are visible for the Main.cpp and the includes files that need them, and how do I make third-party-libraries visible only for the includes that it is being used for.
CMakeLists example structure tried:
CMakeLists.txt
cmake_minimum_required(VERSION 3.2)
project(soundcloud)
#add_subdirectory(src/database)
#add_subdirectory(src/match)
#add_subdirectory(src/record)
add_subdirectory(src/include/spectogram)
add_executable(cmakeDemo src/main.cpp)
SET_TARGET_PROPERTIES(cmakeDemo PROPERTIES LINKER_LANGUAGE Cxx)
target_link_libraries(cmakeDemo spectogram)
#target_link_libraries(cmakeDemo database match record spectogram)
src/include/database/CMakeLists.txt
add_library(spectogram STATIC .)
target_include_directories(spectogram PUBLIC .)
getting error message:
CMake Error: Error required internal CMake variable not set, cmake may not be built correctly.
Missing variable is:
CMAKE_Cxx_LINK_EXECUTABLE
CMake Error: Cannot determine link language for target "spectogram".
CMake Error: CMake can not determine linker language for target: spectogram
You can use the PRIVATE property of target_include_directories. When PRIVATE, it means that the include directories will be available to the sources of the target. When PUBLIC, it will also be available to whomever links to the target.
If I were writing this in a single CMakeLists.txt, I'd do this:
cmake_minimum_required(VERSION 3.0)
add_library(database STATIC src/include/database.cpp)
target_include_directories(database PUBLIC src/include/database)
################
add_library(match STATIC src/include/mathc.cpp)
target_include_directories(match PUBLIC src/include/match)
################
include(ExternalProject)
ExternalProject_Add(portAudio ${SomeCommands})
add_library(record STATIC src/include/record.cpp)
target_include_directories(record PUBLIC src/include/record) # When cmakeDemo links to this, it'll get these includes
target_include_directories(record PRIVATE src/include/record/lib) # When cmakeDemo links to this, it won't get these includes
target_link_libraries(record portAudio)
#################
add_library(spectogram STATIC src/include/spectogram.cpp)
target_include_directories(spectogram PUBLIC src/include/spectogram)
##################
add_executable(cmakeDemo src/main.cpp)
target_link_libraries(cmakeDemo database match record spectogram)
If I were to do this with distributed CMakeLists.txt, I'd split the files where the ###### lines are, fix the paths, and use add_subdirectory() to include the sub-directories from higher-level cmake files.
First, you don't link to includes, you just "include" them during compilation.
Some people put one CMakeList.txt into every directory that contains a fairly independent compilation unit.
Some people just use one big one at the the top.
Having just one CMakeList.txt file makes it easier to start, but if the project gets huge, things get messy.
For every compilation unit, you can specify the include directories with target_include_directories