aboutsummaryrefslogtreecommitdiff
path: root/clang-tools-extra/docs/modularize.rst
blob: 9dde467ec30847dcd748a60a0ee88d6090f86df1 (plain)
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
.. index:: modularize

==================================
Modularize User's Manual
==================================

.. toctree::
   :hidden:

   ModularizeUsage

:program:`modularize` is a standalone tool that checks whether a set of headers
provides the consistent definitions required to use modules. For example, it
detects whether the same entity (say, a NULL macro or size_t typedef) is
defined in multiple headers or whether a header produces different definitions
under different circumstances. These conditions cause modules built from the
headers to behave poorly, and should be fixed before introducing a module
map.

:program:`modularize` also has an assistant mode option for generating
a module map file based on the provided header list. The generated file
is a functional module map that can be used as a starting point for a
module.map file.

Getting Started
===============

To build from source:

1. Read `Getting Started with the LLVM System`_ and `Clang Tools
   Documentation`_ for information on getting sources for LLVM, Clang, and
   Clang Extra Tools.

2. `Getting Started with the LLVM System`_ and `Building LLVM with CMake`_ give
   directions for how to build. With sources all checked out into the
   right place the LLVM build will build Clang Extra Tools and their
   dependencies automatically.

   * If using CMake, you can also use the ``modularize`` target to build
     just the modularize tool and its dependencies.

Before continuing, take a look at :doc:`ModularizeUsage` to see how to invoke
modularize.

.. _Getting Started with the LLVM System: https://llvm.org/docs/GettingStarted.html
.. _Building LLVM with CMake: https://llvm.org/docs/CMake.html
.. _Clang Tools Documentation: https://clang.llvm.org/docs/ClangTools.html

What Modularize Checks
======================

Modularize will check for the following:

* Duplicate global type and variable definitions
* Duplicate macro definitions
* Macro instances, 'defined(macro)', or #if, #elif, #ifdef, #ifndef conditions
  that evaluate differently in a header
* #include directives inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks
* Module map header coverage completeness (in the case of a module map input
  only)

Modularize will do normal C/C++ parsing, reporting normal errors and warnings,
but will also report special error messages like the following::

  error: '(symbol)' defined at multiple locations:
     (file):(row):(column)
     (file):(row):(column)

  error: header '(file)' has different contents depending on how it was included

The latter might be followed by messages like the following::

  note: '(symbol)' in (file) at (row):(column) not always provided

Checks will also be performed for macro expansions, defined(macro)
expressions, and preprocessor conditional directives that evaluate
inconsistently, and can produce error messages like the following::

   (...)/SubHeader.h:11:5:
  #if SYMBOL == 1
      ^
  error: Macro instance 'SYMBOL' has different values in this header,
         depending on how it was included.
    'SYMBOL' expanded to: '1' with respect to these inclusion paths:
      (...)/Header1.h
        (...)/SubHeader.h
  (...)/SubHeader.h:3:9:
  #define SYMBOL 1
          ^
  Macro defined here.
    'SYMBOL' expanded to: '2' with respect to these inclusion paths:
      (...)/Header2.h
          (...)/SubHeader.h
  (...)/SubHeader.h:7:9:
  #define SYMBOL 2
          ^
  Macro defined here.

