3

I am building a library in C++ that requires a few libraries to be included, some of which being GLEW, SDL2, and GLM. I am using CMake to build this library, and have successfully set up (at least to my knowledge) a CMakeLists.txt that adequately does this, but currently without dependencies. I would like to know the proper conventions for adding these external libraries to my own library, keeping in mind that someone on a different machine may be using this library (i.e. not defined file structure/local installs).

This is my current CMakeLists.txt:

cmake_minimum_required(VERSION 3.8)
project(mylib VERSION 1.0.1 LANGUAGES CXX)

set (DEFAULT_BUILD_TYPE "Release")
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
    message(STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as none was specified.")
    set(CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE STRING "Choose the type of build." FORCE)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()

include (GNUInstallDirs)

set (SOURCE_FILES "src/driver.cpp")
add_library(${PROJECT_NAME} ${SOURCE_FILES})

target_include_directories(
    ${PROJECT_NAME} PUBLIC
    $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
    $<INSTALL_INTERFACE:include>
    PRIVATE src
)

set_target_properties (
    ${PROJECT_NAME} PROPERTIES
    VERSION ${PROJECT_VERSION}
    SOVERSION 1
)

install (
    TARGETS ${PROJECT_NAME} EXPORT mylibConfig
    ARCHIVE  DESTINATION ${CMAKE_INSTALL_LIBDIR}
    LIBRARY  DESTINATION ${CMAKE_INSTALL_LIBDIR}
    RUNTIME  DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install (
    DIRECTORY include/ 
    DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}
)

install (
    EXPORT mylibConfig 
    DESTINATION share/mylib/cmake
)

export (
    TARGETS ${PROJECT_NAME}
    FILE mylibConfig.cmake
)

If you similarly notice any key errors/mistakes in my present file, please feel free to let me know, but the more important matter is how I should be properly including these libraries.

6
  • how did you add your dependencies to your project? manually, submodule, findpackage? Commented Aug 6, 2021 at 23:28
  • @Saeid That is part of my question, I am wondering the best practices for this, whether it be through findpackage etc. Portability is important to me, so any answer would need to allow for that. Commented Aug 6, 2021 at 23:34
  • submodule method and findpackage both are portable, if you don't need to change your dependencies(ex: SDL) yourself, you can go with submodule, it's so easy to use and also it's portable, some times your dependencies doesn't have findpackage module, that's also a good time for using submodule, but if the dependency has findpackage, you also can use findpackage and it's also portable. Commented Aug 6, 2021 at 23:37
  • @Saeid If by change you mean modify the dependency, then no I do not. Both of these options do sound viable, I am unfamiliar with submodule usage however. Would these methods also allow for scalability, as the 3 packages I mentioned are just the tip of the iceberg. Commented Aug 6, 2021 at 23:47
  • submodule is a about git, if your packages are on github/gitlab/..., then you can use submodule for all of them, and you can even update your submodules to the updated versions that are on github,... Commented Aug 6, 2021 at 23:49

3 Answers 3

4

To dynamically link GLEW, SDL2, and GLM, you can use the find_package command.

find_package(GLEW REQUIRED)
find_package(SDL2 REQUIRED)
find_package(glm REQUIRED)

Then, after you've called add_library, you will need to link the libraries to your library:

target_link_libraries(mylib PRIVATE GLEW::GLEW SDL2::SDL2 glm::glm)

If you expose any of those dependencies in your API, then you can call target_link_libraries with PUBLIC (instead of PRIVATE) for those dependencies instead.

Sign up to request clarification or add additional context in comments.

2 Comments

I have edited your answer to use imported targets. When imported targets are available, they should always be used.
Upon running into an error such as Could NOT find GLEW (missing: GLEW_INCLUDE_DIRS GLEW_LIBRARIES), would the common list(APPEND CMAKE_PREFIX_PATH <pathToPackage>) solution be adequate, or is there a better way to allow CMake to find this?
3

Consider using a package manager like conan. I took a look for you at conan center and there are recipes for your dependencies. You can follow the getting started guide and it should just work.

Comments

2
+50

In CMake there are many ways to handle dependencies,

  • manually
  • using submodule
  • using fine_package
  • ...

But here I just refer to 2 methods which are easy and portable.

Submodule

If the dependency exists on the github/gitlab or some places that uses git, then you can easily use submodule method.

with submodule your dependencies can be updated always(through their github/gitlab pages), but you can't change them yourself because your changes will be locally(like every time you clone a repository, your changed will be locally unless you do a pull request or be a contributor).

Usage

For using submodule, you need to have this section on your .CMakeLists.txt file:

#--------------------------------------------------------------
# submodule section
# here we use the following code to support
# old versions of git(that don't download submodule
# contents automatically).
#--------------------------------------------------------------
# [[[
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
  if(GIT_SUBMODULE)
    message(STATUS "Submodule update")
    execute_process(
      COMMAND ${GIT_EXECUTABLE} submodule update
      --init --recursive
      WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
      RESULT_VARIABLE GIT_SUBMOD_RESULT
    )
    if(NOT GIT_SUBMOD_RESULT EQUAL "0")
      message(FATAL_ERROR "Git submodule update --init failed with ${GIT_SUBMODULE_RESULT}, Please checkout submodules")
    endif()
  endif()
endif()
# ]]]
#

This easily run git submodule update --init --recursive automatically for you. and then you can use your submodule like this:

# your submodule directory that has a main .CMakeLists.txt file inside it.
add_subdirectory("YOUR_SUBMODULE_DIRECTORY")

target_include_directories(
  executable_or_library_name PRIVATE
  YOUR_SUBMODULE_DIRECTORY/include
)

Here's an example for submodule.

find_package

If the dependency has a find_package support, then you use find_package method, specially if it has this support and it's not on the github/gitlab or a place that uses git, you should use this method then for sure.

Usage

It's also easy to use, with just most of the times one command:

find_package(dependency)

target_include_directories(YOUR_TARGET 
"${dependency_INCLUDE_DIR}"
# ... your other include directories
)

target_link_libraries(YOUR_TARGET 
"${dependency_LIBRARIES}"
# ... your other libraries to add
)

There are many possible arguments like REQUIRED and COMPONENTS that are explained in the official cmake website

Not part of this question but useful (Side Note)

I usually have this at the end of my main .CMakeLists.txt to support emacs/vim/... and every editors which uses LSP for finding symbols:

#--------------------------------------------------------------
# Generating compile_commands.json file for lsp servers
#--------------------------------------------------------------
# [[[
option(CMAKE_EXPORT_COMPILE_COMMANDS "Generate lsp command file" ON)

if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json")
  execute_process(
    COMMAND ${CMAKE_COMMAND} -E copy_if_different
    ${CMAKE_CURRENT_BINARY_DIR}/compile_commands.json
    ${CMAKE_CURRENT_SOURCE_DIR}/compile_commands.json
  )
endif()
# ]]]
#--------------------------------------------------------------

4 Comments

For using find_package(), what is the proper way of including the dependency folder in the project, would it need to be included in my library's structure, or would it simply require the user of my library to have them locally installed and imported on their side? Similarly, when using find_package(), what would be the proper way to link it, with target_link_libraries() or something else?
@Timesis always in modern cmake, use the target_*_() functions, and yes, if you use find_package(or even submodule), you need to use target_link_libraries() to add that library to your target(library/executable), and yes it's the best way of modern cmake to use that function. sometimes, you can check the errors and warnings, depends on the library you are adding, you maybe need to use target_include_directories() as well!
How could I make the target_include_directories compatible with my current orientation presented in my original question?
@Timesis Your target_include_directories is Ok, but you just need to add your library's include folders to it "${dependency_INCLUDE_DIR}", dependency can be any include folder of any library that you are using it.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.