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()
Related
I have a CMake project that produces a static library. It compiles fine in QtCreator and produces the library in build dir. The CMakeLists.txt for the static library looks like this:
cmake_minimum_required(VERSION 3.5)
project(mystaticlib VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
file(GLOB mystaticlib_SRC "src/**.cpp")
add_library( mystaticlib STATIC
${mystaticlib_SRC}
)
target_include_directories( mystaticlib PRIVATE include/mystaticlib INTERFACE include )
Now I have a Qt application project and I'd like to make it so that it depends on the other one - meaning I can do something like in the QtProject:
cmake_minimum_required(VERSION 3.25)
project(MyQtProject VERSION 0.1 LANGUAGES CXX)
# This is how I imagine it working, not an actual code
include_project(mystaticlib, "~/my_projects/mystaticlib/CMakeLists.txt")
set(HEADERS_mystaticlib get_project_headers(mystaticlib))
# end of made up code
add_executable(MyQtProjectEXE ${PROJECT_SOURCES})
target_link_libraries(MyQtProjectEXE mystaticlib)
target_include_directories(MyQtProjectEXE HEADERS_mystaticlib)
How can I get something like that? In practice, I'd like it to work so that the dependent project directory can be set to whatever during cmake configure step.
You are most likely interested in add_subdirectory.
If the containing subdirectory has a CMakeLists.txt then by:
add_subdirectory("./path/to/the/directory")
You will include all targets defined in that CMakeLists.txt file.
All you have to do is add a dependency by using target_link_libraries. CMake will then understand that the subdirectory needs to be build beforehand.
EDIT: To use your project as an example:
cmake_minimum_required(VERSION 3.25)
project(MyQtProject VERSION 0.1 LANGUAGES CXX)
# This is how I imagine it working, not an actual code
#if the path is a subdirectory within the current folder
add_subdirectory("./local_path/to/mystaticlib")
#if not you need to specify a binary dir
add_subdirectory("/absolute/path/to/mystaticlib" "${CMAKE_CURRENT_BINARY_DIR}/mystaticlib")
set(HEADERS_mystaticlib get_project_headers(mystaticlib))
# end of made up code
add_executable(MyQtProjectEXE ${PROJECT_SOURCES})
target_link_libraries(MyQtProjectEXE mystaticlib)
#...
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)
I have a C++ project with the following structure:
CMakeLists.txt
code/
libClient/
CMakeLists.txt
include/
LibClient/
lib_client.h
src/
lib_client.cpp
libServer/
CMakeLists.txt
include/
LibServer/
lib_server.h
src/
lib_server.cpp
libSDK/
include/
CMakeLists.txt
LibSDK/
lib.h
My root CMakeLists.txt has the following content:
cmake_minimum_required(VERSION 3.0)
project(libExample)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
add_subdirectory(code/libClient)
add_subdirectory(code/libServer)
add_subdirectory(code/libSDK)
Now, I would like to build the libs for the server and for the client as .a files. The lib_client.cpp includes the header files with
#include <LibClient/lib_client.h>
Up to here, it is all clear for me, but the header file lib_client.h includes the lib.h which is located at /code/libSDK/include/LibSDK/lib.h with
#ifndef LIBCLIENT_H
#define LIBCLIENT_H
#include <string>
#include <LibSDK/lib.h>
To make this work, I wrote the following content in the CMakelists.txt for the ClientLib:
project(LIBCLIENT)
add_library(
Client_lib STATIC
src/lib_client.cpp
include/LibClient/lib_client.h
)
target_include_directories(egoClient_lib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
target_include_directories(egoClient_lib PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/../libSDK/include") ###this line I think should be different.
My question is now, is it possible to avoid this line with the hard coded path? This lib.h is also used (included) in other header files in this project.
The CMakelists.txt for this lib.h has up to now only the content:
cmake_minimum_required(VERSION 3.0)
project(LIBSDK)
I thought about using the CMake find_package() method and write a Find<package>.cmake file, but I do not see any advantages of this because inside this file I also have to write the paths?
Thank you very much in advance.
If Client_lib uses SDK_lib, you'll want to use the following in Client_lib's CMakeLists.txt:
target_link_libraries(Client_lib SDK_lib)
This will tell CMake that Client_lib uses SDK_lib. It will also automatically make the PUBLIC or INTERFACE include paths configured on SDK_lib (with target_include_directories in SDK_lib's CMakeLists.txt) available for Client_lib.
Firstly, if your libClient and libServer depend on libSDK, you should rearrange your top-level CMake to look like the below. Also, based on your example project structure, your add_subdirectory() for the SDK should be code/libSDK/include:
cmake_minimum_required(VERSION 3.0)
project(libExample)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
add_subdirectory(code/libSDK/include)
add_subdirectory(code/libClient)
add_subdirectory(code/libServer)
You want to be sure to gather information about you SDK first, before traversing to the client and server directories.
Secondly, if you do not intend to build libSDK into a library, but simply use its headers, you can just define a new variable in your libSDK CMake file to specify the location of the headers:
project(LIBSDK)
set(SDK_INCLUDE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} CACHE INTERNAL "My SDK headers directory.")
Now, with a cached variable from the libSDK directory, we can use it in the other CMake files:
project(LIBCLIENT)
add_library(Client_lib STATIC
src/lib_client.cpp
)
target_include_directories(Client_lib PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${SDK_INCLUDE_DIRECTORY}"
)
EDIT: As commented, to avoid the use of cache variables, you can also set the include directories variable with PARENT_SCOPE to make the variable accessible from your client and server CMake files:
project(LIBSDK)
set(SDK_INCLUDE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE)
TL;DR
Using CMake, how can I include subdirectories into a library such that they can be included without referencing the directories they reside?
End TL;DR
In attempt to be brief and speak in higher level ideas of what and how, I have removed everything that I consider to be unnecessary details. I will make edits if need be. As such, this is a brief synopsis of my project structure.
ParentDir
--src
----source.cpp
----source.h
----entities_dir
------entity.cpp
------entity.h
------CMakeLists.txt
----CMakeLists.txt
--CMakeLists.txt
--main.cpp
as it currently stands, I have a library defined by the CMakeLists in the src directory. As such, I can include src files in main by #include as apposed to #include "src/file.h" I would like to be able to do the same for my headers that exist within the subdirectories of src.
CMakeLists.txt
cmake_minimum_required(VERSION 3.6)
project(Project)
add_executable(Project ${SOURCE_FILES} main.cpp)
include_directories(src)
add_subdirectory(src)
target_link_libraries(Project Library) # Engine Libraries
src/CMakeLists.txt
file(GLOB SOURCE_FILES *.cpp)
file(GLOB HEADER_FILES *.h)
add_library(Library STATIC ${SOURCE_FILES} ${HEADER_FILES})
main.cpp
#include <source.h> // this works
#include <entity.h> // this does not work but I want it to
#include <entities/entity.h> // this works but I don't want this
int main() {}
I am not sure how to do this exactly. I have tried to GLOB_RECURSE, add_subdirectory(entities), etc. I have also tried creating a library called Entities inside the src/entities/CMakeLists.txt and linking that with link_libraries. None of these have been successful. What is the proper way to accomplish this, because I think I am probably approaching this completely wrong.
You need that path in your compilers header search path, which is achieved with include_directories() call. You can amend your existing include_directories(src) call to be:
include_directories(src src/entities)
Also, this SO post is related and worth reading: Recursive CMake search for header and source files. There is an excerpt there from the CMake website itself recommending against the usage of file(GLOB ...), which lends to recommending against recursive solutions in general. As a heavy user of CMake, I agree with the arguments made against it.
You just need to add:
include_directories(${CMAKE_CURRENT_LIST_DIR})
In each CMakeLists.txt in your your hierarchy.
CMake in itself doesn't compile your project, it only calls your toolchain and passes parameters to it, and your toolchain doesn't 'know' that it is being called by CMake, or the structure of your project. As such, you need to tell the toolchain where to locate include files.
While it is commonplace to have a CMakeLists.txt in every subdirectory, this is by no means a requirement. The CMakeLists.txt in your src directory could contain all instructions necessary to generate a build. Generally, CMakeLists.txt are put at each level to make the structure easier to manage, eg. each directory only needs to know what it needs to do (presumably, with the files in that directory).
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)