Managing dependencies

Met.3D is primarily used with two different package managers. On Linux the first choice is mamba (or conda), which can be used to install third-party dependencies. Additionally, vcpkg is also supported, which can be integrated into cmake. It builds all dependencies from source.

Adding new dependencies

If you require a new dependency for Met.3D, your first step should be to see, whether the package is available via conda-forge for Linux. If it is not, you might consider an alternative that is, or provide documentation on how to use it in our installation instructions.

Additionally, you should check the vcpkg package registry and see, whether it is available for Linux and Windows. This is usually shown on the right under supported. If it is supported for all platforms except a few, only the exceptions will be listed. For example, the qtbase package is available for all platforms except uwp, so its supported list will show !uwp.

Adding a conda dependency

When adding a conda dependency to Met.3D you will need to reference it in several placed.

  • Our conda package recipe: Add the dependency to util/conda/Recipe/meta.yaml in the Met.3D repository. If the dependency is a header only library, add it to the host section. If it is also required at runtime, add it to the run section instead.

  • Documentation: Add it to the installation instructions to build Met.3D from source on Linux.

To use your newly added dependency, add the appropriate find_package command to the CMakeLists.txt and link the imported target.

Adding a vcpkg dependency

If you use a new dependency available via vcpkg, you just have to add it to the vcpkg.json file in our git repository. As with conda, you have to add the appropriate find_package command to the CMakeLists.txt and link the imported target.

Adding a non-existent vcpkg dependency

If your package is not available via vcpkg, you can add a package definition yourself. This is called an overlay port. We have a directory in our Met.3D repository, called vcpkg-overlays, that contains all third-party packages not available via vcpkg. An overlay port is essentially just a cmake script that instructs vcpkg on how to build the package.

You will need a vcpkg.json and a portfile.cmake. Optionally you can add a usage file containing information on how to find and use the library with cmake.

Inside the vcpkg.json you specify the name of the port, the version, license and dependencies. The vcpkg.json for glfx looks like this, for example:

 1{
 2    "name": "glfx",
 3    "version": "1.0",
 4    "description": "Specialised fork of GLFX for use with Met.3D.",
 5    "license": "BSD-2-Clause",
 6    "dependencies": [
 7        {
 8            "name" : "vcpkg-cmake",
 9            "host" : true
10        },
11        {
12            "name" : "vcpkg-cmake-config",
13            "host" : true
14        },
15        {
16            "name": "qtbase",
17            "default-features": false,
18            "features": ["opengl"]
19        }
20    ]
21}

It requires the vcpkg-cmake and vcpkg-cmake-config cmake helper packages to build the package, which is why the host property is set to true. It also requires qtbase at build and runtime, but only the opengl feature. The license field has to be a SPDX license identifier.

The portfile.cmake contains the build instructions. It looks like this:

 1vcpkg_from_github(
 2    OUT_SOURCE_PATH SOURCE_PATH
 3    REPO thorwin-vogt/glfx
 4    REF c575b65773625c8800ff562fbd3a5d2615a1c18d
 5    SHA512 286c1aae3e6a6d8d617aaeef68d42e57c038fcdf6ca627c5d4c60be4e3639efd9548e07c2696b7b36c48eb398dbb174d8bbc82ff6427f02aca2341289ec8a922
 6    HEAD_REF qt6
 7)
 8
 9vcpkg_cmake_configure(SOURCE_PATH ${SOURCE_PATH})
10vcpkg_cmake_install()
11vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/glfx)
12vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/COPYING")
13
14# Install usage file
15file(INSTALL "${CMAKE_CURRENT_LIST_DIR}/usage" DESTINATION "${CURRENT_PACKAGES_DIR}/share/${PORT}")

It should be fairly explanatory. We first pull the package from GitHub. When you first define your package, you can leave the SHA512 field as 0. Executing cmake will end with an error, that will contain the correct SHA512. You can now replace the 0 with the correct SHA512.

