How cmake add_subdirectory works for projects dependency? - c++

I had some problems about cmake build system:
There are may projects in my code like this:
├── CMakeLists.txt
├── project1
│   └── CMakeLists.txt
└── project2
└── CMakeLists.txt
...
I want to use add_subdirectory to build all projects,so I write them in top level CMakeLists.txt
add_subdirectory(project1)
add_subdirectory(project2)
And there are dependencies between projects ,for example project1 need find project2:
# project1/CMakeLists.txt
find_package(project2 REQUIRED)
And I will get an error message,because project1 can't find project2.
By not providing "Findproject2.cmake" in CMAKE_MODULE_PATH this
project has asked CMake to find a package configuration file provided by
"project2", but CMake did not find one.
All I know is that order won't solve the problem.
So I want to konw how deal with this problem?Any suggestions?

Related

How to link an external library with CMake project

I made a Non-Qt project, Plain C++ Application in QT creator. My current folder structure is like this:
.
├── client
│   ├── client.cpp
│   └── client.h
├── CMakeLists.txt
├── CMakeLists.txt.user
├── main.cpp
└── server
├── server.cpp
└── server.h
My CMakeLists.txt file looks like this:
cmake_minimum_required(VERSION 3.5)
project(eshraagh-project LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(eshraagh-project main.cpp server/server.h server/server.cpp client/client.h client/client.cpp)
In my main file, I included server.h and client.h and made instances of both of them, and it didn't give any errors.
The problem arises when I try to use an external library (this library). I have no idea how to use it.
I tried to copy contents of its src folder (cpnet-export.h, cpnet-network.h and cpnet-network.c) into my project and add them to CMakeLists, just like I did with server and client files, but it doesn't work. I copied the server part of the repository and pasted it in my main, but it gives me errors such as undefined reference to 'cpnet_bind' and more (A little note: The repository uses the functions with net prefix, but QT recommender told me to write cpnet prefix instead. I don't know why this is happening).
Linking a library of an executable in CMake is achieved by using the target_link_libraries function. In your case,
target_link_libraries(eshraagh-project PRIVATE cpnet)
Here, cpnet could be the name of a target that you CMake project knows (otherwise, it is turned into some kind of platform specific -lcpnet linker flag). In order to make CMake know about the cpnet library, you can
add the external library sources to your project (as a git submodule, or by tracking them yourself), for this example let's say they the sources are located at lib/cpnet
tell CMake to include the configuration instructions from that folder. This works if the external project uses CMake, too, and doesn't mess with global variables and settings (this seems to be the case here).
So in your toplevel CMakeLists.txt:
add_subdirectory(lib/cpnet)
Note that this must be done before the target_link_libraries call.

How to write a CMakeLists.txt to use FreeImage precompiled library and header file

