mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-22 07:25:02 +08:00

This is a full re-write of the CMake Tutorial for CMake 3.23, both the functionality it provides, as well as the modern workflows that developers use when interfacing with CMake. Issue: #22663, #23086, #23799, #26053, #26105, #26153, #26914
280 lines
8.7 KiB
ReStructuredText
280 lines
8.7 KiB
ReStructuredText
Step 7: Custom Commands and Generated Files
|
|
===========================================
|
|
|
|
Code generation is a ubiquitous mechanism for extending programming languages
|
|
beyond the bounds of their language model. CMake has first-class support for
|
|
Qt's Meta-Object Compiler, but very few other code generators are notable
|
|
enough to warrant that kind of effort.
|
|
|
|
Instead, code generators tend to be bespoke and usage specific. CMake provides
|
|
facilities for describing the usage of a code generator, so projects can
|
|
add support for their individual needs.
|
|
|
|
In this step, we will use :command:`add_custom_command` to add support for a
|
|
code generator within the tutorial project.
|
|
|
|
Background
|
|
^^^^^^^^^^
|
|
|
|
Any step in the build process can generally be described in terms of its inputs
|
|
and outputs. CMake assumes that code generators and other custom processes
|
|
operate on the same principle. In this way, the code generator acts identically
|
|
to compilers, linkers, and other elements of the toolchain; when the inputs are
|
|
newer than the outputs (or the outputs don't exist), a user-specified command
|
|
will be run to update the outputs.
|
|
|
|
.. note::
|
|
This model assumes the outputs of a process are known before it is run. CMake
|
|
lacks the ability to describe code generators where the name and location of
|
|
the outputs depends on the *content* of the input. Various hacks exist to
|
|
shim this functionality into CMake, but they are outside the scope of this
|
|
tutorial.
|
|
|
|
Describing a code generator (or any custom process) is usually performed in
|
|
two parts. First, the inputs and outputs are described independently of the
|
|
CMake target model, concerned only with the generation process itself. Second,
|
|
the outputs are associated with a CMake target to insert them into the CMake
|
|
target model.
|
|
|
|
For sources, this is as simple as adding the generated files to the source list
|
|
of a ``STATIC``, ``SHARED``, or ``OBJECT`` library. For header-only generators,
|
|
it's often necessary to use an intermediary target created via
|
|
:command:`add_custom_target` to add the header file generation to the
|
|
build stage (because ``INTERFACE`` libraries have no build step).
|
|
|
|
Exercise 1 - Using a Code Generator
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
|
|
|
The primary mechanism for describing a code generator is the
|
|
:command:`add_custom_command` command. A "command", for the purpose of
|
|
:command:`add_custom_command` is either an executable available in the build
|
|
environment or a CMake executable target name.
|
|
|
|
.. code-block:: cmake
|
|
|
|
add_executable(Tool)
|
|
# ...
|
|
add_custom_command(
|
|
OUTPUT Generated.cxx
|
|
COMMAND Tool -i input.txt -o Generated.cxx
|
|
DEPENDS Tool input.txt
|
|
VERBATIM
|
|
)
|
|
# ...
|
|
add_library(GeneratedObject OBJECT)
|
|
target_sources(GeneratedObject
|
|
PRIVATE
|
|
Generated.cxx
|
|
)
|
|
|
|
Most of the keywords are self-explanatory, with the exception of ``VERBATIM``.
|
|
This argument is effectively mandatory for legacy reasons that are uninteresting
|
|
to explain in a modern context. The curious should consult the
|
|
:command:`add_custom_command` documentation for additional details.
|
|
|
|
The ``Tool`` executable target appears both in the ``COMMAND`` and ``DEPENDS``
|
|
parameters. While ``COMMAND`` is sufficient for the code to build correctly,
|
|
adding the ``Tool`` itself as a dependency of the custom command ensure that
|
|
if ``Tool`` is updated, the custom command will be rerun.
|
|
|
|
For header-only file generation, additional commands are necessary because the
|
|
library itself has no build step. We can use :command:`add_custom_target` to
|
|
create an "artificial" build step for the library. We then force the custom
|
|
target to be run before any targets which link the library with the command
|
|
:command:`add_dependencies`.
|
|
|
|
.. code-block:: cmake
|
|
|
|
add_custom_target(RunGenerator DEPENDS Generated.h)
|
|
|
|
add_library(GeneratedLib INTERFACE)
|
|
target_sources(GeneratedLib
|
|
INTERFACE
|
|
FILE_SET HEADERS
|
|
BASE_DIRS
|
|
${CMAKE_CURRENT_BINARY_DIR}
|
|
FILES
|
|
${CMAKE_CURRENT_BINARY_DIR}/Generated.h
|
|
)
|
|
|
|
add_dependencies(GeneratedLib RunGenerator)
|
|
|
|
.. note::
|
|
We add the :variable:`CMAKE_CURRENT_BINARY_DIR`, a variable which names the
|
|
current location in the build tree where our artifacts are being placed, to
|
|
the base directories because that's the working directory our code generator
|
|
will be run inside of. Listing the ``FILES`` is unnecessary for the build and
|
|
done so here only for clarity.
|
|
|
|
Goal
|
|
----
|
|
|
|
Add a generated table of pre-computed square roots to the ``MathFunctions``
|
|
library.
|
|
|
|
Helpful Resources
|
|
-----------------
|
|
|
|
* :command:`add_executable`
|
|
* :command:`add_library`
|
|
* :command:`target_sources`
|
|
* :command:`add_custom_command`
|
|
* :command:`add_custom_target`
|
|
* :command:`add_dependencies`
|
|
|
|
Files to Edit
|
|
-------------
|
|
|
|
* ``MathFunctions/CMakeLists.txt``
|
|
* ``MathFunctions/MakeTable/CMakeLists.txt``
|
|
* ``MathFunctions/MathFunctions.cxx``
|
|
|
|
Getting Started
|
|
---------------
|
|
|
|
The ``MathFunctions`` library has been edited to use a pre-computed table when
|
|
given a number less than 10. However, the hardcoded table is not particularly
|
|
accurate, containing only the nearest truncated integer value.
|
|
|
|
The ``MakeTable.cxx`` source file describes a program which will generate a
|
|
better table. It takes a single argument as input, the file name of the table
|
|
to be generated.
|
|
|
|
Complete ``TODO 1`` through ``TODO 10``.
|
|
|
|
Build and Run
|
|
-------------
|
|
|
|
No special configuration is needed, configure and build as usual. Note that
|
|
the ``MakeTable`` executable is sequenced before ``MathFunctions``.
|
|
|
|
.. code-block:: console
|
|
|
|
cmake --preset tutorial
|
|
cmake --build build
|
|
|
|
Verify the output of ``Tutorial`` now uses the pre-computed table for values
|
|
less than 10.
|
|
|
|
Solution
|
|
--------
|
|
|
|
First we add a new executable to generate the tables, adding the
|
|
``MakeTable.cxx`` file as a source.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 1-2: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step8/MathFunctions/MakeTable/CMakeLists.txt
|
|
:caption: TODO 1-2: MathFunctions/MakeTable/CMakeLists.txt
|
|
:name: MathFunctions/MakeTable/CMakeLists.txt-add_executable
|
|
:language: cmake
|
|
:start-at: add_executable
|
|
:end-at: MakeTable.cxx
|
|
:append: )
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Then we add a custom command which produces the table, and custom target which
|
|
depends on the table.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 3-4: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step8/MathFunctions/MakeTable/CMakeLists.txt
|
|
:caption: TODO 3-4: MathFunctions/MakeTable/CMakeLists.txt
|
|
:name: MathFunctions/MakeTable/CMakeLists.txt-add_custom_command
|
|
:language: cmake
|
|
:start-at: add_custom_command
|
|
:end-at: add_custom_target
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
We need to add an interface library which describes the output which will
|
|
appear in :variable:`CMAKE_CURRENT_BINARY_DIR`. The ``FILES`` parameter is
|
|
optional.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 5-6: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step8/MathFunctions/MakeTable/CMakeLists.txt
|
|
:caption: TODO 5-6: MathFunctions/MakeTable/CMakeLists.txt
|
|
:name: MathFunctions/MakeTable/CMakeLists.txt-add_library
|
|
:language: cmake
|
|
:start-at: add_library
|
|
:end-at: SqrtTable.h
|
|
:append: )
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Now that all the targets are described, we can force the custom target to run
|
|
before any dependents of the interface library by associating them with
|
|
:command:`add_dependencies`.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 7: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step8/MathFunctions/MakeTable/CMakeLists.txt
|
|
:caption: TODO 7: MathFunctions/MakeTable/CMakeLists.txt
|
|
:name: MathFunctions/MakeTable/CMakeLists.txt-add_dependencies
|
|
:language: cmake
|
|
:start-at: add_dependencies
|
|
:end-at: add_dependencies
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
We are ready to add the interface library to the linked libraries of
|
|
``MathFunctions``, and add the entire ``MakeTable`` folder to the project.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 8-9: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step8/MathFunctions/CMakeLists.txt
|
|
:caption: TODO 8: MathFunctions/CMakeLists.txt
|
|
:name: MathFunctions/CMakeLists.txt-link-sqrttable
|
|
:language: cmake
|
|
:start-at: target_link_libraries(MathFunctions
|
|
:end-at: )
|
|
|
|
.. literalinclude:: Step8/MathFunctions/CMakeLists.txt
|
|
:caption: TODO 9: MathFunctions/CMakeLists.txt
|
|
:name: MathFunctions/CMakeLists.txt-add-maketable
|
|
:language: cmake
|
|
:start-at: add_subdirectory(MakeTable
|
|
:end-at: add_subdirectory(MakeTable
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|
|
|
|
Finally, we update the ``MathFunctions`` library itself to take advantage of
|
|
the generated table.
|
|
|
|
.. raw:: html
|
|
|
|
<details><summary>TODO 10: Click to show/hide answer</summary>
|
|
|
|
.. literalinclude:: Step8/MathFunctions/MathFunctions.cxx
|
|
:caption: TODO 10: MathFunctions/MathFunctions.cxx
|
|
:name: MathFunctions/MathFunctions.cxx-include-sqrttable
|
|
:language: c++
|
|
:start-at: #include <SqrtTable.h>
|
|
:end-at: {
|
|
|
|
.. raw:: html
|
|
|
|
</details>
|