0

I'm new(ish) to CMake (meaning off and on over a few years I've been forced to use it even though it's something that's made me question my career path).

I have a folder with an include folder, and a lib folder containing .lib files and their corresponding .dlls for a dependency I'll call "mydep". I need to provide the infrastructure files, mydep-config.cmake, mydep-target.cmake, etc, which will add the includes folders and .lib files to the command line for compiling and linking and then move the .dlls to a specific location.

Can anyone point me to a simple example anywhere on the net that illustrates the way to do this? The CMake documentation is utterly useless.

Thanks!

  • I assume the targets you're trying to import aren't your own libs with cmake projects provided for them? There is some functionality for generating much of the logic needed automatically, if you're building a lib using your own cmake project... – fabian Sep 23 '22 at 16:19
  • 1
    "The CMake documentation is utterly useless." - Here is an example in CMake documentation: https://cmake.org/cmake/help/latest/manual/cmake-developer.7.html#a-sample-find-module – Tsyvarev Sep 23 '22 at 16:27
  • 1
    And here are 2 more parts of the cmake documentation you claim to be utterly useless providing relevant information: https://cmake.org/cmake/help/latest/guide/importing-exporting/index.html#importing-targets and https://cmake.org/cmake/help/latest/command/find_package.html#config-mode-search-procedure – fabian Sep 23 '22 at 16:30
  • 1
    Btw: If you build and install boost, you've got good examples of the required cmake config files for `find_package(Boost ...)` available, but they may not be the simplest ones out there... `libpng` seems to provide a simpler alternative... – fabian Sep 23 '22 at 16:32
  • @fabian - yes - they are externals I have and need to package (I'll forgo my opinions of vcpckg) and thanks - I'll have a look at the links you provided and see what I can do with them. I'll check out libpng as well (we may have one of those. "Utterly useless" may have been a bit of histrionics on my part...still, i like simple (and perhaps it's because I AM simple) and CMake is the antithesis of it. Immensely powerful and equally difficult to soak up. – Sean Michael McGhee Sep 23 '22 at 17:14
  • @Tsyvarev - thanks for the link! I will check it out. apologize for being a bit whiny but not for disliking CMake. That's a big club. – Sean Michael McGhee Sep 23 '22 at 17:14

1 Answers1

0

There are basically 2 files you need to put in the correct location on the file system. Let's assume the library is to import is called ExternLib with version 1.2.3 and compiled for 64 bit. With the and you've got it stored in your repo as

external_dependencies/lib/ExternLib.lib
external_dependencies/bin/ExternLib.dll
external_dependencies/include/ExternLib/ExternHeader.hpp
external_dependencies/include/ExternLib/...
...

and you want the include paths used in your project to be

#include "ExternLib/ExternHeader.hpp"

First of all we'll use the file names externlib-config.cmake and externlib-version-config.cmake to allow the user to use arbitrary case when using find_package.

externlib-version-config.cmake

This file is used to check, if the version of the config script is compatible with the cmake configuration trying to find the package. Adding this file allows you to place multiple versions of the library to be found for different target architectures (e.g. Windows x64, Win32, linux x86_64, ...). find_package won't read the externlib-config.cmake in the same directory, if the version is marked as non-compatible via the version file.

set(PACKAGE_VERSION "1.2.3")

# basically ignore the version passed
set(PACKAGE_VERSION_COMPATIBLE TRUE) # consider every version compatible

#usually you'd add logic for ignoring any unspecified parts of the version here instead of treating unspecified version parts as 0
# we'll keep it simple though
if(PACKAGE_FIND_VERSION VERSION_EQUAL PACKAGE_VERSION)
    set(PACKAGE_VERSION_EXACT TRUE) # make version as exact match
endif()

if(NOT WIN32)
  set(PACKAGE_VERSION_COMPATIBLE FALSE) # disallow older version
else()

if (NOT CMAKE_SYSTEM_NAME STREQUAL "Windows") # note: we'll ignore the possibility of find_package before the first project() command
    set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()

if(NOT CMAKE_SIZEOF_VOID_P STREQUAL "8") # check for 64 bit
    set(PACKAGE_VERSION_UNSUITABLE TRUE)
endif()

externlib-config.cmake

Here we'll put the creation of the actual library in addition to providing any functions/macros/variables the user should have access to. We'll use ExternLib::ExternLib as target name, but you can simply replace this by any name of your choosing unique in your project.

if(NOT TARGET ExternLib::ExternLib) # prevent recreation on multiple uses of find_package
    add_library(ExternLib::ExternLib SHARED IMPORTED)
    get_filename_component(EXTERNLIB_BASE_DIR ${CMAKE_CURRENT_LISTS_DIR}/../../.. ABSOLUTE)

    set(EXTERNLIB_BINARY_DIR ${EXTERNLIB_BASE_DIR} CACHE INTERNAL "Directory containing the dlls for ExternLib")

    # add info about locations of the dll/lib files
    set_target_properties(ExternLib::ExternLib PROPERTIES
        IMPORTED_LOCATION "$CACHE{EXTERNLIB_BINARY_DIR}/ExternLib.dll"
        IMPORTED_IMPLIB "${EXTERNLIB_BASE_DIR}/lib/ExternLib.lib"
    )

    # add include directory information
    target_include_directories(ExternLib::ExternLib INTERFACE "${EXTERNLIB_BASE_DIR}/include")

    # add dependencies, if required; you may need to use find_package to locate those
    # target_link_libraries(ExternLib::ExternLib INTERFACE ...)
endif()

# any helper function/macro definitions should go here, since they may need to get reintroduced

Note: I did ignore the possibility of components of a package being specified here. Any components specifying components for find_package are simply ignored for the script above.

Placement of the files

You'll need to place the files in one of some possible paths below one of the directories mentioned in CMAKE_PREFIX_PATH or in one of the default dirs, see find_package Config Mode Search Procedure.

Note: we'll use a path that isn't documented for windows, since this path would also work for linux. (Boost is doing this too.)

lib/cmake/ExternLib-x64-1.2.3

(You could choose a different suffix to lib/cmake/ExternLib or just use lib/cmake/ExternLib. The search procedure picks up on any directory names which starts with the package name ignoring case, if it expects the lib name.)

Place both the files in this directory. externlib-config.cmake assumes lib is external_dependencies/lib here. Otherwise you may need to adjust EXTERNLIB_BASE_DIR accordingly.

Usage

We'll assume the CMakeLists.txt file is placed in the same directory as external_dependencies

project(...)

...
add_executable(my_exe ...)
...

# this allows the user to pass directories to be searched first via -D option during configuration
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_SOURCE_DIR}/external_dependencies)

find_package(ExternLib REQUIRED)

target_link_libraries(my_exe PRIVATE ExternLib::ExternLib)

# allow vs debugger to find the dll without copying the dlls around
set_target_properties(my_exe PROPERTIES
    VS_DEBUGGER_ENVIRONMENT "PATH=${EXTERNLIB_BINARY_DIR};$ENV{PATH}"
)
fabian
  • 80,457
  • 12
  • 86
  • 114