I started playing around with CMake to create a project with Qt and test it with Google Test. At the moment, I succesfully found a way to compile and link all the required libraries. However, I couldn't find a way to link sources to test files with following project structure:
root
|
+-- CMakeLists.txt
+-- src
| |
| +-- CMakeLists.txt
| +-- MyClass.h
| +-- MyClass.cpp
|
+-- test
| |
| +-- CMakeLists.txt
| +-- MyClassTest.cpp
|
+-- lib
|
+-- gtest-1.6.0
|
+-- CMakeLists.txt
Root CMakeLists.txt contains add_subdirectory for gtest, src and test folders. I have succesfully compiled and run "Hello world" app and simple EXPECT_TRUE(true) test in order to check that each part compiles correctly. Unfortunately, I couldn't find a way to include my source file to tests. Is it possible with the following project structure?
PS I know that it is possible to compile my sources as a library and link it to tests, but I dislike that approach, since it is more appropriate for integration testing, rather then unit testing...
EDIT: Added class names to the tree
You can add a global variable at the level of your root CMakeLists.txt:
set(ALL_SRCS CACHE INTERNAL "mydescription" FORCE)
In the first add_subdirectory(src), you can do:
set(ALL_SRCS ${ALL_SRCS} blabla.cpp CACHE INTERNAL "description")
And in the add_subdirectory(test), you continue with:
set(ALL_SRCS ${ALL_SRCS} bla_test.cpp CACHE INTERNAL "description")
You can then do, add_executable, or library or whatever, with all your sources files.
EDIT: add trick for global variables in CMake.
In the root CMakeLists.txt you can add a include_directories(src) This will then also be used by the tests. Another thing you can do is in the test CMakeLists.txt add a include_directories(${<projectName>_SOURCE_DIR}) where projectName is the name specified using project(myproj) in the src/ CMakeLists.txt (if you specified a project in there of course. Also check the docs about project)
Related
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?
I am trying to create a sample project for myself using Plog library. according to its documentation, I must add its include folder to my include path. SO, I add these Lines to C/C++ configuration in my vs-code:
${default}
${workspaceFolder}/**
and this is my main function:
#include <iostream>
#include "plog/Log.h"
int main() {
plog::init(plog::debug, "log.txt");
std::string name;
std::cin >> name;
LOGD << "user entered name :" << name;
std::cout << name << std::endl;
return 0;
}
but when I run this code I am getting this error:
fatal error: plog/Logger.h: No such file or directory
which plog/Logger.h is referenced by log.h in the main function.
plog folder which contains All headers of plog library is located in my project root folder.
this is My folder structure:
root
|_plog
| |
| |_ all my header files *.h
|_main.cpp
Is there any more configuration which I missed? or did I make a mistake in any step?
Try
#include <plog/Log.h>
Why do you have ** after ${workspaceFolder}?
First we need to know what the directory structure is. For instance
+-- C:\
+-- ProgramData
+-- plog-dist
+-- plog
+-- Log.h
+-- lib
+-- plog.lib
+-- bin
+-- plog.dll
+-- Users
+-- Me
+-- Documents
+-- myproject
Let us assume that you already have an environment variable setup
setx PLOG=C:\ProgramData\plog-dist
You can either manage this under property pages or you can set it on individual projects. Let us just do the second method because it is easier and has less explaining. Under C++ General Properties, the first line says Additional Include Directories. Add in
$(PLOG)
This will pick up your environment variable PLOG. In your code
#include "plog/Log.h"
VS will look in $(PLOG) for plog/Log.h. If the distribution is
+-- ProgramData
+-- plog
+-- Log.h
And the environment variable is
setx PLOG=C:\ProgramData\plog
Then you should just
#include "Log.h"
VS will look in $(PLOG) for Log.h
Next go to the linker section of properties. Here, you will see Additional Library Directories. Add $(PLOG)/lib. This is where it can find plog.lib. If plog.lib lives in $(PLOG) then add $(PLOG). It really depends on what the directory structure is.
Next go to the Input property page. Under Additional Dependencies add plog.lib.
If you set up the PLOG variable and type tree /f %PLOG% from the cmd line, it will tell you where everything is relative to $(PLOG).
Assuming that below is your folder hierarchy:
root
|_plog
| |
| |_ Init.h
| |_ Log.h
| |_ Init.h
| |_ Other plog's *.h files
|_main.cpp
In that case, adding ${workspaceFolder} to your include paths must suffice.
When you do #include "plog/Log.h", it expects the plog folder to be a direct children of the include paths. As plog folder is a direct child of ${workspaceFolder} directory, this should work.
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!
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.
I am working on a c++ project using cmake that uses hiredis. The CMake and compilation process do not give any errors. However, when I try to execute my project (from the terminal or from the IDE I'm using [CLion], I get the following error:
dyld: Library not loaded: libhiredis.0.13.dylib
Referenced from: /Users/connorriley/CLionProjects/DispatchingOptimization/bin/dispatch
Reason: image not found
I'm not sure why my project is looking for libhiredis.0.13.dylib because the only hiredis library file I have is libhiredis.dylib.
My project file structure is the following:
.
+-- bin
| +-- dispatch (my executable)
+-- lib
| +-- hiredis
| | +-- libhiredis.dylib
| +-- otherlibs
+-- src
| +-- source code/subfolders with source code
additional info:
compiler: clang
os: macOS 10.12.3
cmake version 3.7.2
Looks like your DYLD_LIBRARY_PATH is not set correctly. You can get more information by setting DYLD_PRINT_LIBRARIES and/or some other environment variables mentioned here
But probably you just need to add your hiredis directory to CMAKE_LIBRARY_PATH like this:
set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${PROJECT_SOURCE_DIR}/lib/hiredis)
I fixed my issue, it was that I went into my hiredis directory and typed:
make
but didn't follow that with
make install
Therefore, the file that my code was looking for was not in my /usr/local/lib