How to set BUILD_SHARED_LIBS for included directory - cmake

I have a very basic CMake project that uses Google Test. I want to build it as a dll, and the CMakeLists.txt file in gtest indicates that BUILD_SHARED_LIBS needs to be set in order for gtest to be built as a shared library.
My problem is that I cannot figure out how to set BUILD_SHARED_LIBS so that it shows up. If I use cmake-gui an set the value in the cache, then I do indeed see the generated build attempt to create a dll.
Below is my CMakeLists.txt. I would appreciate any suggestions on how to make it set BUILD_SHARED_LIBS.
CMAKE_MINIMUM_REQUIRED(VERSION 3.0 FATAL_ERROR)
PROJECT(MyProj)
SET(BUILD_SHARED_LIBS ON)
ADD_EXECUTABLE(MyProj main.cpp)
ADD_LIBRARY(MyLib STATIC mylib.cpp)
TARGET_LINK_LIBRARIES(MyProj MyLib)
ADD_SUBDIRECTORY(gtest-1.7.0)
Google Test will only build as a shared library (DLL) if BUILD_SHARED_LIBS is set. Hence I want to set that in this CmakeLists.txt file. I know how to make my own library shared, but I can't figure out how to set that variable in a way that the gtest CMakeLists.txt file sees it.

In your code
ADD_LIBRARY(MyLib STATIC mylib.cpp) // Your code STATIC lib
Change above line to this
ADD_LIBRARY(MyLib SHARED mylib.cpp) // Shared Lib is added

Related

CMake building multiple libraries in the same project

I have a specific CMake project I would like to build as both a static lib and a shared object (Linux only). The relevant part of the CMake file:
...
# Static lib
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
# Build shared object in Linux only
if(UNIX)
set_target_properties(${PROJECT_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
add_library("${PROJECT_NAME}_SHARED" SHARED ${SOURCES} ${HEADERS})
endif(UNIX)
link_directories(${ILI_EXTERNAL_LIB_DIRS})
target_link_libraries(${PROJECT_NAME} ${ILI_EXTERNAL_LIBS} IliLib PlantModel)
I can figure out why the shared object is not being built, when I add a message within the Unix block, it gets printed during the make build. The rest of this cmake file only sets variables and declares the project name.
Any tips for how to go about debugging this issue? On both Windows and Linux, it only builds the static lib at present.
Edit:
Link to CMake and Make log outputs.
The solution was suggested by #Tsyvarev, trivial error on my part.
The static lib was being built because it was a dependency of another build target. I had not explicitly run "Make" on the SimulatorLib project.
CMake messages appear in the CMake log irrespective of whether that particular project is being built.

Linking against GTest fails

I have the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.15)
set(CMAKE_CXX_STANDARD 17)
# adding library
set(ST_SRC simple_tree.cpp)
add_library(st ${ST_SRC})
target_include_directories(st PUBLIC ${PROJECT_SOURCE_DIR}/include/data_structures/simple_tree/)
# adding googletest
set(GOOGLETEST_PATH ~/local/googletest)
set(GTEST_INCLUDE_DIR ~/local/include/)
set(GTEST_LIBRARY ~/local/lib/)
set(GTEST_MAIN_LIBRARY ~/local/lib/)
find_package(GTest REQUIRED)
# adding tests
set(TEST_TARGET test_simple_tree)
add_executable(${TEST_TARGET} test_simple_tree.cpp)
target_include_directories(${TEST_TARGET}
PUBLIC
${PROJECT_SOURCE_DIR}/include/data_structures/simple_tree
${GOOGLETEST_PATH}
${GTEST_INCLUDE_DIR})
target_link_libraries(${TEST_TARGET} PUBLIC st)
target_link_libraries(${TEST_TARGET} PUBLIC gtest gtest_main)
Basically, I've installed googletest into my home directory rather than system-wide.
The find_package() command apparently succeeds. However, trying to build test_simple_tree fails with:
/usr/bin/ld: cannot find -lgtest
/usr/bin/ld: cannot find -lgtest_main
Inside this CMakeLists.txt, how else can I tell the linker to look elsewhere
for the gtest?
EDIT: After reading the docs, I've fixed the Gtest issue as described below. However, the following issue cropped up: CMake imported target includes non-existent path
If find_package() was successful in finding the GTest includes/libraries, it should populate targets for you, per the CMake FindGTest documentation:
This module defines the following IMPORTED targets:
GTest::GTest:
The Google Test gtest library, if found; adds Thread::Thread automatically
GTest::Main:
The Google Test gtest_main library, if found
You should use these in your target_link_libraries() command instead. Also, CMake will populate GTEST_INCLUDE_DIRS, but the GTest include directories should be pulled in from the imported targets mentioned above.
Another important note: I'm not sure if you posted all of your CMake code, but I don't see a project() call in your code. As a result, the ${PROJECT_SOURCE_DIR} variable may not be what you expect. In general, it is good practice to declare your project with project() at the top of your CMake.
Your CMake file with these modifications could look something like this:
cmake_minimum_required(VERSION 3.15)
project(simple_tree_example)
set(CMAKE_CXX_STANDARD 17)
# adding library
set(ST_SRC simple_tree.cpp)
add_library(st ${ST_SRC})
target_include_directories(st PUBLIC
${PROJECT_SOURCE_DIR}/include/data_structures/simple_tree/)
# adding googletest
set(GOOGLETEST_PATH ~/local/googletest)
set(GTEST_INCLUDE_DIR ~/local/include/)
set(GTEST_LIBRARY ~/local/lib/)
set(GTEST_MAIN_LIBRARY ~/local/lib/)
find_package(GTest REQUIRED)
# adding tests
set(TEST_TARGET test_simple_tree)
add_executable(${TEST_TARGET} test_simple_tree.cpp)
target_include_directories(${TEST_TARGET}
PUBLIC
${PROJECT_SOURCE_DIR}/include/data_structures/simple_tree
${GOOGLETEST_PATH})
target_link_libraries(${TEST_TARGET} PUBLIC st GTest::GTest GTest::Main)
Targets gtest and gtest_main are created only when GTest is used via add_subdirectory() approach.
When use GTest via find_package(), one need to use either IMPORTED targets GTest::GTest and GTest::Main, or variables GTEST_LIBRARIES and GTEST_MAIN_LIBRARIES correspondingly. This is described in the documentation:
find_package(GTest REQUIRED)
...
target_link_libraries(test_simple_tree PUBLIC GTest::GTest gtest_main)
or
find_package(GTest REQUIRED)
...
# That case we need to add include directories
target_include_directories(test_simple_tree ${GTEST_INCLUDE_DIRS})
target_link_libraries(test_simple_tree PUBLIC ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})

