123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596 |
- Step 9: Installation Commands and Concepts
- ==========================================
- Projects need to do more than build and test their code, they need to make it
- available to consumers. The layout of files in the build tree is unsuitable
- for consumption by other projects, binaries are in unexpected places, header
- files are located far away in the source tree, and there's no clear way
- to discover what targets are provided or how to use them.
- This translation, moving artifacts from the source and build trees into a final
- layout suitable for consumption, is known as installation. CMake supports a
- complete installation workflow as part of the project description, controlling
- both the layout of artifacts in the install tree, and reconstructing targets
- for other CMake projects which want to consume the libraries provided by the
- install tree.
- Background
- ^^^^^^^^^^
- All CMake installation goes through a single command, :command:`install`, which
- is split into many subcommands responsible for various aspects of the
- installation process. For target-based CMake workflows, it is mostly sufficient
- to rely on installing targets themselves with :command:`install(TARGETS)`
- instead of resorting to manually moving files with :command:`install(FILES)`
- or :command:`install(DIRECTORY)`.
- .. note::
- This is why we need to add ``FILES`` to header sets which are intended to be
- installed. CMake needs to be able to locate the files when their associated
- target is installed.
- CMake divides target-based installation into various artifact kinds. The
- available artifact kinds (in CMake 3.23) are:
- ``ARCHIVE``
- Static libraries (``.a`` / ``.lib``), DLL import libraries (``.lib``), and
- a handful of other "archive-like" objects.
- ``LIBRARY``
- Shared libraries (``.so``), modules, and other dynamically loadable
- objects. **Not** Window's DLL files (``.dll``) or MacOS frameworks.
- ``RUNTIME``
- Executables of all kinds except MacOS bundles; and Window's DLLs (``.dll``).
- ``OBJECT``
- Objects from ``OBJECT`` libraries.
- ``FRAMEWORK``
- Both static and shared MacOS frameworks
- ``BUNDLE``
- MacOS bundle executables
- ``PUBLIC_HEADER`` / ``PRIVATE_HEADER`` / ``RESOURCE``
- Files described by the :prop_tgt:`PUBLIC_HEADER`, :prop_tgt:`PRIVATE_HEADER`
- and :prop_tgt:`RESOURCE` target properties, typically used with MacOS
- frameworks
- ``FILE_SET <set-name>``
- A file set associated with the target. This is how headers are typically
- installed.
- Most important artifact kinds have known destinations which CMake will default
- to unless instructed to do otherwise. For example, ``RUNTIME`` will be installed
- to the location named by :module:`CMAKE_INSTALL_BINDIR <GNUInstallDirs>`, if
- the variable is available, otherwise they default to ``bin``.
- The full list of artifact kind default destinations is described in the
- following table.
- =============================== =============================== ======================
- Target Type Variable Built-In Default
- =============================== =============================== ======================
- ``RUNTIME`` ``${CMAKE_INSTALL_BINDIR}`` ``bin``
- ``LIBRARY`` ``${CMAKE_INSTALL_LIBDIR}`` ``lib``
- ``ARCHIVE`` ``${CMAKE_INSTALL_LIBDIR}`` ``lib``
- ``PRIVATE_HEADER`` ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include``
- ``PUBLIC_HEADER`` ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include``
- ``FILE_SET`` (type ``HEADERS``) ``${CMAKE_INSTALL_INCLUDEDIR}`` ``include``
- =============================== =============================== ======================
- For the most part, projects should leave the defaults alone unless they need to
- install to a specific subdirectory of a default location.
- CMake does not define the ``CMAKE_INSTALL_<dir>`` variables by default. If a
- project wishes to dictate installing to a subdirectory of one of these
- locations, it is necessary to include the :module:`GNUInstallDirs` module, which
- will provide values for all ``CMAKE_INSTALL_<dir>`` variables that have not
- already been defined.
- Exercise 1 - Installing Artifacts
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- For modern, target-based CMake projects installation of artifacts is trivial
- and consists of a single call to :command:`install(targets)`.
- .. code-block:: cmake
- install(
- TARGETS MyApp MyLib
- FILE_SET HEADERS
- FILE_SET AnotherHeaderFileSet
- )
- Most artifact kinds are installed by default and do not need to be listed in
- the :command:`install` command. However, ``FILE_SET``\ s must be named to let
- CMake know you want to install. In the above example we install two file
- sets, one named ``HEADERS`` and another named ``AnotherHeaderFileSet``.
- When named, an artifact kind can be given various options, such as a destination.
- .. code-block:: cmake
- include(GNUInstallDirs)
- install(
- TARGETS MyApp MyLib
- RUNTIME
- DESTINATION ${CMAKE_INSTALL_BINDIR}/Subfolder
- FILE_SET HEADERS
- )
- This will install the ``MyApp`` target to ``bin/Subfolder`` (if the packager
- hasn't changed :module:`CMAKE_INSTALL_BINDIR <GNUInstallDirs>`).
- Importantly, if the ``OBJECT`` artifact kind is never given a destination, it
- will act like an ``INTERFACE`` library, only installing its headers.
- Goal
- ----
- Install the artifacts for the libraries and executables (except tests) described
- in the tutorial project.
- Helpful Resources
- -----------------
- * :command:`install`
- Files to Edit
- -------------
- * ``CMakeLists.txt``
- Getting Started
- ---------------
- The ``Help/guide/tutorial/Step9`` directory contains the complete, recommended
- solution to ``Step8``. Complete ``TODO 1`` and ``TODO 2``.
- Build and Run
- -------------
- No special configuration is needed, configure and build as usual.
- .. code-block:: console
- cmake --preset tutorial
- cmake --build build
- We can verify the installation is correct with :option:`cmake --install`.
- .. code-block:: console
- cmake --install build --prefix install
- The ``install`` folder should be populated correctly for our artifacts.
- Solution
- --------
- First we add an :command:`install(TARGETS)` for the conditionally built,
- thus conditionally installed, ``Tutorial`` executable.
- .. raw:: html
- <details><summary>TODO 1 Click to show/hide answer</summary>
- .. code-block:: cmake
- :caption: TODO 1: CMakeLists.txt
- :name: CMakeLists.txt-install-tutorial
- if(TUTORIAL_BUILD_UTILITIES)
- add_subdirectory(Tutorial)
- install(
- TARGETS Tutorial
- )
- endif()
- .. raw:: html
- </details>
- Then we can install the rest of the targets.
- .. raw:: html
- <details><summary>TODO 2 Click to show/hide answer</summary>
- .. code-block:: cmake
- :caption: TODO 2: CMakeLists.txt
- :name: CMakeLists.txt-install-libs
- install(
- TARGETS MathFunctions OpAdd OpMul OpSub MathLogger SqrtTable
- FILE_SET HEADERS
- )
- .. raw:: html
- </details>
- .. note::
- We could add :command:`install(TARGETS)` commands locally to each subfolder
- where the targets are defined. This would be typical in very large projects
- where keeping track of all the installable targets is difficult.
- It might seem unnecessary to install the ``SqrtTable`` and ``MathLogger``,
- and it is at this stage. Due to how CMake models target relationships, when we
- reconstruct the target model in the next exercise we will need these targets to
- be available.
- Exercise 2 - Exporting Targets
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- This raw collection of installed files is a good start, but we lose the CMake
- target model. These are effectively no better than the pre-compiled vendored
- libraries we discussed in ``Step 4``. We need some way for other projects to
- reconstruct our targets from what we have provided in the install tree.
- The mechanism CMake provides to solve this is a CMakeLang file known as a
- "target export file". It is created by the :command:`install(EXPORT)`
- command.
- .. code-block:: cmake
- install(
- TARGETS MyApp MyLib
- EXPORT MyProjectTargets
- )
- include(GNUInstallDirs)
- install(
- EXPORT MyProjectTargets
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject
- NAMESPACE MyProject::
- )
- There are several parts to the above example. Firstly the
- :command:`install(TARGETS)` command takes an export name, basically a list to
- add the installed targets to.
- Later, the :command:`install(EXPORT)` command consumes this list of targets
- to generate the target export file. This will be a file named
- ``<ExportName>.cmake`` located in the provided ``DESTINATION``. The
- ``DESTINATION`` provided in this example is the conventional one, but any
- location searched by the :command:`find_package` command is valid.
- Finally, the targets created by the target export file will be prefixed with the
- ``NAMESPACE`` string, ie they will be of the form ``<NAMESPACE><TargetName>``.
- It is conventional for this to be the project name followed by two colons.
- For reasons that will become more obvious in future steps, we typically don't
- consume this file directly. Instead we have a file named
- ``<ProjectName>Config.cmake`` consume it via :command:`include()`.
- .. code-block:: cmake
- include(${CMAKE_CURRENT_LIST_DIR}/MyProjectTargets.cmake)
- .. note::
- The :variable:`CMAKE_CURRENT_LIST_DIR` variable names the directory that the
- currently running CMake Language file is inside of, regardless of how that
- file was included or launched.
- Then this file is installed alongside the target export with
- :command:`install(FILES)`.
- .. code-block:: cmake
- install(
- FILES
- cmake/MyProjectConfig.cmake
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/MyProject
- )
- .. note::
- The name of this file and its location are dictated by the discovery
- semantics of the :command:`find_package` command, which we will discuss more
- in the next step.
- Goal
- ----
- Export the Tutorial project targets so other projects may consume them.
- Helpful Resources
- -----------------
- * :command:`install`
- * :module:`GNUInstallDirs`
- * :variable:`CMAKE_CURRENT_LIST_DIR`
- Files to Edit
- -------------
- * ``CMakeLists.txt``
- * ``cmake/TutorialConfig.cmake``
- Getting Started
- ---------------
- Continue editing the files in the ``Help/guide/tutorial/Step9`` directory.
- Complete ``TODO 3`` through ``TODO 8``.
- Build and Run
- -------------
- The build command is sufficient to reconfigure the project.
- .. code-block:: console
- cmake --build build
- We can verify the installation is correct with :option:`cmake --install`.
- .. note::
- As with CTest, when using multi-config generator, eg Visual Studio, it will be
- necessary to specify a configuration with
- ``cmake --install --config <config> <remaining flags>``, where
- ``<config>`` is a value like ``Debug`` or ``Release``. This is true whenever
- using a multi-config generator, and won't be called out specifically in
- future commands.
- .. code-block:: console
- cmake --install build --prefix install
- .. note::
- CMake won't update files which have not changed, only installing new or
- updated files from the build and source trees.
- The ``install`` folder should be populated correctly for our artifacts and
- export files. We'll demonstrate how to use these files in the next step.
- Solution
- --------
- First we add the ``Tutorial`` target to the ``TutorialTargets`` export.
- .. raw:: html
- <details><summary>TODO 3 Click to show/hide answer</summary>
- .. literalinclude:: Step10/TutorialProject/CMakeLists.txt
- :caption: TODO 3: CMakeLists.txt
- :name: CMakeLists.txt-install-tutorial-export
- :language: cmake
- :start-at: install(
- :end-at: )
- .. raw:: html
- </details>
- Soon we will need access to the ``CMAKE_INSTALL_<dir>`` variables, so next
- we include the :module:`GNUInstallDirs` module.
- .. raw:: html
- <details><summary>TODO 4 Click to show/hide answer</summary>
- .. literalinclude:: Step10/TutorialProject/CMakeLists.txt
- :caption: TODO 4: CMakeLists.txt
- :name: CMakeLists.txt-gnuinstalldirss
- :language: cmake
- :start-at: include(GNUInstallDirs)
- :end-at: include(GNUInstallDirs)
- .. raw:: html
- </details>
- Now we add the rest of our targets to the ``TutorialTargets`` export.
- .. raw:: html
- <details><summary>TODO 5 Click to show/hide answer</summary>
- .. literalinclude:: Step10/TutorialProject/CMakeLists.txt
- :caption: TODO 5: CMakeLists.txt
- :name: CMakeLists.txt-install-libs-export
- :language: cmake
- :start-at: TARGETS MathFunctions
- :end-at: )
- :prepend: install(
- .. raw:: html
- </details>
- Next we install the export itself, to generate our target export file.
- .. raw:: html
- <details><summary>TODO 6 Click to show/hide answer</summary>
- .. code-block:: cmake
- :caption: TODO 6: CMakeLists.txt
- :name: CMakeLists.txt-install-export
- install(
- EXPORT TutorialTargets
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Tutorial
- NAMESPACE Tutorial::
- )
- .. raw:: html
- </details>
- And then we install our "config" file, which we will use to include our target
- export file.
- .. raw:: html
- <details><summary>TODO 7 Click to show/hide answer</summary>
- .. code-block:: cmake
- :caption: TODO 7: CMakeLists.txt
- :name: CMakeLists.txt-install-config
- install(
- FILES
- cmake/TutorialConfig.cmake
- DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Tutorial
- )
- .. raw:: html
- </details>
- Finally we can add the necessary :command:`include` command to the config file.
- .. raw:: html
- <details><summary>TODO 8 Click to show/hide answer</summary>
- .. literalinclude:: Step10/TutorialProject/cmake/TutorialConfig.cmake
- :caption: TODO 8: cmake/TutorialConfig.cmake
- :name: cmake/TutorialConfig.cmake
- :language: cmake
- :start-at: include
- :end-at: include
- .. raw:: html
- </details>
- Exercise 3 - Exporting a Version File
- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- When importing CMake targets from a target export file, there is no way to
- "bail out" or "undo" the operation. If it turns out a package is a wrong or
- incompatible version for the one we requested, we'll be stuck with any
- side-effects incurred while we learned that version information.
- The answer CMake provides for this problem is a light-weight version file which
- only describes this version compatibility information, which can be checked
- before CMake commits to fully importing the file.
- CMake provides helper modules and scripts for generating these version files,
- namely the :module:`CMakePackageConfigHelpers` module.
- .. code-block:: cmake
- include(CMakePackageConfigHelpers)
- write_basic_package_version_file(
- ${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfigVersion.cmake
- COMPATIBILITY ExactVersion
- )
- The available versions are:
- * ``AnyNewerVersion``
- * ``SameMajorVersion``
- * ``SameMinorVersion``
- * ``ExactVersion``
- Additionally packages can mark themselves as ``ARCH_INDEPENDENT``, intended for
- packages which ship no binaries which would tie them to a specific machine
- architecture.
- By default, the ``VERSION`` used by ``write_basic_package_version_file()`` is
- the ``VERSION`` number given to the :command:`project` command.
- Goal
- ----
- Export a version file for the Tutorial project.
- Helpful Resources
- -----------------
- * :command:`project`
- * :command:`install`
- * :module:`CMakePackageConfigHelpers`
- * :variable:`PROJECT_VERSION`
- Files to Edit
- -------------
- * ``CMakeLists.txt``
- Getting Started
- ---------------
- Continue editing the files in the ``Help/guide/tutorial/Step9`` directory.
- Complete ``TODO 9`` through ``TODO 12``.
- Build and Run
- -------------
- Rebuild and install as done previously.
- .. code-block:: console
- cmake --build build
- cmake --install build --prefix install
- The ``install`` folder should be populated correctly with our newly generated
- and installed version file.
- Solution
- --------
- First we add a ``VERSION`` parameter to the :command:`project` command.
- .. raw:: html
- <details><summary>TODO 9 Click to show/hide answer</summary>
- .. literalinclude:: Step10/TutorialProject/CMakeLists.txt
- :caption: TODO 9: CMakeLists.txt
- :name: CMakeLists.txt-project-version
- :language: cmake
- :start-at: project(
- :end-at: )
- .. raw:: html
- </details>
- Next we include the :module:`CMakePackageConfigHelpers` modules and use it
- to generate the config version file.
- .. raw:: html
- <details><summary>TODO 10-11 Click to show/hide answer</summary>
- .. literalinclude:: Step10/TutorialProject/CMakeLists.txt
- :caption: TODO 10-11: CMakeLists.txt
- :name: CMakeLists.txt-write_basic_package_version_file
- :language: cmake
- :start-at: include(CMakePackageConfigHelpers
- :end-at: COMPATIBILITY ExactVersion
- :append: )
- .. raw:: html
- </details>
- Finally we add the config version file to the list of files to be installed.
- .. raw:: html
- <details><summary>TODO 12 Click to show/hide answer</summary>
- .. literalinclude:: Step10/TutorialProject/CMakeLists.txt
- :caption: TODO 12: CMakeLists.txt
- :name: CMakeLists.txt-install-version-config
- :language: cmake
- :start-at: FILES
- :end-at: )
- :prepend: install(
- .. raw:: html
- </details>
|