Checks will also be performed for '#include' directives that are
nested inside 'extern "C/C++" {}' or 'namespace (name) {}' blocks,
and can produce error message like the following::

  IncludeInExtern.h:2:3:
  #include "Empty.h"
  ^
  error: Include directive within extern "C" {}.
  IncludeInExtern.h:1:1:
  extern "C" {
  ^
  The "extern "C" {}" block is here.

.. _module-map-coverage:

Module Map Coverage Check
=========================

The coverage check uses the Clang library to read and parse the
module map file. Starting at the module map file directory, or just the
include paths, if specified, it will collect the names of all the files it
considers headers (no extension, .h, or .inc--if you need more, modify the
isHeader function). It then compares the headers against those referenced
in the module map, either explicitly named, or implicitly named via an
umbrella directory or umbrella file, as parsed by the ModuleMap object.
If headers are found which are not referenced or covered by an umbrella
directory or file, warning messages will be produced, and this program
will return an error code of 1. If no problems are found, an error code of
0 is returned.

Note that in the case of umbrella headers, this tool invokes the compiler
to preprocess the file, and uses a callback to collect the header files
included by the umbrella header or any of its nested includes. If any
front end options are needed for these compiler invocations, these
can be included on the command line after the module map file argument.

Warning message have the form:

  warning: module.modulemap does not account for file: Level3A.h

Note that for the case of the module map referencing a file that does
not exist, the module map parser in Clang will (at the time of this
writing) display an error message.

To limit the checks :program:`modularize` does to just the module
map coverage check, use the ``-coverage-check-only option``.

For example::

  modularize -coverage-check-only module.modulemap

.. _module-map-generation:

Module Map Generation
=====================

If you specify the ``-module-map-path=<module map file>``,
:program:`modularize` will output a module map based on the input header list.
A module will be created for each header. Also, if the header in the header
list is a partial path, a nested module hierarchy will be created in which a
module will be created for each subdirectory component in the header path,
with the header itself represented by the innermost module. If other headers
use the same subdirectories, they will be enclosed in these same modules also.

For example, for the header list::

  SomeTypes.h
  SomeDecls.h
  SubModule1/Header1.h
  SubModule1/Header2.h
  SubModule2/Header3.h
  SubModule2/Header4.h
  SubModule2.h

The following module map will be generated::

  // Output/NoProblemsAssistant.txt
  // Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \
       -root-module=Root NoProblemsAssistant.modularize

  module SomeTypes {
    header "SomeTypes.h"
    export *
  }
  module SomeDecls {
    header "SomeDecls.h"
    export *
  }
  module SubModule1 {
    module Header1 {
      header "SubModule1/Header1.h"
      export *
    }
    module Header2 {
      header "SubModule1/Header2.h"
      export *
    }
  }
  module SubModule2 {
    module Header3 {
      header "SubModule2/Header3.h"
      export *
    }
    module Header4 {
      header "SubModule2/Header4.h"
      export *
    }
    header "SubModule2.h"
    export *
  }

An optional ``-root-module=<root-name>`` option can be used to cause a root module
to be created which encloses all the modules.

An optional ``-problem-files-list=<problem-file-name>`` can be used to input
a list of files to be excluded, perhaps as a temporary stop-gap measure until
problem headers can be fixed.

For example, with the same header list from above::

  // Output/NoProblemsAssistant.txt
  // Generated by: modularize -module-map-path=Output/NoProblemsAssistant.txt \
       -root-module=Root NoProblemsAssistant.modularize

  module Root {
    module SomeTypes {
      header "SomeTypes.h"
      export *
    }
    module SomeDecls {
      header "SomeDecls.h"
      export *
    }
    module SubModule1 {
      module Header1 {
        header "SubModule1/Header1.h"
        export *
      }
      module Header2 {
        header "SubModule1/Header2.h"
        export *
      }
    }
    module SubModule2 {
      module Header3 {
        header "SubModule2/Header3.h"
        export *
      }
      module Header4 {
        header "SubModule2/Header4.h"
        export *
      }
      header "SubModule2.h"
      export *
    }
  }

Note that headers with dependents will be ignored with a warning, as the
Clang module mechanism doesn't support headers the rely on other headers
to be included first.

The module map format defines some keywords which can't be used in module
names. If a header has one of these names, an underscore ('_') will be
prepended to the name. For example, if the header name is ``header.h``,
because ``header`` is a keyword, the module name will be ``_header``.
For a list of the module map keywords, please see:
`Lexical structure <https://clang.llvm.org/docs/Modules.html#lexical-structure>`_