CMake for Proto libraires - c++

I have two Proto files across two different folders and am trying to use CMake for building the overall project.
protofile1 has protofile2 as it's dependency.
Library1 has protofile1 as it's dependency which I can generate using protobuf_generate_cpp.
But for generating protofile1, I have protofile2 as it's dependency. How do I do this using CMake?
How do I compile proto file and make it available as a library using CMake (in folder2)?
Folder Structure:
|
|-folder1
---|-protofile1.proto
---|-library1.cc
|-folder2
---|-protofile2.proto
---|-library2.cc
CMakeLists.txt for folder1
cmake_minimum_required(VERSION 3.3)
find_package(Protobuf REQUIRED)
protobuf_generate_cpp(protofile1_cc protofile1_header protofile1.proto)
target_link_libraries(protofile1_cc INTERFACE protofile2_lib) # is this correct?
add_library(library1 INTERFACE)
target_sources(library1 INTERFACE library1.cc)
target_link_libraries(library1 INTERFACE protofile1_cc)
CMakeLists.txt for folder2
cmake_minimum_required(VERSION 3.3)
find_package(Protobuf REQUIRED)
# don't know how to do this
add_library(protofile2_lib INTERFACE) # is this correct?
target_sources(protofile2_lib INTERFACE protofile2.proto) # is this correct?

The protobuf_generate_cpp command does not define targets, but it defines CMake variables referring to the auto-generated source files (.cc, .h). These variables should be used to define your targets via add_library(). You are on the right track, but your CMakeLists.txt file in folder2 should also call protobuf_generate_cpp to process protofile2.proto as well.
Also, if you're using CMake to build both, the top-level CMake (in the parent folder to folder1 and folder2) can find Protobuf so you don't have to find it twice. Something like this should get you closer to the desired solution:
CMakeLists.txt (top-level):
cmake_minimum_required(VERSION 3.3)
find_package(Protobuf REQUIRED)
add_subdirectory(folder2)
add_subdirectory(folder1)
folder2/CMakeLists.txt
# Auto-generate the source files for protofile2.
protobuf_generate_cpp(protofile2_cc protofile2_header protofile2.proto)
# Use the CMake variables to add the generated source to the new library.
add_library(protofile2_lib SHARED ${protofile2_cc} ${protofile2_header})
# Link the protobuf libraries to this new library.
target_link_libraries(protofile2_lib PUBLIC ${Protobuf_LIBRARIES})
folder1/CMakeLists.txt
# Auto-generate the source files for protofile1.
protobuf_generate_cpp(protofile1_cc protofile1_header protofile1.proto)
# Use the CMake variables to add the generated source to the new library.
add_library(library1 SHARED ${protofile1_cc} ${protofile1_header})
# Link proto2 library to library1.
target_link_libraries(library1 INTERFACE protofile2_lib)

Related

How can I write a CMakeList.txt file for heavily nested project?

Suppose my source code has the following directory structure:
C:\USERS\PC\SOURCE\REPOS\my_app_src
├───apps {a.hh, b.cc, c.hh, d.cc}
│ └───biosimulations {main1.hh, main1.cc, x.hh, y.cc}
└───core {w.cc, x.hh, y.hh, z.cc}
└───algorithms {p.hh, p.cc, q.hh, r.cc, s.hh, s.cc}
└───trees {r.hh, r.cc, main2.hh, main2.cc}
Each folder has any number of header and source files with any name.
How can I write a CMakeList.txt file for this project?
This is scientific software.
I need to be able to use various parts of the same library to compile and build executables for multiple applications.
For example, in the above sample, main1.exe and main2.exe are supposed to be two different executable files.
Sometimes, I need to be able to switch off one or another executable from compiling.
Do you want the project to support testing, installation, and/or packaging?
No, I don't need them. I just need to be able to compile and execute the apps.
What is in core?
Model classes. e.g., Atom, Protein, Chain, etc.
Are the source files for core part of a single library or executable?
Part of executable. There is no static or dynamic library in the project.
Using add_subdirectory and add_library commands, recursively add all of the source files in the directory structure.
cmake_minimum_required(VERSION 3.x)
project(my_app)
# Add subdirectories recursively
add_subdirectory(apps)
add_subdirectory(core)
add_subdirectory(simulations)
add_subdirectory(ui)
add_subdirectory(utils)
# Create the final executable
add_executable(my_app main.cpp)
# Link the libraries to the executable
target_link_libraries(my_app core simulations apps ui utils)
Then in each subdirectories(apps,core,simulations,ui, utils) you would need to add a new CMakeLists.txt that tells which source files are in that directory and create a library.
cmake_minimum_required(VERSION 3.x)
SET(SRC_FILES
program.cpp
)
#for static libraries
add_library(core STATIC ${SRC_FILES})
#for dynamically linking libraries
add_library(core SHARED ${SRC_FILES})
# If you need executable here
# add_executable(core ${SRC_FILES})
This needs to be repeated for all subdirectories, untill all sources are covered. Above examples gives general structure, you need to define CMAKE flags as required.

