# CMakeListsUtilities

Various useful bits of code used for writing CMakeLists.txt files:

  - CheckProjectIsTopLevel: sets PROJECT_IS_TOP_LEVEL even if CMake version < 3.21
  - GNUPackageInstallDirs: extension of GNUInstallDirs which adds project-specific directories for libraries, header files, data files, and CMake package config files
  - StandardRpathHandling: make sure all libraries/executables have correct RPATH settings
  - InstallConfigPackage: installs Config files so other packages can find us (no version information)
  - InstallConfigPackageVersion: installs Config files so other packages can find us (with version information)
  - projectConfig.cmake.in: template to be used as base for package Config files

## StandardRpathHandling

Usage:
~~~{.cmake}
include(StandardRpathHandling)
~~~

  + Ensures the RPATH/RUNPATH for all executables & libraries is set to find our package's libraries at runtime, as well as any 3rd party libraries on which they depend, without needing to modify `LD_LIBRARY_PATH`
  + `CMAKE_INSTALL_PKGLIBDIR` must be set beforehand: it should contain the path (relative to `CMAKE_INSTALL_PREFIX`) which contains our package's libraries (e.g. `lib` or `lib/packageName`)

## InstallConfigPackage, InstallConfigPackageVersion

Usage:
~~~{.cmake}
include(InstallConfigPackage)
~~~
or 
~~~{.cmake}
include(InstallConfigPackageVersion)
~~~

  + Typically used at the end of the top level CMakeLists.txt in order to generate and install the `projectNameConfig.cmake` file which will allow others to find and use our package with a `find_package(projectName)` command
  + use `InstallConfigPackageVersion` if your project has a version number, otherwise use `InstallConfigPackage`
  + The project's exported targets will be written in a projectName-targets.cmake file, make sure that:
    - when exporting targets, use `${PROJECT_NAME}Exports` as the name of the target set;
    - if required, set `PROJECT_TARGETS_NAMESPACE` to the name that will be prefixed to targets (as `${PROJECT_TARGETS_NAMESPACE}::[target]`). By default, `${PROJECT_NAME}` will be used, unless the variable NO_PROJECT_TARGETS_NAMESPACE is set;;
    - if required, set `PROJECT_CONFIG_TEMPLATE_FILE` to contain the path to the template Config file (see projectConfig.cmake.in below). By default, we look for a file `${PROJECT_SOURCE_DIR}/${PROJECT_NAME}Config.cmake.in`;
  + set `CMAKE_INSTALL_PKGCONFIGDIR` beforehand to the relative path where all Config files will be installed (typicaly, `lib/cmake/projectName`)
  + any calls to `find_dependency` generated by `FindPackageAddDependency` (see https://gitlab.in2p3.fr/jdfcode/cmake/findpackageadddependency.git) will be automatically added to the Config script, as long as the template file contains the necessary code (see projectConfig.cmake.in below).
  + for any subprojects added with `add_subproject` (see https://gitlab.in2p3.fr/jdfcode/cmake/addsubproject) the list of subproject RPATH additions is retrieved and will be automatically added to the Config script, as long as the template file contains the necessary code (see projectConfig.cmake.in below).

## projectConfig.cmake.in

Template file to be used with `configure_package_config_file()` or with `InstallConfigPackage[Version]` (see above).

Some manual intervention may be required depending on the project, but it should work (minimally) as is.
  + any calls to `find_dependency` generated by `FindPackageAddDependency` (see https://gitlab.in2p3.fr/jdfcode/cmake/findpackageadddependency.git) will be automatically added to the Config script
  + all subprojects added with `add_subproject` (see https://gitlab.in2p3.fr/jdfcode/cmake/addsubproject) will have the paths to their libraries added to the install RPATH
  + any library or executable linking to our targets gets a full RPATH to allow it to find all the required shared libs

## GNUPackageInstallDirs
Just an extension of GNUInstallDirs which adds project-specific directories for installing
libraries, header files, data files and CMake package config files. The result depends on
whether the current project is being built as a top-level project or a subproject of
another:

~~~{.cmake}
if(PROJECT_IS_TOP_LEVEL)
#-- if this a stand-alone installation, define our installataion layout
   include(GNUInstallDirs)
   set(CMAKE_INSTALL_PKGINCDIR ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME})
   set(CMAKE_INSTALL_PKGLIBDIR ${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME})
   set(CMAKE_INSTALL_PKGCONFIGDIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME})
   set(CMAKE_INSTALL_PKGDATADIR ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME})
else()
#-- when built as a subproject, we define our installation relative to the main project
   set(CMAKE_INSTALL_PKGINCDIR ${CMAKE_INSTALL_PKGINCDIR}/${PROJECT_NAME})
   set(CMAKE_INSTALL_PKGLIBDIR ${CMAKE_INSTALL_PKGLIBDIR}/${PROJECT_NAME})
   set(CMAKE_INSTALL_PKGCONFIGDIR ${CMAKE_INSTALL_PKGCONFIGDIR}/${PROJECT_NAME})
   set(CMAKE_INSTALL_PKGDATADIR ${CMAKE_INSTALL_PKGDATADIR}/${PROJECT_NAME})
endif()
~~~

PROJECT_NAME must be set before calling. By default, the project name is converted to all lower case to be used in the directory names, unless variable DO_NOT_LOWER_CASE_PROJECT_INSTALL_DIRS is set.

If being used as a subproject, the master project must have defined the CMAKE_INSTALL_* variables
(e.g. by itself calling `include(GNUPackageInstallDirs)`)