The folder structure:
Project/
├── main.cpp
└── deps/
└── FreeImage/
├── FreeImage.lib
├── FreeImage.dll
├── FreeImage.h
├── FreeImagePlus.lib
├── FreeImagePlus.dll
└── FreeImagePlus.h
The code:
#include <FreeImagePlus.h>
int main(void)
{
fipImage i;
return 0;
}
And now the question:
How to write a CMakeLists.txt file to be able to compile the above in windows?
My attempt as an answer below
You should use different steps here. A proper goal is to have everything you need to use FreeImage in a FindFreeImage.cmake file. Then you can set FreeImage.cmake like this:
FIND_PATH(FreeImage_INCLUDE_DIR FreeImage.h HINTS ${FreeImage_ROOT})
FIND_LIBRARY(FreeImage_LIBRARY NAMES FreeImage HINTS ${FreeImage_ROOT})
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(FreeImage DEFAULT_MSG FreeImage_INCLUDE_DIR FreeImage_LIBRARY)
Of course, you should add more so that when linking against FreeImage, you get the include path set, installation procedure... But that would be for another question.
One solution of a CMakeLists.txt with extra tips
cmake_minimum_required(VERSION 3.8)
set(NAME cmakecopytest)
project(${NAME})
# The place to put built libraries
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/windows-64/debug")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/windows-64/debug")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/windows-64/debug")
# Copying pre-compiled dll libraries next to the built libraries for them to be found in runtime without extra effort
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/deps/FreeImage/FreeImagePlus.dll" "${CMAKE_BINARY_DIR}/bin/windows-64/debug/FreeImagePlus.dll" COPYONLY)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/deps/FreeImage/FreeImage.dll" "${CMAKE_BINARY_DIR}/bin/windows-64/debug/FreeImage.dll" COPYONLY)
add_executable(${NAME}_exe main.cpp)
target_include_directories(${NAME}_exe PUBLIC ./ include/ deps/FreeImage)
target_link_libraries(${NAME}_exe PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/deps/FreeImage/FreeImage.lib" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/deps/FreeImage/FreeImagePlus.lib")
# target_link_libraries(${NAME}_exe PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/deps/FreeImage/FreeImage" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/deps/FreeImage/FreeImagePlus") # ERROR LNK1104: cannot open file '..\deps\FreeImage\FreeImage.obj'
Extra tips:
CMAKE_BINARY_DIR contains the place where you built the solution (i.e., D:/Work/Project/build)
Setting the output directories and then copying the precompiled libraries to that same location is a easy way to have your code run without any added extra configurations in MSVS
math(EXPR platform_bits "${CMAKE_SIZEOF_VOID_P} * 8")
set(platform_dir bin/${CMAKE_SYSTEM_NAME}-${platform_bits})
foreach(config DEBUG RELEASE RELWITHDEBINFO MINSIZEREL)
foreach(var
CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${config}
CMAKE_LIBRARY_OUTPUT_DIRECTORY_${config}
CMAKE_RUNTIME_OUTPUT_DIRECTORY_${config}
)
set(${var} "${CMAKE_BINARY_DIR}/${platform_dir}/${config}")
string(TOLOWER "${${var}}" ${var})
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/deps/FreeImage/FreeImagePlus.dll ${var}/FreeImagePlus.dll COPYONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/deps/FreeImage/FreeImage.dll ${var}/FreeImage.dll COPYONLY)
endforeach()
endforeach()
And this for cycle does that for you for all the desired configurations
However, I do not like having to specify the '.lib' in target_link_libraries(...FreeImage.lib...)
Anyone else has a solution to be able to say target_link_libraries(${NAME}_exe PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/deps/FreeImage/FreeImage" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/deps/FreeImage/FreeImagePlus") while avoiding the linking error LNK1104: cannot open file '..\deps\FreeImage\FreeImage.obj' ?

Problems when linking my main output library with my test executable