Accessing an external project with add-subdirectory results in CMake Error related to export set

I have a project A that depends on spdlog. Here is the structure:
|--- dir A
...|---src
......|---CMakeLists.txt
...|---include
...|---CMakeLists.txt
|---external/3rd_party/spdlog
I am trying to access spdlog in project A by adding a subdirectory. Here is how my A/CMakeLists.txt looks like:
cmake_minimum_required(VERSION 3.9.3 FATAL_ERROR)
project(GLOBAL CXX)
add_subdirectory(../external/3rd_party/spdlog ${CMAKE_BINARY_DIR}/spdlog EXCLUDE_FROM_ALL)
add_subdirectory(src)
Here is how my A/src/CMakeLists.txt looks like:
cmake_minimum_required(VERSION 3.9.3 FATAL_ERROR)
project(Alib CXX)
if(NOT TARGET spdlog)
# Stand-alone build
find_package(spdlog_header_only REQUIRED)
endif()
add_librray(A A.cpp)
target_link_libraries(A PUBLIC spdlog_header_only)
install(TARGETS A
EXPORT ATargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(EXPORT ATargets
NAMESPACE A::
FILE ATargets.cmake
DESTINATION ${INSTALL_CONFIGDIR})
install(FILES AConfig.cmake DESTINATION ${INSTALL_CONFIGDIR})
When I try to build this, I get the following error:
CMake Error: install(EXPORT "ATargets" ...) includes target "A" which requires target "spdlog_header_only" that is not in the export set.
Please can you suggest me how to fix it?
For some reason I need to maintain the same directory structure I have shown above.
Here is a related question but does not have an answer : here
Meaining of the error
Since you use PUBLIC keyword when link your library (A) with spdlog_header_only, CMake expects that this linking is also needed for users of your library. So, when you create config file for your library (with install(EXPORT)), CMake adds linking with spdlog_header_only target into the config file too. Like
# [ATargets.cmake]
target_link_libraries(A::A PUBLIC spdlog_header_only)
Linking with a target implies existence of this target, but you do not install spdlog_header_only target. Because of that, created config file for your library won't work. This is what CMake tells you in the error message.
Simple fix
The simplest fix would be using PRIVATE linking with spdlog_header_only target, so that linking won't be part of the config file. Beware, in that case a user of your (installed) library won't get access to the header files for spdlog.
(But a user could obtain these headers by other means.)
Hard fix
But if you want a user of your library to have access to spdlog headers (or, worse, the public headers of your library use #include for headers from spdlog), then you cannot drop PUBLIC linking. In that case you need to install spdlog_header_only target too. E.g. by enabling SPDLOG_INSTALL option
set(SPDLOG_INSTALL ON)
before
add_subdirectory(../external/3rd_party/spdlog ${CMAKE_BINARY_DIR}/spdlog EXCLUDE_FROM_ALL)
(Note, that aside from enabling SPDLOG_INSTALL option, several other adjustments needs to be done for make the config file for your library to work.)

Cannot link local libraries in CMake

I'm developing a c++ program on visual studio that will be deployed on linux, and it is debugged on linux through an ssh. Currently, this is the structure of my folder:
ANT
-xscommon
--xscommon_config.h
-xscontroller
-xstypes
ANT.cpp
ANT.h
CMakeLists.txt
CMakeSettings.json
hashes.h
quaternionic.h
stars.h
Currently, all the .h, .cpp, .o, .cpp.o, .a files that I think I have to link to are kept within the three xs------- directories. I am quite new to cmake, and this linking to these libraries is giving me trouble; I am able to link correctly to the includes, but there are undefined references errors thrown when I don't do linking, and when I attempt linking, it throws errors. This is my current CMakeLists.txt file:
# CMakeList.txt : CMake project for ANT, include source and define
# project specific logic h"ere.
#
cmake_minimum_required (VERSION 3.8)
project ("ANT")
link_directories(${ANT_SOURCE_DIR}/xscommon xscontroller xstypes)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(ANT PUBLIC xscommon_config)
When I run this, the builder says the following:
/usr/bin/ld: cannot find -lxscommon_config
I need to look for these libraries in the directory that ANT.cpp is in, as this is where they are kept, however nothing I do (and I have messed around with configurations for hours now) will tell camke to look for these libraries in the src folder. it always goes to /usr/bin/ld.
I really just need to know what to tell CMake such that it will look in the correct place for each file, that is if I am telling it to look for the correct file (I am fairly sure I am).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Update
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
So I have remade the CMakeLists.txt file to this:
# CMakeList.txt : CMake project for ANT, include source and define
# project specific logic here.
#
cmake_minimum_required (VERSION 3.15)
project ("ANT")
#[STATIC | SHARED | MODULE]
#[STATIC | SHARED | MODULE]
#[STATIC | SHARED | MODULE]
add_library(xscommon SHARED IMPORTED)
add_library(xscontroller SHARED IMPORTED)
add_library(xstypes SHARED IMPORTED)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
)
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
And still get undefined references. I am going to try building the libraries instead. Additionally, I have contacted the manufacturer of the IMUs which use this SDK, as colleagues have not been able to fix this either.
The problem is you are linking to a library that has not been build.
This
# link to this directory
target_link_libraries(ANT PRIVATE xscommon)
tries to link to a library called xscommon to the target ANT but you have not build xscommon anywhere in your project.
If xscommon is a pre-build library and you just want to import it then add the library and set the IMPORTED target property:
add_library(xscommon [STATIC | SHARED | MODULE] IMPORTED)
If you want to build xscommon in your root CMakeLists.txt. Add xscommon as a library and include the location of the headers.
add_library(xscommon [STATIC | SHARED | MODULE]
xxx/xxx.cpp #list all source files that build the library - use relative path
)
target_include_directories(xscommon PRIVATE
xxx/xxx #path to the location of library header files
)
Also you don't need to add the header files when adding the executable. So this
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
can be simplified to
add_executable(
ANT
"ANT.cpp"
)
Suppose your dir is like this:
ANT
-xscommon
--xscommon_config.h
--xscommon_config.cpp
...
First add a CMakeLists.txt file to xscommon/:
ANT
-xscommon
--CMakeLists.txt
--xscommon_config.h
--xscommon_config.cpp
...
Now in xscommon/CMakeLists.txt we will create a library, that will be imported and linked in the main CMakeLists.txt file:
xscommon/CMakeLists.txt:
#define another target, let's name it 'xscommon'
add_library(xscommon
xscommon_config.h
xscommon_config.cpp
#more sources if you want
)
Now in the main CMakeLists.txt file:
cmake_minimum_required (VERSION 3.8)
project ("ANT")
#remove this line
#link_directories(${ANT_SOURCE_DIR}/xscommon xscontroller xstypes)
add_executable(
ANT
"ANT.cpp"
"ANT.h"
"quaternionic.h"
"stars.h"
"hashes.h"
)
# add the xscommon directory, this will make the library target defined there available here
add_subdirectory(xscommon)
# link to this directory
target_link_libraries(ANT PRIVATE xscommon)
# use PUBLIC if the xscommon library will be part of the public interface of your
# library. But since it is an executable, PRIVATE is better here.
target_include_directories(ANT PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
Use the above method, you can create more libraries and link to them.
Note that it is not necessary to create separate cmake files for each subdirectory but it is considered a good practice and modularizes your code. If you want to do this in the main cmake file instead of creating a subdirectory, add this to the main cmake:
add_library(xscommon
xscommon/xscommon_config.h
xscommon/xscommon_config.cpp
#more sources if you want
)
Update according to the changes in question
Your current CMakeLists.txt:
These three lines below are not doing anything, definitely not what you think. add_library() command has the word "add" in it, I know, but it doesn't add any library, just like add_executable doesn't add any executable. It creates a library.
add_library(xscommon SHARED IMPORTED)
add_library(xscontroller SHARED IMPORTED)
add_library(xstypes SHARED IMPORTED)
How to create a library in cmake out of two file a.cpp and a.h:
add_library(myALib "a.cpp")
That's it. If you have more sources, you will include them accordingly of course. In your case you will have to add the sources of xscommon and others accordingly.
Once you have created the libraries, you need to link them to your executable. If you won't you will get undefined reference errors because compiler can locate the declarations in header files but not the definitions of your code which exists in .cpp files.
So, how do you link? Simple:
target_link_libraries(TARGET_NAME PUBLIC | PRIVATE LIBRARY_NAME)
# TARGET_NAME: can be `executable` or `library`
# PUBLIC or PRIVATE (for exe, it is usually private)
# LIBRARY_NAME: Name of library you want to link to TARGET_NAME
# So if you wanted to link "myALib" which I created above to ANT, you would do:
target_link_libraries(ANT PRIVATE myALib)
# Note: You need to add this line **after** add_executable() because target "ANT" will be created after that. You can do the linking after the "target_include_directories" command.

CMake doesn't include header directory of submodule A within submodule B

I have a CMake project that looks like this:
project/
CMakeLists.txt
subprojectA/
CMakeLists.txt
include/
headerA.hpp
src/
libraryA.cpp
subprojectB/
CMakeLists.txt
src/
mainB.cpp
The "library" subproject, A, is compiled as a static library, becoming libsubprojectA.a. The "main" project, B, is compiled as a binary and depends on the library. mainB.cpp includes a reference to headerA.hpp.
Here is subprojectA/CMakeLists.txt:
project(SubProjectA)
include_directories(include)
add_library(subprojectA STATIC src/libraryA.cpp)
set(${PROJECT_NAME}_INCLUDE_DIRS
${PROJECT_SOURCE_DIR}/include
CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE)
And here is subprojectB/CMakeLists.txt:
project(SubProjectB)
include_directories(${SubProjectA_INCLUDE_DIRS})
add_executable(mainBinary src/mainB.cpp)
target_link_libraries(mainBinary subprojectA)
The main Project CMakeLists.txt looks like:
project(Project)
add_subdirectory(subprojectB)
add_subdirectory(subprojectA)
Note that subprojectB, the main project, is listed before subprojectA.
Here's the problem. When I first run "cmake" on this project, ${SubProjectA_INCLUDE_DIRS} is not set within SubProjectB.
What I think is happening is that the CMakeLists for SubProjectB loads first, when ${SubProjectA_INCLUDE_DIRS} has not yet been set. It sets its own include path to an empty string as a result. However, even though libsubprojectA.a gets built successfully before mainBinary, the include path was already set empty beforehand. As a result, I get this error when trying to make mainBinary:
subprojectB/src/mainB.cpp:1:23: fatal error: headerA.hpp: No such file or directory
#include "headerA.hpp"
^
It's a workaround to put subprojectA before subprojectB in the main Project CMakeLists in the declarative world of CMake. What I really want is to know the proper way to indicate to CMake that the include_directories(${SubProjectA_INCLUDE_DIRS}) line depends on the definitions that exist inside SubProjectA's CMakeLists. Is there a better way to do this?
If you want to express that include directory subprojectA/include is an interface of the library subprojectA, attach this property to the target with target_include_directories command:
subprojectA/CMakeLists.txt:
project(SubProjectA)
add_library(subprojectA STATIC src/libraryA.cpp)
# PUBLIC adds both:
# 1) include directories for compile library and
# 2) include directories for library's interface
target_include_directories(subprojectA PUBLIC include)
So any executable(or other library) which linked with subprojectA will have this include directory automatically:
subprojectB/CMakeLists.txt:
project(SubProjectB)
add_executable(mainBinary src/mainB.cpp)
target_link_libraries(mainBinary subprojectA)
Of course, for use last command properly you need to process directory with library before one with executable:
CMakeLists.txt:
project(Project)
add_subdirectory(subprojectA)
add_subdirectory(subprojectB)

CMake: Build Multiple Executables in one Project with Static Library

I'm working on a project that consists of 3 server executables and one library for shared code. I want it to be cross-platform, so I'm using CMake (since Xcode is being a pain anyway) to handle the build process. I'm having trouble with setting up the CMakeLists so that I can include the library from a directory at the same level when I'm building the executable.
Here's the directory structure (and the CMake files):
tethealla2.0/
CMakeLists.txt
libtethealla/
CMakeLists.txt
encryption/
utils/
patch_server/
CMakeLists.txt
login_server/
CMakeLists.txt
ship_server/
CMakeLists.txt
My top-level CMake (tethealla2.0/CMakeLists.txt, only includes the sub-project that should compile):
project(tethealla CXX)
cmake_minimum_required(VERSION 2.6)
add_subdirectory(libtethealla)
add_subdirectory(patch_server)
tethealla2.0/libtethealla/CMakeLists.txt, which generates a static library:
project(Libtethealla C)
cmake_minimum_required(VERSION 2.6)
include_directories(encryption)
set(ENC_DR encryption/)
set(ENCRYPTION_SOURCES
${ENC_DR}/psobb-crypt.c
${ENC_DR}/psogc-crypt.c
${ENC_DR}/psobb-crypt.c
${ENC_DR}/encryption.c
)
add_library(tethealla STATIC ${ENCRYPTION_SOURCES})
tethealla2.0/patch_server/CMakeLists.txt thus far:
project(patch_server CXX)
cmake_minimum_required(VERSION 2.6)
add_executable(server main.cc)
target_link_libraries(server tethealla)
So it makes more sense if I build it from the top level since tethealla2.0/CMakeLists.txt will inherit the targets from each of the subdirectories and the one in patch_server will have access to the tethealla library. However what I want is to be able to build from within these subdirectories to generate Xcode projects so that I can work on/recompile them individually. To do so I need to be able to get to the libtethealla/build directory (where CMake outputs) to access the libtethealla.a library from patch_server. Is this possible?
On kind of another note, even in building from the top-level directory my source in patch_server can't include "encryption.h", the header file for the library in encryption. Which seems to be building fine. Any thoughts on that are also greatly appreciated!
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()