CMake - Module + Library confusion - c++

I've started a new c++ project , and I am confused with all the CMake capabilities. I have tried to understand better by looking at examples and CMake tutorials
I should create a new project composed of:
Library: It contains some common classes that will be used by the following module(s) (e.g., vector, matrix, image, etc..)
Module (possibly more than 1 in the future): It contains some module-specific classes (e.g., classifier, estimator, etc.) and a main.
My proposed folder structure is as below:
|-- Root Project
|-- CMakeLists.txt
|
|-- Library
| |-- CMakeLists.txt
| |-- include
| | |-- CMakeLists.txt (?)
| | `-- Lib_Class.h
| `-- src
| |-- CMakeLists.txt (?)
| `-- Lib_Class.h
|
|-- Application 1
| |-- CMakeLists.txt
| |-- include
| | |-- CMakeLists.txt (?)
| | `-- Method.h
| `-- src
| |-- CMakeLists.txt (?)
| |-- Method.cpp
| `-- main.cpp
|
|-- Application 2
| |-- CMakeLists.txt
| |
`
The problem arises when I have to actually add the code to the different CMakeLists.txt files. According to my reasoning, I would have:
Root/CMakeLists.txt: For creating the project and adding the subdirectories of the Library and the Module(s).
Library/CMakeLists.txt: This creates the library with the header (from include folder) and source (from src folder) files.
Module/CMakeLists.txt: This creates an executable from the src/main.cpp file using the Library and the module-specific classes with header files in include folder and source files in src folder.
I have 2 questions:
First, I also found solutions in other replies with CMakeLists.txt files in the Library/src and Module/src folders. But I really don't understand how to use them and what to write inside them, because I would have used only the CMakeLists.txt file in the parent folder.
Second, in case I want to link an external library (e.g., OpenCV or dlib) should I link it in the modules and library, individually, or should I link it in the root CMakeLists.txt file (provided that the library is used everywhere)?
I really need some assistance to try to understand CMAKE. Can someone explain or please direct me to a suitable tutorial on this subject.
Matthieu, thank you very much for your help. According to the explanation you provided me, I came out with the following CMakeLists.txt files:
Root/CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
project(Project_Name)
add_subdirectory(Library)
add_subdirectory(Application)
Library/CMakeLists.txt
project(Library)
set(LIB_HEADERS
include/Lib_Class.h
)
set(LIB_SOURCES
src/Lib_Class.cpp
)
add_library(Library_Name SHARED ${LIB_SOURCES} ${LIB_HEADERS})
Application/CMakeLists.txt
project(Application)
set(APP_HEADERS
include/Method.h
)
set(APP_SOURCES
src/Method.cpp
src/main.cpp
)
add_executable(Application_Name ${APP_SOURCES} ${APP_HEADERS})
target_link_libraries(Application_Name Library_Name)
Now everything seems to work grat! Thank you again and sorry again for being confusing somethimes!

The root cmakelists should set up all the variables, checking compiler support and library presence.
Then you go to each subfolder and create the libraries and executables based on the source code and the detected libraries. You should also set up all the linked libraries there.
Then cmake will figure out what depends on what.

Related

CMake Error "include could not find requested file" when nest a third-party cmake project into my own

Following is my CMakeLists.txt structure
|-- MyProject
|-- CMakeLists.txt
|-- README.md
|-- bin
|-- include
|-- lib
|-- src
| |-- CMakeLists.txt
| |-- main.cc
| |-- parser
| |-- CMakeLists.txt
| |-- parser.cc
| |-- parser.h
|-- third-party
|-- CMakeLists.txt
|-- cassandra-cpp-driver
Now I want to introduce a yugabyteDB-cpp-driver https://github.com/yugabyte/cassandra-cpp-driver/tree/2.9.0-yb to use its API, so I add those commands into my root CMakeLists.txt
set(THIRD_PARTY_DIR ${PROJECT_SOURCE_DIR}/third-party)
add_subdirectory(${THIRD_PARTY_DIR})
Then I add these to third-party/CMakeLists.txt
set(CQL_DRIVER_LIB_NAME "cassandra-cpp-driver")
set(CQL_DRIVER_INC_PATH ${CQL_DRIVER_LIB_NAME}/include)
add_subdirectory(${CQL_DRIVER_LIB_NAME})
After these, when I trying to do build the project, I got
CMake Error at third-party/cassandra-cpp-driver/CMakeLists.txt:10 (include):
include could not find requested file:
CppDriver
CMake Error at third-party/cassandra-cpp-driver/CMakeLists.txt:12 (CassInitProject):
Unknown CMake command "CassInitProject".
Seems the cpp-driver it self has a include() command which calls the .cmake files the git contains, but I can't trigger it in my project.
Note: Below I describe a solution to the specific problem occuring, but this is in no way a complete solution to allow you to include the project the way you intend to. This is unlikely to be possible without modifying the third party cmake logic. The cmake logic just doesn't seem to be designed for your intended use case.
CMake Error at third-party/cassandra-cpp-driver/CMakeLists.txt:10 (include):
include could not find requested file:
CppDriver
The relevant lines in the CMakeLists.txt file in the repository linked are
set(CASS_ROOT_DIR ${CMAKE_SOURCE_DIR})
...
list(APPEND CMAKE_MODULE_PATH ${CASS_ROOT_DIR}/cmake/modules)
include(CppDriver)
The first line is supposed to make cmake able to the CppDriver.cmake module when using the include() command.
CMAKE_SOURCE_DIR refers to the directory containg the toplevel CMakeLists.txt though, which is your own directory. To be able to include the project via add_subdirectory, the relevant line should read
set(CASS_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR})
(Note: In no way this is a complete analysis of the cmake logic. There may be different parts that would require similar adjustments.)
You could fix this specific error by simply adding the correct path before using add_subdirectory():
set(CQL_DRIVER_LIB_NAME "cassandra-cpp-driver")
set(CQL_DRIVER_INC_PATH ${CQL_DRIVER_LIB_NAME}/include)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/${CQL_DRIVER_LIB_NAME}/cmake/modules)
add_subdirectory(${CQL_DRIVER_LIB_NAME})
But you'd just run into a similar issue in a different place in the third party cmake logic.

Eclipse CMake support both global project and subprojects

I have a project that I'm trying to configure to make use of CMake, and to make my life easier I'm organizing it as a series of libraries that I'm treating as subprojects.
MyProject
|-- common-cmake
| |-- common-functions1.cmake
| |-- common-functions2.cmake
| `-- CMakeLists.txt
|-- lib1
| |-- include
| | `-- someClass.h
| |-- src
| | `-- someClass.cpp
| |-- test
| | |-- include
| | | `-- someClassTest.h
| | |-- src
| | | `-- someClassTest.cpp
| | `-- CMakeLists.txt
| `-- CMakeLists.txt
|-- lib2
| `-- <snip>
|-- lib3
| `-- <snip>
`-- CMakeLists.txt
There's a global CMakeLists.txt in MyProject that handles the cross-library specifics (such as version information and installation directories) and ensures that everything is included via
add_subdirectory(cmake-common)
add_subdirectory(lib1)
add_subdirectory(lib2)
add_subdirectory(lib3)
common-cmake contains common logic to avoid repetition in each library's or test's CMakeLists.txt (such as common shared library definition since all of the libraries have a common structure). Each library contains a CMakeLists.txt with the necessary information so that it itself can be compiled, and includes its test directory. Each test in turn has its own CMakeLists.txt to provide the necessary details for the compilation of the tests for the library it is contained within. Thus, both the library and its test CMakeLists.txt is dependent on details the global CMakeLists.txt provides and functions that are contained within the common-cmake.
After fighting with the specifics of this, I've got it working. I can kick-off a build from the global level and build/test/install all libraries as well as for an individual library via the appropriate make target.
Now that I've got this working at the global level, I'm trying to get this working in Eclipse. I've managed to get the global project imported and working by calling
cmake -G "Eclipse CDT4 - Unix Makefiles"
at the global level, however this means that everything is contained within a single Eclipse project and I find it extremely unwieldy to work with. Ideally I'd like to have each of the libraries as its own project within Eclipse. I can import the library as a project from within Eclipse directly, but when I do that Eclipse in turn starts throwing errors relating to failure to CMakeCache.txt not existing, or the common functions from common-cmake not being available (since it's not longer starting from the global CMakeLists.txt, those common files are never added).
The only way around this that I can think of would be to allow each library to be both part of the global project (as it is right now) as well as to be usable independently. I've been trying to figure out how this could be done, but unfortunately I haven't had any success.
add_subdirectory("..")
add_subdirectory("../common-cmake")
add_subdirectory doesn't allow me to add anything that's outside of the current directory's subdirectories (which makes a lot of sense), and the only way I've found is to include each file individually and have a rather ugly flag in place to prevent duplication, where the global and the project check each other's flag to prevent stepping on each other's toes.
# Global CMakeLists.txt
add_subdirectory(cmake-common)
if(NOT DEFINED ${PROJECT_BUILD})
set(FULL_BUILD true)
add_subdirectory(lib1)
add_subdirectory(lib2)
add_subdirectory(lib3)
endif()
# lib CMakeLists.txt
if(NOT DEFINED ${FULL_BUILD})
set(PROJECT_BUILD true)
include("../common-cmake/common-functions1.cmake")
include("../common-cmake/common-functions2.cmake")
endif()
Is there a way to accomplish this? Is what I'm trying to do antithetical to CMake and thus not possible? Is there a better way to achieve my end goal?

CMake: best structure when migrating microrepositories to a monorepo?

At the early stage of our development, we have created micro-repositories, to be able to deeply modify repositories while minimizing interference with each other's work.
Now that we have a fairly stable version of our codebase, we want to merge some of the micro-repositories into a monorepo. That will ease the users' experience while building from source, as well as the deployment phase.
All our micro-repositories are built with CMake, and each of them obviously defines a project(). They also have 'internal' dependencies: some of the micro-repositories is built first and then used by the others with find_package().
RepoA
|-- CMakeLists.txt
| project(RepoA)
RepoB
|-- CMakeLists.txt
| project(RepoB)
| find_package(RepoA)
RepoC
|-- CMakeLists.txt
| project(RepoC)
| find_package(RepoA)
| find_package(RepoB)
While migrating them into a monorepo, the first option that came to my mind is moving each of them into a subdirectory, then adding add_subdirectory() to the root CMakeLists.txt file to build them. In the first instance, I would define a global project by using project() in the root CMakeLists.txt file, and I would also keep the project() definition in subdirectories.
Monorepo/
|-- CMakeLists.txt
| project(Monorepo)
| add_subdirectory(RepoA)
| add_subdirectory(RepoB)
| add_subdirectory(RepoC)
|-- RepoA/
|-- CMakeLists.txt
| project(RepoA)
|-- RepoB/
|-- CMakeLists.txt
| project(RepoB)
| find_package(RepoA)
|-- RepoC/
|-- CMakeLists.txt
| project(RepoC)
| find_package(RepoA)
| find_package(RepoB)
Now the question is: is that a good choice? Would you advise having a global project and multiple sub-projects, all of them defined through the use of the CMake project() command? Are there better strategies?
And also, would this strategy have drawbacks?
Many thanks for your kind help!

How do I share headers and libraries in subdirectory by cmake?

I want to use my headers and libs as common libraries for app1 and app2. My project tree are below. image/ and math/ are library directories used by app1 and app2. In this case, should I set same settings to both CmakeLists.txt under app1 and app2? Of course I know it works, but are there any smarter ways to set common libraries?
|-- CMakeLists.txt
|-- app1
| |-- CMakeLists.txt
| `-- main.cc
|-- app2
| |-- CMakeLists.txt
| `-- main.cc
|-- image
| |-- CMakeLists.txt
| |-- include
| | `-- image_func.h
| `-- src
| `-- image_func.cc
`-- math
|-- CMakeLists.txt
|-- include
| `-- math_util.h
`-- src
`-- math_util.cc
Roots CMakelists.txt is below. Is it possible to set math and image parameters for app1 and app2? My actual project has many applications which uses multiple libraries.
cmake_minimum_required(VERSION 2.8)
add_subdirectory("./image")
add_subdirectory("./math")
add_subdirectory("./app1")
add_subdirectory("./app2")
With newer versions oft CMake (since 2.8.12) you can use target_link_libraries and related functions to manage dependencies. By specifying PUBLIC the includes and libraries are also applied to all targets using the library.
This will reduce duplication to a minimum.
For math and image you need to specify that the respective include directory is used and any libraries you might require.
math/CMakeLists.txt
add_library(math ...)
target_include_directories(math PUBLIC include ...)
target_link_libraries(math PUBLIC ...)
image/CMakeLists.txt
add_library(image ...)
target_include_directories(image PUBLIC include ...)
target_link_libraries(image PUBLIC ...)
app1/CMakeLists.txt
add_executabke(app1 ...)
target_link_libraries(app1 PUBLIC image math)
app2/CMakeLists.txt
add_executabke(app2 ...)
target_link_libraries(app2 PUBLIC image math)

