Skip to content

Commit

Permalink
Add HowTos for TriBITS-compliant raw CMake packages (#582)
Browse files Browse the repository at this point in the history
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)
  • Loading branch information
bartlettroscoe committed Sep 14, 2023
1 parent 617df45 commit eddbfc1
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 2 deletions.
2 changes: 2 additions & 0 deletions tribits/doc/guides/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tribits/doc/guides/Makefile.common_generated_files
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
180 changes: 180 additions & 0 deletions tribits/doc/guides/TribitsGuidesBody.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
-------------

Expand Down Expand Up @@ -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 ``<Package>::all_libs`` target both in the current CMake build
system and also in the generated ``<Package>Config.cmake`` file for the build
directory and in the installed ``<Package>Config.cmake`` file. Every such
TriBITS-compliant internal package therefore is also capable of installing a
TriBITS-compliant external package ``<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`` (``<Package>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`` (``<Package>::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``
(``<Package>::all_libs``) for the current CMake project as well as sets up for
the IMPORTED target ``Package1::all_libs`` (``<Package>::all_libs``) getting
put in the generated ``Package1ConfigTargets.cmake`` file (see below).

The ``Package1Config.cmake`` (``<Package>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
``<buildDir>/cmake_packages/Package1/``.

Finally, the code for generating and installing the ``Package1Config.cmake``
file for the install directory ``CMAKE_PREFIX_PATH=<installDir>`` 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 ``<installDir>/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 ``<Package>Config.cmake`` file
that provides the required targets and behaviors. For most existing raw CMake
projects that already produce a "Professional CMake" compliant
``<Package>Config.cmake`` file, that usually just means adding the IMPORTED
target called ``<Package>::all_libs`` to the installed
``<Package>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`` (``<Package>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`` (``<Package>::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
-----------------------------------------------------------

Expand Down
19 changes: 17 additions & 2 deletions tribits/doc/guides/generate-guide.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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!"
Expand Down

0 comments on commit eddbfc1

Please sign in to comment.