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: .. code-block:: json :linenos: { "name": "glfx", "version": "1.0", "description": "Specialised fork of GLFX for use with Met.3D.", "license": "BSD-2-Clause", "dependencies": [ { "name" : "vcpkg-cmake", "host" : true }, { "name" : "vcpkg-cmake-config", "host" : true }, { "name": "qtbase", "default-features": false, "features": ["opengl"] } ] } 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: .. code-block:: cmake :linenos: vcpkg_from_github( OUT_SOURCE_PATH SOURCE_PATH REPO thorwin-vogt/glfx REF c575b65773625c8800ff562fbd3a5d2615a1c18d SHA512 286c1aae3e6a6d8d617aaeef68d42e57c038fcdf6ca627c5d4c60be4e3639efd9548e07c2696b7b36c48eb398dbb174d8bbc82ff6427f02aca2341289ec8a922 HEAD_REF qt6 ) vcpkg_cmake_configure(SOURCE_PATH ${SOURCE_PATH}) vcpkg_cmake_install() vcpkg_cmake_config_fixup(CONFIG_PATH lib/cmake/glfx) vcpkg_install_copyright(FILE_LIST "${SOURCE_PATH}/COPYING") # Install usage file file(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: .. code-block:: text :linenos: glfx provides CMake targets: find_package(glfx CONFIG REQUIRED) 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: .. code-block:: cmake :linenos: # Set the name of the package set (PKG_NAME YourLibrary) # Find the include directory find_path(${PKG_NAME}_INCLUDE_DIR NAMES # Name of all header files YourLibrary.h PATH_SUFFIXES # Subfolders in the install directory include ) # Find the release library find_library(${PKG_NAME}_LIBRARY_RELEASE NAMES YourLibrary PATH_SUFFIXES lib64 lib ) # Find the debug library, often suffixed d. find_library(${PKG_NAME}_LIBRARY_DEBUG NAMES YourLibraryd PATH_SUFFIXES lib64 lib ) # Print what libraries were not found. if (NOT ${PKG_NAME}_LIBRARY_DEBUG) message("Debug library for ${PKG_NAME} was not found") set(${PKG_NAME}_LIBRARY_DEBUG "${${PKG_NAME}_LIBRARY_RELEASE}") endif() if (NOT ${PKG_NAME}_LIBRARY_RELEASE) message("Release library for ${PKG_NAME} was not found") set(${PKG_NAME}_LIBRARY_RELEASE "${${PKG_NAME}_LIBRARY_DEBUG}") endif() # handle the QUIETLY and REQUIRED arguments and set ${PKG_NAME}_FOUND to TRUE if # all listed variables are TRUE find_package_handle_standard_args(${PKG_NAME} REQUIRED_VARS ${PKG_NAME}_LIBRARIES ${PKG_NAME}_INCLUDE_DIR) # Marks cmake cached variables as advanced mark_as_advanced(${PKG_NAME}_INCLUDE_DIR ${PKG_NAME}_LIBRARIES) # Create imported targets # Look for dlls to create shared targets for windows if (WIN32) # Set release dll path if (${PKG_NAME}_LIBRARY_RELEASE) string(REPLACE ".lib" ".dll" ${PKG_NAME}_DLL_PATH "${${PKG_NAME}_LIBRARY_RELEASE}") cmake_path(GET ${PKG_NAME}_DLL_PATH FILENAME ${PKG_NAME}_DLL_NAME) find_file(${PKG_NAME}_DLL REQUIRED NAMES ${${PKG_NAME}_DLL_NAME} HINTS /bin $ENV{${PKG_NAME}_DIR}/bin PATH_SUFFIXES Release Debug) unset(${PKG_NAME}_DLL_PATH) unset(${PKG_NAME}_DLL_NAME) endif() # Set debug dll path if (${PKG_NAME}_LIBRARY_DEBUG) string(REPLACE ".lib" ".dll" ${PKG_NAME}_DLL_PATH_DEBUG "${${PKG_NAME}_LIBRARY_DEBUG}") cmake_path(GET ${PKG_NAME}_DLL_PATH_DEBUG FILENAME ${PKG_NAME}_DLL_NAME_DEBUG) find_file(${PKG_NAME}_DLL_DEBUG NAMES ${${PKG_NAME}_DLL_NAME_DEBUG} HINTS /bin $ENV{${PKG_NAME}_DIR}/bin PATH_SUFFIXES Release Debug) unset(${PKG_NAME}_DLL_PATH_DEBUG) unset(${PKG_NAME}_DLL_NAME_DEBUG) endif() # Create imported targets for YourLibrary. if (${PKG_NAME}_FOUND) if (EXISTS "${${PKG_NAME}_DLL}") add_library(yourlibrary::yourlibrary SHARED IMPORTED) set_target_properties(yourlibrary::yourlibrary PROPERTIES IMPORTED_LOCATION_RELEASE "${${PKG_NAME}_DLL}" IMPORTED_IMPLIB "${${PKG_NAME}_LIBRARY_RELEASE}" INTERFACE_INCLUDE_DIRECTORIES "${${PKG_NAME}_INCLUDE_DIR}" IMPORTED_CONFIGURATIONS Release) if (EXISTS "${${PKG_NAME}_DLL_DEBUG}") set_property(TARGET yourlibrary::yourlibrary APPEND PROPERTY IMPORTED_CONFIGURATIONS Debug) set_target_properties(yourlibrary::yourlibrary PROPERTIES IMPORTED_LOCATION_DEBUG "${${PKG_NAME}_DLL_DEBUG}" IMPORTED_IMPLIB_DEBUG "${${PKG_NAME}_LIBRARY_DEBUG}") endif() else () add_library(yourlibrary::yourlibrary UNKNOWN IMPORTED) set_target_properties(yourlibrary::yourlibrary PROPERTIES IMPORTED_LOCATION "${${PKG_NAME}_LIBRARY_RELEASE}" INTERFACE_INCLUDE_DIRECTORIES "${${PKG_NAME}_INCLUDE_DIR}") if (EXISTS "${${PKG_NAME}_LIBRARY_DEBUG}") set_property(TARGET yourlibrary::yourlibrary APPEND PROPERTY IMPORTED_CONFIGURATIONS Debug) set_target_properties(yourlibrary::yourlibrary PROPERTIES IMPORTED_LOCATION_DEBUG "${${PKG_NAME}_LIBRARY_DEBUG}") endif() endif() endif() else() # Create imported targets for YourLibrary. if (${PKG_NAME}_FOUND) add_library(yourlibrary::yourlibrary UNKNOWN IMPORTED) set_target_properties(yourlibrary::yourlibrary PROPERTIES IMPORTED_LOCATION_RELEASE "${${PKG_NAME}_LIBRARY_RELEASE}" INTERFACE_INCLUDE_DIRECTORIES "${${PKG_NAME}_INCLUDE_DIR}" IMPORTED_CONFIGURATIONS Release) if (EXISTS "${${PKG_NAME}_DLL_DEBUG}") set_property(TARGET yourlibrary::yourlibrary APPEND PROPERTY IMPORTED_CONFIGURATIONS Debug) set_target_properties(yourlibrary::yourlibrary PROPERTIES IMPORTED_LOCATION_DEBUG "${${PKG_NAME}_LIBRARY_DEBUG}") endif() endif() endif() 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. .. _mamba: https://mamba.readthedocs.io/en/latest/installation/mamba-installation.html .. _conda-forge: https://conda-forge.org/packages/ .. _vcpkg: https://vcpkg.io/en/ .. _vcpkg package registry: https://vcpkg.io/en/packages .. _vcpkg portfile reference: https://learn.microsoft.com/en-us/vcpkg/maintainers/functions/vcpkg_acquire_msys .. _SPDX license identifier: https://spdx.org/licenses/