Better way to structure custom compiled libs

I am custom compiling libcurl , libssl and some other library. I don't want to replace system library, because if I am changing it system wise, it is going to create lib conflict and I need to compile all other component depending on these libs.
So I started using RPATH and started structuring like this:
|-- bin
| |-- app.out
|-- lib
| |-- libboost_program_options.so -> libboost_program_options.so.1.49.0
| |-- libboost_program_options.so.1.49.0
| |-- libboost_system.so -> libboost_system.so.1.49.0
| |-- libboost_system.so.1.49.0
| |-- libboost_thread.so -> libboost_thread.so.1.49.0
| |-- libboost_thread.so.1.49.0
| |-- libcares.so -> libcares.so.2.0.0
| |-- libcares.so.2 -> libcares.so.2.0.0
| `-- pkgconfig
`-- sbin
`-- nginx
This approach worked. Now problem is that , We started using PHP and node which require same application version.
|-- bin
| |-- a.out
|-- lib
| |-- libboost_program_options.so -> libboost_program_options.so.1.49.0
| |-- libboost_program_options.so.1.49.0
| |-- libboost_system.so -> libboost_system.so.1.49.0
| |-- libboost_system.so.1.49.0
| |-- libboost_thread.so -> libboost_thread.so.1.49.0
| |-- libboost_thread.so.1.49.0
| |-- libcares.so -> libcares.so.2.0.0
| |-- libcares.so.2 -> libcares.so.2.0.0
| `-- pkgconfig
|-- php_ext
| `-- sqlite3.so
|-- node
| `-- node_modules
| |-- bin
| | |-- node
`-- sbin
`-- nginx
Now , this svn repo is becoming bigger and bigger after every release. Is there a better way to structure this ? without duplicating lib folder in each app ?
As someone who has used both git and svn extensively for years, I'd seriously consider moving to git and using git submodules. Git is hugely more space-efficient (among many, many other benefits). There are also git-svn bridges that you can make if you're stuck using svn at your company.
Failing that, I'd make an svn externals for each group of shared libraries. If you have something that changes often or is logically grouped together, it can go in one svn repo while other libraries that may not change very often.
One of the advantages of git over svn is that git protects you from file corruption. I can painfully remember several occurrences of svn corrupting files (something that wasn't noticed until the client submitted a bug report).
Seriously, save yourself a world of headaches, ditch svn in favour of git.
I was taking a totally different approach when I was working with C++.
I don't treat libs as part of source code. I only want the "dependency" to exist with my source. (It is not appropriate to have "version control" for the lib binary anyway)
I have a separate directory to store all libs in a organized manner, something like libname/version/arch.
In the build script, I am referring to something like $LIB_DIR/libname/version/arch/lib-ver.so.
You can have different way to store/distribute the Lib directory, either put it in a network volume, put that in SVN etc.