| 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> |