Providing include directory outside source folder for static library users

I am developing a simple static C library for learning purposes using cmake.
Some projects like GLFW provide an include folder on the root, so library users can copy it and use it as an include directory.
In my library, I want to have an include folder on the root, so when I use the library on other projects, I can just copy this folder and set it as an include directory.
Here is a simplified folder structure of my library:
include
+--mylib.h
src
+--myheader.h
+--mysource.c
+--CMakeLists.txt
CmakeLists.txt
The src folder has my headers and implementation files, and a CMakeLists.txt for building a static library out of mysource.c.
The CMakeLists on the root folder just sets the project and adds src as a subdirectory.
I want the mylib.h file to have a #include <myheader.h>.
Here's a detour to talk about how I want to use it when it's done.
The idea is that when using the lib on another project, I can have something like this:
deps
+--include
+--mylib.h
src
+--main.c
And in the main.c file, include mylib.h and use what's defined on myheader.h
Here the detour ends, and I'm talking about my actual lib project again.
How can I achieve this using cmake? As far as I know, the mylib.h file needs to know it's including files from the src diretory, but I see no way of setting that, as for exemple in GLFW this directory does not have a CMakeLists.txt.
I am gonna quess that this is a design issue since it would make sense to you if you would have installed the library to a system before you tried to use it. That is, not using add_subdirectory() but find_library() at usage.
First, if you are using a external library, but not installing it, you would include all files in you deps-folder. All files then include source-files and so on and will be compiled besides you main.c. This is done with add_subdirectory(deps/MyLib) and later also included in you main-project.
Example:
add_subdirectory(deps/MyLib EXCLUDE_FROM_ALL)
target_link_libraries(${PROJECT_NAME} PRIVATE MyLib)
target_include_directories(${PROJECT_NAME} PRIVATE MyLib)
If you do not want to compile it all the time, then you must instruct cmake where it can find headers and library-files. Preferred way is to use find_library() which does some magic for you. Since you do not mention any installation i will assume that it does not exist and your only option is then to use add_subdirectory().
"I can just copy this folder and set it as an include directory."
CMake wants to handle these things for you so you should never copy headers around. You should either use add_subdirectory() to include a project/headers or make use of the find_library() which make sure you find where the headers are in the system.
I suggest that you push yourself to learn howto install a library into a system and how to utilize it later, but only by using find_library(). Then the library will be global for all projects and also not duplicated.
Adding some kind of psudo-code in hope it all makes more sense. Although it is around add_subdirectory() since the code for installing is quite large unfortunately.
CMakeLists.txt for main.c
cmake_minimum_required(VERSION 3.8)
project(MyLibTest)
add_executable(${PROJECT_NAME}
src/main.c
)
add_subdirectory(external/MyLib EXCLUDE_FROM_ALL)
target_link_libraries(${PROJECT_NAME} PRIVATE MyLib)
target_include_directories(${PROJECT_NAME} PRIVATE MyLib)
CMakeLists.txt for library
cmake_minimum_required(VERSION 3.8)
project(MyLib)
add_library(${PROJECT_NAME} STATIC
src/MyLib.c
)
target_include_directories(${PROJECT_NAME}
PUBLIC
$<INSTALL_INTERFACE:include>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
)
The structure for the project would then be:
/
external/MyLib
external/MyLib/src
MyLib.c
external/MyLib/include
MyLib.h
src
main.c
CMakeLists.txt

