File: Custom%20Commands%20and%20Generated%20Files.rst

package info (click to toggle)
cmake 4.2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 152,348 kB
  • sloc: ansic: 403,894; cpp: 303,807; sh: 4,097; python: 3,582; yacc: 3,106; lex: 1,279; f90: 538; asm: 471; lisp: 375; cs: 270; java: 266; fortran: 239; objc: 215; perl: 213; xml: 198; makefile: 108; javascript: 83; pascal: 63; tcl: 55; php: 25; ruby: 22
file content (279 lines) | stat: -rw-r--r-- 8,956 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
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>