Next we configure cmake for the sources. The command vcpkg_cmake_install will build and install the library. The next line fixes some issues with glfxConfig.cmake, which is used to later find it with cmake. Not all libraries generate such a file, in which case you won’t need that line, but probably need to write a find script for your library for Met.3D. vcpkg_install_copyright installs the copyright notice. The last line installs the usage file.

The usage file for this port looks like this:

1glfx provides CMake targets:
2
3    find_package(glfx CONFIG REQUIRED)
4    target_link_libraries(main PRIVATE glfx::glfx)

Writing a cmake find script

If your dependency does not provide a *Config.cmake file, you have to write a find script for it. To do this, add a new file to the cmake directory and add a FindLibrary.cmake file. Replace the “Library” with your library name.

A minimal example could be:

  1# Set the name of the package
  2set (PKG_NAME YourLibrary)
  3
  4# Find the include directory
  5find_path(${PKG_NAME}_INCLUDE_DIR
  6    NAMES # Name of all header files
  7        YourLibrary.h
  8    PATH_SUFFIXES # Subfolders in the install directory
  9        include
 10    )
 11
 12# Find the release library
 13find_library(${PKG_NAME}_LIBRARY_RELEASE
 14    NAMES
 15        YourLibrary
 16    PATH_SUFFIXES
 17        lib64
 18        lib
 19    )
 20
 21# Find the debug library, often suffixed d.
 22find_library(${PKG_NAME}_LIBRARY_DEBUG
 23    NAMES
 24        YourLibraryd
 25    PATH_SUFFIXES
 26        lib64
 27        lib
 28    )
 29
 30# Print what libraries were not found.
 31if (NOT ${PKG_NAME}_LIBRARY_DEBUG)
 32    message("Debug library for ${PKG_NAME} was not found")
 33    set(${PKG_NAME}_LIBRARY_DEBUG "${${PKG_NAME}_LIBRARY_RELEASE}")
 34endif()
 35
 36if (NOT ${PKG_NAME}_LIBRARY_RELEASE)
 37    message("Release library for ${PKG_NAME} was not found")
 38    set(${PKG_NAME}_LIBRARY_RELEASE "${${PKG_NAME}_LIBRARY_DEBUG}")
 39endif()
 40
 41# handle the QUIETLY and REQUIRED arguments and set ${PKG_NAME}_FOUND to TRUE if
 42# all listed variables are TRUE
 43find_package_handle_standard_args(${PKG_NAME} REQUIRED_VARS ${PKG_NAME}_LIBRARIES ${PKG_NAME}_INCLUDE_DIR)
 44# Marks cmake cached variables as advanced
 45mark_as_advanced(${PKG_NAME}_INCLUDE_DIR ${PKG_NAME}_LIBRARIES)
 46
 47
 48# Create imported targets
 49# Look for dlls to create shared targets for windows
 50if (WIN32)
 51    # Set release dll path
 52    if (${PKG_NAME}_LIBRARY_RELEASE)
 53        string(REPLACE ".lib" ".dll" ${PKG_NAME}_DLL_PATH "${${PKG_NAME}_LIBRARY_RELEASE}")
 54        cmake_path(GET ${PKG_NAME}_DLL_PATH FILENAME ${PKG_NAME}_DLL_NAME)
 55
 56        find_file(${PKG_NAME}_DLL REQUIRED
 57                NAMES ${${PKG_NAME}_DLL_NAME}
 58                HINTS /bin $ENV{${PKG_NAME}_DIR}/bin
 59                PATH_SUFFIXES Release Debug)
 60
 61        unset(${PKG_NAME}_DLL_PATH)
 62        unset(${PKG_NAME}_DLL_NAME)
 63    endif()
 64
 65    # Set debug dll path
 66    if (${PKG_NAME}_LIBRARY_DEBUG)
 67        string(REPLACE ".lib" ".dll" ${PKG_NAME}_DLL_PATH_DEBUG "${${PKG_NAME}_LIBRARY_DEBUG}")
 68        cmake_path(GET ${PKG_NAME}_DLL_PATH_DEBUG FILENAME ${PKG_NAME}_DLL_NAME_DEBUG)
 69
 70        find_file(${PKG_NAME}_DLL_DEBUG
 71                NAMES ${${PKG_NAME}_DLL_NAME_DEBUG}
 72                HINTS /bin $ENV{${PKG_NAME}_DIR}/bin
 73                PATH_SUFFIXES Release Debug)
 74
 75        unset(${PKG_NAME}_DLL_PATH_DEBUG)
 76        unset(${PKG_NAME}_DLL_NAME_DEBUG)
 77    endif()
 78
 79    # Create imported targets for YourLibrary.
 80    if (${PKG_NAME}_FOUND)
 81        if (EXISTS "${${PKG_NAME}_DLL}")
 82            add_library(yourlibrary::yourlibrary SHARED IMPORTED)
 83
 84            set_target_properties(yourlibrary::yourlibrary PROPERTIES
 85                                IMPORTED_LOCATION_RELEASE     "${${PKG_NAME}_DLL}"
 86                                IMPORTED_IMPLIB               "${${PKG_NAME}_LIBRARY_RELEASE}"
 87                                INTERFACE_INCLUDE_DIRECTORIES "${${PKG_NAME}_INCLUDE_DIR}"
 88                                IMPORTED_CONFIGURATIONS       Release)
 89
 90            if (EXISTS "${${PKG_NAME}_DLL_DEBUG}")
 91                set_property(TARGET yourlibrary::yourlibrary APPEND PROPERTY IMPORTED_CONFIGURATIONS Debug)
 92                set_target_properties(yourlibrary::yourlibrary PROPERTIES
 93                                    IMPORTED_LOCATION_DEBUG "${${PKG_NAME}_DLL_DEBUG}"
 94                                    IMPORTED_IMPLIB_DEBUG   "${${PKG_NAME}_LIBRARY_DEBUG}")
 95            endif()
 96        else ()
 97            add_library(yourlibrary::yourlibrary UNKNOWN IMPORTED)
 98            set_target_properties(yourlibrary::yourlibrary PROPERTIES
 99                                IMPORTED_LOCATION             "${${PKG_NAME}_LIBRARY_RELEASE}"
100                                INTERFACE_INCLUDE_DIRECTORIES "${${PKG_NAME}_INCLUDE_DIR}")
101
102            if (EXISTS "${${PKG_NAME}_LIBRARY_DEBUG}")
103                set_property(TARGET yourlibrary::yourlibrary APPEND PROPERTY IMPORTED_CONFIGURATIONS Debug)
104                set_target_properties(yourlibrary::yourlibrary PROPERTIES
105                                    IMPORTED_LOCATION_DEBUG "${${PKG_NAME}_LIBRARY_DEBUG}")
106            endif()
107        endif()
108    endif()
109else()
110    # Create imported targets for YourLibrary.
111    if (${PKG_NAME}_FOUND)
112        add_library(yourlibrary::yourlibrary UNKNOWN IMPORTED)
113
114        set_target_properties(yourlibrary::yourlibrary PROPERTIES
115                            IMPORTED_LOCATION_RELEASE     "${${PKG_NAME}_LIBRARY_RELEASE}"
116                            INTERFACE_INCLUDE_DIRECTORIES "${${PKG_NAME}_INCLUDE_DIR}"
117                            IMPORTED_CONFIGURATIONS       Release)
118
119        if (EXISTS "${${PKG_NAME}_DLL_DEBUG}")
120            set_property(TARGET yourlibrary::yourlibrary APPEND PROPERTY IMPORTED_CONFIGURATIONS Debug)
121            set_target_properties(yourlibrary::yourlibrary PROPERTIES
122                                IMPORTED_LOCATION_DEBUG "${${PKG_NAME}_LIBRARY_DEBUG}")
123        endif()
124    endif()
125endif()

This find script will create imported targets named yourlibrary::yourlibrary. You can then link to that library via target_link_libraries(Met3D PUBLIC yourlibrary::yourlibrary). This will also add its include directory to Met.3D’s include path.