How do I specify the include directory for an imported shared library using CMake?

I am using cmake version 3.9.1.
I have a third party shared library and header file in my source tree. I am trying to add it as a link target.
All the documentation I can find says that this should work:
test.cpp
#include "ftd2xx.h"
int main(int argc, char **argv)
{
FT_HANDLE handle;
FT_STATUS status = FT_Open(1, &handle);
return 0;
}
CMakeLists.txt
cmake_minimum_required (VERSION 3.6)
project(test_proj CXX)
add_subdirectory(ftdi)
add_executable(mytest test.cpp)
target_link_libraries(mytest ftd2xx)
ftdi/CMakeLists.txt
add_library(ftd2xx SHARED IMPORTED)
set_target_properties(ftd2xx PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(ftd2xx PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR})
However compiling test.cpp, which includes "ftd2xx.h", complains that it cannot find the header file and the relevant -I<path> entry is missing from the generated makefiles.
If I specify the library as INTERFACE rather than SHARED IMPORTED then the header file is found correctly, but CMake barfs on setting the IMPORTED_LOCATION property.
If I specify the library as INTERFACE rather than SHARED IMPORTED and then use target_link_libraries to point directly to the library file than this works for Windows but not for Linux.
I'd appreciate any help anyone can offer.
The CMake documentation does actually answer this one, but so concisely, and in the middle of a much larger paragraph, that it is easy to miss:
The target name has scope in the directory in which it is created and below, but the GLOBAL option extends visibility.
I am using the target name in a higher level directory, so I need to declare the library as SHARED IMPORTED GLOBAL rather than just SHARED IMPORTED.
Final code is:
add_library(ftd2xx SHARED IMPORTED GLOBAL)
set_target_properties(ftd2xx PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
And then for Windows:
set_target_properties(ftd2xx PROPERTIES IMPORTED_IMPLIB ${CMAKE_CURRENT_SOURCE_DIR}/win32/ftd2xx.lib)
set_target_properties(ftd2xx PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/win32/ftd2xx.dll)
And for Linux:
set_target_properties(ftd2xx PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/i386/libftd2xx.so)

CMake execution order - first build shared library then look for it from another project

I have the following cmake setup:
colorizer_root
|
|-------colorizer_lib
|-------colorizer_template_project
The colorizer_root contains the top level CMakeLists.txt which is invoked when running cmake:
colorizer_root CMakeLists.txt
project(colorizer_root)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_BUILD_TYPE Debug)
add_subdirectory(colorizer_lib)
add_subdirectory(colorizer_template_project)
As you can see it contains 2 subdirectories each a project on its own. Basically what the colorizer_lib does is create a shared library named libcolorize.so (no executables here!), which then is to be used by the other project colorizer_template_project (the executable is created in this project). Here are the two CMakeLists.txt files for their respective projects:
colorizer_lib CMakeLists.txt
project(colorizer_lib)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_CXX_FLAGS "--std=gnu++11 ${CMAKE_CXX_FLAGS}")
include_directories(. INCLUDES)
add_library(colorizer SHARED colorizer.cpp)
colorizer_template_project CMakeLists.txt
project(colorizer_template_project)
cmake_minimum_required(VERSION 2.8)
find_library(COLORIZER_LIB colorizer
PATHS ${CMAKE_BINARY_DIR}/colorizer_lib
)
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})
target_link_libraries(${PROJECT_NAME} ${COLORIZER_LIB})
I'm having trouble figuring out how the whole lookup thing works. The problem here is that when I run the top level CMakeLists.txt it goes through both (obviously) but during processing the colorizer_template_project it breaks with a complaint:
CMake Error: The following variables are used in this project, but they are set to NOTFOUND.
Please set them or make sure they are set and tested correctly in the CMake files:
COLORIZER_LIB
linked by target "colorizer_template_project" in directory /home/USER/Programming/C_Cpp/colorizer/colorizer_template_project
This is an expected behaviour since libcolorizer.so cannot be present at the time of running cmake because it is created after make has been invoked.
How do I tell cmake to first process the first project (including the build step!) and then go to the next one? I know that this works if I add an executable to the project that creates the library and then directly link it to the binary but in this case I want separate projects for the library and the executable that is using it.
PS: I haven't given any details about the sources because they are not important here. It is - I believe - a general question, which is not specific to whether I'm using C, C++ or something similar.
project command doesn't make subprojects independent, so colorizer target is actually accessible for colorizer_template_project, and you can directly link with it:
add_executable(${PROJECT_NAME} ${SRC_LIST})
target_link_libraries(${PROJECT_NAME} colorizer)