From 967b32e6f8487b2aa4d9937a9938196534b69ef1 Mon Sep 17 00:00:00 2001 From: "Roscoe A. Bartlett" Date: Thu, 14 Sep 2023 07:34:37 -0600 Subject: [PATCH] Add HowTos for TriBITS-compliant raw CMake packages (#582) Several things were done here: * Added new section "How to implement a TriBITS-compliant internal package using raw CMake" * Added new section "How to implement a TriBITS-compliant external package using raw CMake" * Added subsection on example project TribitsExampleProject2 under the "Example TriBITS Projects" section. * Added generation of reduced versions of the package1/CMakeLists.raw.cmake file for different cases. (But will only trigger a re-make if the generated files change.) * Added make dependencies on generated *.cmake files * generate-guide.sh: Added time to 'make' command and discard STDOUT for 'cd -' command (makes output look better) --- tribits/doc/guides/.gitignore | 2 + .../guides/Makefile.common_generated_files | 1 + tribits/doc/guides/TribitsGuidesBody.rst | 180 ++++++++++++++++++ tribits/doc/guides/generate-guide.sh | 19 +- 4 files changed, 200 insertions(+), 2 deletions(-) diff --git a/tribits/doc/guides/.gitignore b/tribits/doc/guides/.gitignore index 7f018678f..266b34812 100644 --- a/tribits/doc/guides/.gitignore +++ b/tribits/doc/guides/.gitignore @@ -4,6 +4,8 @@ /TriBITS.README.DIRECTORY_CONTENTS.rst.tmp /TribitsCommonTPLsList.txt /TribitsCommonTPLsList.txt.tmp +/TribitsExampleProject2_Package1_CMakeLists.raw.external.cmake* +/TribitsExampleProject2_Package1_CMakeLists.raw.internal.cmake* /TribitsGitVersion.txt /TribitsGitVersion.txt.tmp /TribitsHelloWorldDirAndFiles.txt diff --git a/tribits/doc/guides/Makefile.common_generated_files b/tribits/doc/guides/Makefile.common_generated_files index c6a352d12..6984ee3c6 100644 --- a/tribits/doc/guides/Makefile.common_generated_files +++ b/tribits/doc/guides/Makefile.common_generated_files @@ -27,6 +27,7 @@ COMMON_DEPENDENT_FILES = \ ../get-tribits-packages-from-files-list.txt \ ../install_devtools-help.txt \ ../TriBITS.README.DIRECTORY_CONTENTS.rst \ + $(wildcard ../*.cmake) \ TribitsMacroFunctionDoc.rst \ UtilsMacroFunctionDoc.rst diff --git a/tribits/doc/guides/TribitsGuidesBody.rst b/tribits/doc/guides/TribitsGuidesBody.rst index dbabbce0e..3a5e71106 100644 --- a/tribits/doc/guides/TribitsGuidesBody.rst +++ b/tribits/doc/guides/TribitsGuidesBody.rst @@ -2970,6 +2970,28 @@ should be copied from this example project as they represent best practice when using TriBITS for the typical use cases. +TribitsExampleProject2 +---------------------- + +``TribitsExampleProject2`` in an example `TriBITS Project`_ and `TriBITS +Repository`_ contained in the TriBITS source tree under:: + + tribits/examples/TribitsExampleProject2/ + +This example TriBITS project provides some examples for a few other features +and testing scenarios. It contains three internal packages ``Package1``, +``Package2``, and ``Package3`` as shown in its ``PackagesList.cmake`` file: + +.. include:: ../../examples/TribitsExampleProject2/PackagesList.cmake + :literal: + +and supports four external packages/TPLs ``Tpl1``, ``Tpl2``, ``Tpl3``, and +``Tpl4`` as shown in its ``TPLsList.cmake`` file: + +.. include:: ../../examples/TribitsExampleProject2/TPLsList.cmake + :literal: + + MockTrilinos ------------- @@ -6240,6 +6262,164 @@ file as well. Then every ``CMakeLists.txt`` file in subdirectories just calls ``include_tribits_build()``. That is it. +How to implement a TriBITS-compliant internal package using raw CMake +--------------------------------------------------------------------- + +As described in `TriBITS-Compliant Internal Packages`_, it is possible to +create a raw CMake build system for a CMake package that can build under a +parent TriBITS CMake project. The raw CMake code for such a package must +provide the ``::all_libs`` target both in the current CMake build +system and also in the generated ``Config.cmake`` file for the build +directory and in the installed ``Config.cmake`` file. Every such +TriBITS-compliant internal package therefore is also capable of installing a +TriBITS-compliant external package ``Config.cmake`` file. + +.. ToDo: Consider listing out the key features of a raw CMake build system + that is needed for a TriBITS-compliant internal package. + +A raw CMake build system for a TriBITS-compliant internal package is +demonstrated in the `TribitsExampleProject2`_ project ``Package1`` package. +The base ``CMakeLists.txt`` file for building ``Package1`` with a raw CMake +build system (called ``CMakeLists.raw.cmake`` in that directory) looks like: + +.. include:: TribitsExampleProject2_Package1_CMakeLists.raw.internal.cmake + :literal: + +As shown above, this simple CMake package contains the basic features of any +CMake project/package including calling the ``cmake_minimum_required()`` and +``project()`` commands as well as including ``GNUInstallDirs``. In this +example, the project/package being built ``Package1`` has a dependency on a +external package ``Tpl1`` pulled in with ``find_package(Tpl1)``. Also in this +example, the package has native tests it defines with ``include(CTest)`` and +``add_subdirectory()`` (if ``Package1_ENABLE_TESTS`` is set to ``ON``). + +The file ``package1/src/CMakeLists.raw.cmake`` (which gets included from +``package1/src/CMakeLists.txt``) creates a library and executable for the +package and has the contents: + +.. include:: ../../examples/TribitsExampleProject2/packages/package1/src/CMakeLists.raw.cmake + :literal: + +This creates a single installable library target ``Package1_package1`` which +is aliased as ``Package1::package1`` in the current CMake project and sets up +to create the IMPORTED target ``Package1::package1`` in the generated +``Package1ConfigTarget.cmake`` file, which gets included in the installed +``Package1Config.cmake`` (``Config.cmake``) file (as recommenced in +the book "Professional CMake", see below). In addition, the above code +creates the installable executable ``package1-prg``. + +The ``Package1::all_libs`` (``::all_libs``) target is defined and set +up inside of the included file +``package1/cmake/raw/DefineAllLibsTarget.cmake`` which contains the code: + +.. include:: ../../examples/TribitsExampleProject2/packages/package1/cmake/raw/DefineAllLibsTarget.cmake + :literal: + +The above code contains the ALIAS library target ``Package1::all_libs`` +(``::all_libs``) for the current CMake project as well as sets up for +the IMPORTED target ``Package1::all_libs`` (``::all_libs``) getting +put in the generated ``Package1ConfigTargets.cmake`` file (see below). + +The ``Package1Config.cmake`` (``Config.cmake``) file for the build +directory is generated inside of the included file +``cmake/raw/GeneratePackageConfigFileForBuildDir.cmake`` which has the +contents: + +.. include:: ../../examples/TribitsExampleProject2/packages/package1/cmake/raw/GeneratePackageConfigFileForBuildDir.cmake + :literal: + +The above code uses the ``export(EXPORT ...)`` command to generate the file +``Package1ConfigTargets.cmake`` for the build directory which provides the +IMPORTED targets ``Package1::package1`` and ``Package1::all_libs``. The +command ``configure_file(...)`` generates the ``Package1Config.cmake`` file +that includes it for the build directory +``/cmake_packages/Package1/``. + +Finally, the code for generating and installing the ``Package1Config.cmake`` +file for the install directory ``CMAKE_PREFIX_PATH=`` is specified in +the included file ``cmake/raw/GeneratePackageConfigFileForInstallDir.cmake`` +with the contents: + +.. include:: ../../examples/TribitsExampleProject2/packages/package1/cmake/raw/GeneratePackageConfigFileForInstallDir.cmake + :literal: + +The above uses the command ``install(EXPORT ...)`` to have CMake automatically +generate and install the file ``Package1ConfigTargets.cmake`` in the install +directory ``/libs/cmake/Package1/`` which provides the IMPORTED +targets ``Package1::package1`` and ``Package1::all_libs``. The command +``configure_file()`` is used to generate the file +``Package1Config.install.cmake`` in the build directory from the template file +``Package1Config.cmake.in``. Finally, the ``install()`` command is used in +the file ``GeneratePackageConfigFileForInstallDir.cmake`` to set up the +installation of the ``Package1Config.cmake`` file. + +Note, the template file ``package1/cmake/raw/Package1Config.cmake.in`` (which +is unique to ``Package1``) is: + +.. include:: ../../examples/TribitsExampleProject2/packages/package1/cmake/raw/Package1Config.cmake.in + :literal: + +As shown in the all of the above code, there is a lot of boilerplate CMake +code needed to correctly define the targets such that they get put into the +installed ``Package1Config.cmake`` file using the correct namespace +``Package1::`` and care must be taken to ensure that a consistent "export set" +is used for this purpose. (For more details, see the book "Professional +CMake".) + +**NOTE:** One should compare the above raw CMakeLists files to the more +compact TriBITS versions for the base ``CMakeLists.txt`` file (called +``CMakeLists.tribits.cmake`` in the base directory ``pacakge1/``): + +.. include:: ../../examples/TribitsExampleProject2/packages/package1/CMakeLists.tribits.cmake + :literal: + +and the TriBITS ``CMakeLists.txt`` file (called ``CMakeLists.tribits.cmake``) +in the subdirectory ``package1/src/``: + +.. include:: ../../examples/TribitsExampleProject2/packages/package1/src/CMakeLists.tribits.cmake + :literal: + +This shows the amount of boilerplate code that TriBITS addresses automatically +(which reduces the overhead of finer-grained packages). + + +How to implement a TriBITS-compliant external package using raw CMake +--------------------------------------------------------------------- + +As described in `TriBITS-Compliant External Packages`_, it is possible to +create a raw CMake build system for a CMake package such that once it is +installed, satisfies the requirements for a TriBITS-compliant external +package. These installed packages provide a ``Config.cmake`` file +that provides the required targets and behaviors. For most existing raw CMake +projects that already produce a "Professional CMake" compliant +``Config.cmake`` file, that usually just means adding the IMPORTED +target called ``::all_libs`` to the installed +``Config.cmake`` file. + +A raw CMake build system for a TriBITS-compliant external package is +demonstrated in the `TribitsExampleProject2`_ project ``Package1`` package. +The base ``CMakeLists.txt`` file for building ``Package1`` with a raw CMake +build system (called ``CMakeLists.raw.cmake`` in that directory) for +implementing a TriBITS-compliant internal package (which also, by definition, +creates a TriBITS-compliant external package) looks like: + +.. include:: TribitsExampleProject2_Package1_CMakeLists.raw.external.cmake + :literal: + +Note that the raw build system this example is identical to the build system +for the raw TriBITS-compliant internal package described above. The only +differences are: + +1) The ``Package1Config.cmake`` (``Config.cmake``) file does **not** + need to be generated for the build directory and therefore the code in + ``cmake/raw/GeneratePackageConfigFileForBuildDir.cmake`` does **not** need + to be included. + +2) The ALIAS library target ``Package1::all_libs`` (``::all_libs``) + does **not** need to be generated (but should be to be "Professional CMake" + compliant). + + How to check for and tweak TriBITS "ENABLE" cache variables ----------------------------------------------------------- diff --git a/tribits/doc/guides/generate-guide.sh b/tribits/doc/guides/generate-guide.sh index e8e4ae07d..179fb9334 100755 --- a/tribits/doc/guides/generate-guide.sh +++ b/tribits/doc/guides/generate-guide.sh @@ -152,6 +152,21 @@ function tribits_extract_other_doc { &> TribitsHelloWorldDirAndFiles.txt.tmp update_if_different TribitsHelloWorldDirAndFiles.txt tmp + echo + echo "Generating TribitsExampleProject2/Package1 CMakeList file variants ..." + echo + + cat ../../examples/TribitsExampleProject2/packages/package1/CMakeLists.raw.cmake \ + | grep -v EnableTribitsTestSupport \ + | grep -v GeneratePackageConfigFileForBuildDir \ + &> TribitsExampleProject2_Package1_CMakeLists.raw.external.cmake.tmp + update_if_different TribitsExampleProject2_Package1_CMakeLists.raw.external.cmake tmp + + cat ../../examples/TribitsExampleProject2/packages/package1/CMakeLists.raw.cmake \ + | grep -v EnableTribitsTestSupport \ + &> TribitsExampleProject2_Package1_CMakeLists.raw.internal.cmake.tmp + update_if_different TribitsExampleProject2_Package1_CMakeLists.raw.internal.cmake tmp + echo echo "Generating output for 'checkin-test.py --help' ..." echo @@ -246,8 +261,8 @@ function make_final_doc_in_subdir { if [[ "${skip_final_generation}" == "0" ]] ; then cd $dir_name echo $PWD - make - cd - + time make + cd - > /dev/null else echo echo "Skipping final generation of '${dir_name}' on request!"