I'm building a "framework" library which one I'm trying to integrate Google Tests. It's a pretty small library, that at the end gives me a .so or .dll file.
When I was starting to tests my library, I found a configuration (details below) that works fine on my CMakeFile at my linux environment. But when I try to run the same project using a MSBuild for MSVC14, it gives me a link error
LINK : fatal error LNK1104: cannot open file '..\src\Debug\foobar.lib'
I'm thinking that my cmake is guessing the lib name wrong (foobar.lib instead of foobar.dll), but I couldn't find why nor how to fix it.
Also, I don't know if this is really the best way for me testing this. What I want is a way to test the whole framework (initializing, creating stuffs, checking returns etc..) without a main.cpp file, and meanwhile start to create the unit tests.
So, my question is.. What am I doing wrong that at the Windows environment the linker does not find the foobar lib built by src/CMakeLists.txt? (I checked, and the lib is created in "src/Debug/foobar.dll", same dir that appears at the error, and works fine)
Also, is my method so wrong that windows just don't wanna deal with? lol I mean, it's wrong to do something like I'm trying to do? It's not that I do not want to make unit tests, which I'll be doing soon, but I really like to build and try my lib without using any external exec binary before I starting to do that.
Thanks!
OBS:
My google tests is working fine both in linux and windows;
I can run the test FooA if I remove the FooBar test, which is linking to foobar lib.
Using my linux environment this configuration works perfectly.
Update:
As #vre suggested, I found the macro __declspec(dllexport) and put it before my FooBar class name and the compilation passed, but it crashes when I run and throws this warning when compiling:
warning C4251: 'FooBar::_impl': class 'std::unique_ptr<FooBar::FooBarImpl,
std::default_delete<_Ty>>' needs to have dll-interface to be used by clients of class 'FooBar'
That's because I have a PImp implementation of the FooBar class. So, I have this:
class __declspec(dllexport) FooBar
{
...
private:
class FooBarImpl;
std::unique_ptr<FooBarImpl> _impl;
}
I don't really know what it means yet. Trying to find out why it crashes.
My project has this file tree:
├── CMakeLists.txt
├── include
| ├── FooBar.hpp
├── src
│   ├── CMakeLists.txt
│   ├── FooBar.cpp
│   ├── FooA
│   │   └── CMakeLists.txt
│   ├── FooB
│   │   └── CMakeLists.txt
│   └── FooC
│   └── CMakeLists.txt
└── test
├── CMakeLists.txt
├── FooBar
│   └── FooBarTest.hpp
├── FooA
│   ├── FakeBar.hpp
│   ├── FooATest.hpp
│   └── mockObj.hpp
My main CMakeLists.txt is like this:
...
set(FOOBAR_INCLUDE "${PROJECT_SOURCE_DIR}/include/FooBar.hpp")
# Include src main CMakeLists
add_subdirectory("${PROJECT_SOURCE_DIR}/src")
# Include tests if enabled
if (test)
add_subdirectory("${PROJECT_SOURCE_DIR}/test")
endif ()
My src/CMakeLists.txt:
...
set(FOOBAR_SOURCES "FooBar.cpp")
# Build
add_library(foobar SHARED ${FOOBAR_SOURCES} ${FOOBAR_INCLUDE})
# Links the library with components.
target_link_libraries(foobar FooA FooB FooC)
And my test/CMakeLists.txt is something like this:
enable_testing()
# Include directories used for testing.
include_directories("${CMAKE_SOURCE_DIR}/src"
"${CMAKE_SOURCE_DIR}/include"
"FooA/"
"FooBar/")
# Include the files for testing.
set(INCLUDE_TESTS "${CMAKE_SOURCE_DIR}/src/FooA/FooA.cpp"
"${CMAKE_SOURCE_DIR}/src/FooA/Bar.cpp")
# Include the test source files.
set(TEST_SOURCES "main.cpp"
"FooBar/JepluTest.hpp"
"FooA/FakeBar.hpp"
"FooA/FooATest.hpp")
# Build
add_executable(foobar-test ${TEST_SOURCES} ${INCLUDE_TESTS})
# Links the library with components. (HERE IS WHERE OCCURS THE PROBLEM)
target_link_libraries(foobar-test gtest foobar)
# Not really important right now
add_test(NAME foobar-test COMMAND foobar-test)
Reformulating and enhancing my previous comments:
You need to export symbols from the DLL on Windows otherwise no import library is created, and that's what MSBuild is complaining about.
First you should add to your FooBar.hpp header the following construct:
#ifdef WIN32
#ifdef FOOBARLIB_EXPORTS
#define FOOBARLIB_API __declspec(dllexport)
#else
#define FOOBARLIB_API __declspec(dllimport)
#endif
#else
#define FOOBARLIB_API
#endif
Later mark your classes, functions and symbols to be exported as follows:
void FOOBARLIB_API foobar(char*)
{
}
In your CMakeLists.txt after creating the shared library target foobar add the line:
target_compile_definitions(foobar PRIVATE FOOBARLIB_EXPORTS)
EDIT:
As #vre commented, these CMake properties are also needed because Windows does not load a DLL located in another folder, causing a crash when it tries to run the executable. So, when a DLL is build and the CMAKE_RUNTIME_OUTPUT_DIRECTORY variable is set, the output library goes to the same directory as the test .exe file.
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

Why am I not able to include header file from cmake exported/imported library

