S
S
syxme2021-04-30 16:39:43
C++ / C#
syxme, 2021-04-30 16:39:43

How to properly build a project structure using cmake?

Good afternoon, there is a project on c ++ CMake.

It consists of 2 parts:
1. The library of my engine where the entire computational part is located.
2. A software product that uses and inherits the engine library classes.

Purpose:
To make product development convenient by developing the product and the engine in parallel (2 repositories)
For example, I want to debug the engine in the product using the static library of the engine.
So that the engine is assembled during the assembly of the product.

The first idea was to use add_subdirectory but I don't know if this is correct.

Answer the question

In order to leave comments, you need to log in

2 answer(s)
K
klirichek, 2021-05-04
@syxme

a lib is usually a file (.a, .lib, .so, .dll, .dylib - depends on statics/dynamics) + headers that the application using the lib should include.
When developing a lib and an application at the same time, add_subdirectory is a fairly obvious option, as long as it combines both of them into one project. It is advisable to take care of modularity (since you have divided the project into two parts, it means that this was necessary for some reason!).
The folder with either should have its own CMakeLists.txt that builds the lib; it must be self-contained for assembly. Those. if it needs some dependencies - so that they are searched specifically for the project, and not "somewhere above". That is, some sources, some find (), then add_library (name). And you need to determine what the application using it should see from the lib. This is done by setting the target_XXX() properties. For example, to show the consumer the path to the necessary headers - target_include_directories, to add compilation keys - target_compile_definitions(). In modern cmake, this can be configured as in real OOP - the properties needed to build the lib itself (for example, the dependencies with which it is statically linked. She needs it, but as it was linked, they are already invisible and unnecessary to the consumer); properties needed by the consumer, but not needed to build libs (for example, headers specific to including the lib, but not needed to build it), and general properties needed by both the lib and its users. The point is that all these "settings for external users" are not put into some global cmake variables (this has been done for many years, but these are far from best practices), but are defined as properties of the library itself. And the icing on the cake, at the end of the project, either you can add an export and an alias for it (why you need it - just below). but are defined as properties of the library itself. And the icing on the cake, at the end of the project, either you can add an export and an alias for it (why you need it - just below). but are defined as properties of the library itself. And the icing on the cake, at the end of the project, either you can add an export and an alias for it (why you need it - just below).
export(TARGETS mylib FILE "mylib-targets.cmake" NAMESPACE mylib::)
add_library(mylib::mylib ALIAS mylib)
An application that uses a lib must first find it. And secondly, link. About add_subdirectory - it's clear. Add a folder and either is available. Another option is if the lib is already established and you don't want to rebuild it every time. Then instead of add_subdirectory you include (via include) the 'mylib-targets.cmake' file, which will be in the lib build folder. On the developer's machine, this is a completely working option! Well, the last option is to do a full-fledged export, then users will be able to find a lib through the regular find_package().
After finding the lib, it is added with a single line:
target_link_libraries(myprog PUBLIC mylib::mylib)
Thanks to the properties of the lib, this line automatically adds everything you need - paths to headers, a lib for linking, additional compilation options, etc. (previously, during global variables, it was assumed that after finding a lib using find_package, you had several variables, like mylib_FOUND - that it was found, mylib_INCLUDE_DIRECTORIES - the path to the headers, mylib_LIBRARIES - what to link. And if the lib appeared through add_subdirectory, there is generally fantasy on the topic "how to report yourself" was not limited to anything, which, in turn, led to noodle-like code that used the library. With the properties of targets, this is all no longer necessary!). Well, the name is mylib::mylib - it works both for an imported lib (through the inclusion of mylib-targets.cmake), and for add_subdirectory(),
So in the main project you will have something like
add_subdirectory(mylib)
target_link_libraries(myprog PUBLIC mylib::mylib)
or
include (mylib/build/mylib-targets.cmake)
target_link_libraries(myprog PUBLIC mylib::mylib)
or
find_package( mylib REQUIRED)
target_link_libraries(myprog PUBLIC mylib::mylib)
The first option is for concurrent development. The IDE will have the source code for both the main project and either at once. The dark side - when you do a clean all, either will be erased too :) (which is obvious)
The second is for the application. Liba will always be at hand, but already as a binary. You can link as much as you like, nothing will be erased during cleaning, the path is tied to a folder (you can’t move to another folder, because absolute paths are written in the file).
The third is for broad development. When the lib is already separate, and is installed separately, and someone is developing something with it. This is the most civilized option, the folder can be moved anywhere, the main thing is to tell cmake where to look for it. But setting it up is a little more difficult and not always justified.
And yes - if you still study cmake, then either is not necessarily a linking library "in the generally accepted sense". But it's just a collection of properties (include folders, compiler options, link options, etc.) encapsulated into something called a "library". For example, you can define a lib to set the -wall compiler warnings, where there will be neither sources nor libraries, but only one compiler option. And this is a completely normal and working way! In addition to static libs, to slightly speed up the build, you can use libs of type object. It's just a set of compiled objects that are not packaged into a separate .a or .lib file. and directly so are transferred to dependent projects. One less packing/unpacking operation).

R
res2001, 2021-04-30
@res2001

It is more logical to debug the library with tests specially written for this. In this case, the library will represent an independent product. It can be installed separately for clients. And in the main product, use find_package to find the library.
If you don't need this, it's fine to include the library source directory as a product subdirectory and use add_subdirectory.

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question