I am trying to write a header only library to be imported by others, using CMake. I tried to follow the instructions by Daniel Pfeifer - "Effective CMake". However I am not able to include the header files into another project. For illustration purpose I created a similar toy project having the tree structure:
├── testinclude
│ ├── CMakeLists.txt
│ └── testinclude.cpp
└── testlib
├── CMakeLists.txt
├── include
│ └── testLib.h
└── testLibConfig.cmake
while the library is supposed to be testLib.h containing
void sayHello(){
std::cout<<"Hello"<<'\n';
}
and the source code for the executable testinclude.cpp looks like:
#include"testLib.h"
int main(){
sayHello();
return 0;
}
Trying to follow Daniel Pfeifers guidelines my CMakeLists.txt in the directory testlib looks like:
cmake_minimum_required(VERSION 3.5)
project(testLib VERSION 0.1)
add_library(testLib INTERFACE )
target_include_directories(testLib INTERFACE
$<INSTALL_INTERFACE:include>
)
install(TARGETS testLib EXPORT testLibTargets
INCLUDES DESTINATION include
)
install(EXPORT testLibTargets
FILE testLibTargets.cmake
NAMESPACE testLib::
DESTINATION lib/cmake/testLib
)
include(CMakePackageConfigHelpers)
write_basic_package_version_file("testLibConfigVersion.cmake"
VERSION ${testLib_VERSION}
COMPATIBILITY SameMajorVersion
)
install(FILES "testLibConfig.cmake" "testLibConfigVersion.cmake"
DESTINATION lib/cmake/testLib
)
and testLibConfig.cmake:
include("${CMAKE_CURRENT_LIST_DIR}/testLibTargets.cmake")
end finally the CMakeLists.txt in the directory testinclude:
cmake_minimum_required(VERSION 3.5)
project(testInclude)
add_executable(testInclude testinclude.cpp)
find_package(testLib)
target_link_libraries(testInclude PRIVATE testLib::testLib)
After running in the directory testlib:
cmake .
sudo make install
and running in the testinclude directory:
cmake .
make
I get the error that testLib.h was not found, even though I had no complains or error before.
The comment by Tsyvarev was absolutely correct. Additionally installing the public header file "testLib.h" (into some location independent of the project) was the missing thing to do.
Adding the line
install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/include/testLib.h" DESTINATION
include/testLib )
to the "testLib/CMakeLists.txt"
fixed the problem.
I think your install doesn't include the header files.
use set_target_properties(tgt PROPERTIEs PUBLIC_HEADER "${HEADER_FILES}")
and PUBLIC_HEADER in install to install them (warning no subdirectory allowed)
also "include DESTINATION" in install should not be use, try to use target_include_directories() instead ("includes" which is a reminiscence of pre Modern CMake). It is just need when you want to add install include directories...
Otherwise seems fine to me...
e.g. https://github.com/Mizux/cmake-cpp/blob/master/Foo/CMakeLists.txt
src: https://cmake.org/cmake/help/latest/prop_tgt/PUBLIC_HEADER.html

Creating C++ library with CMake

I am trying to create a reasonable library structure and CMake file which would allow other CMake projects to easily include this library. I've found a lot of similar questions, however, none of them seems to address my exact problem.
My current structure is the following:
/*MyLibrary/
├── CMakeLists.txt (including lib subdirectories)
├── external/
│ └── googletest/
├── lib/
│ ├── common/
│ │ ├── CMakeList.txt (creates static lib common)
│ │ ├── include/common/*.h
│ │ └── src/*.cpp
│ ├── cipher/
│ │ ├── CMakeList.txt (creates static lib cipher)
│ │ ├── include/cipher/*.h
│ │ └── src/*.cpp
└── test/
├── main.cpp (code for executing google test)
├── CMakeLists.txt (creates unittest executable)
├── common/*Test.cpp
└── cipher/*Test.cpp
*/
Now I want to create a project with a similar directory structure but the problem occurs when I want to create a static library in that project with the same name as one of the static libraries in MyLibrary (common for example).
I was thinking of including the library into the project using add_subdirectory(external/MyLibrary) in the projects CMakeLists, but that fails because the names of the static libraries collide.
Even when I solve this issue by renaming the library (which I don't in fact believe is an elegant solution), I end up with googletest conflicts because both, the library and my project, are dependent on googletest.
Is there any way to solve this easily? I was thinking of 2 possibilities:
In the MyLibrary I would create one static library from all the sub-libraries called libMyLibrary.aand include only that library (preferred option, but I don't have a clue how to achieve it in CMake)
Forcing the users of the library to install it and don't include the library as a project submodule (last option I would go for)
Is there some other reasonable way to solve it which would make my library compatible with most CMake projects? If not, how can I achieve at least one of the mentioned options?
In short: how can I create a CMake file for a library so the library is easy to include in other CMake projects?
How can I deal with redundant dependencies such as googletest?
How can I create a CMake file for a library so the library is easy to include in other CMake projects?
There is no universal approach for that purpose.
By including your project into another one with add_subdirectory(), you "open" internals of your project to other one. Aside of advantage in having the library's target ready for linking, this approach has disadvantages too. Target's collisions, cached variables collisions will be your headache, among with some other problems.
If you want your project to be included into other with add_subdirectory, avoid using "generic" names for your targets. E.g., use <my_project_name>_common instead of common.
Some targets you cannot rename (like gtest). If those targets aren't really required for your project to work, create an option for disable them:
option(<my_project_name>_TESTING "Enable testing" ON)
if(<my_project_name>_TESTING)
add_subdirectory(external/googletest)
endif()
See also my answer to the similar question about targets' names collision.