Ubuntu's modularize-20 checks a batch of C or C++ headers for modularisation problems and can draft a starting module.modulemap for you. The command does not rewrite your headers. Allow 15 to 30 minutes for a small header set, plus time to fix any diagnostics it finds.
This guide uses clang-tools-20 version 1:20.1.8~++20250804090239+87f0227cb601-1~exp1~20250804210352.139 and the installed executable reports LLVM 20.1.8. You need readable headers, a shell, and compiler-style arguments that let Clang parse them. The checks are normally unprivileged; use sudo only to read files that your account genuinely cannot access.
Create a plain text file with one header per line. Blank lines and lines beginning with # are ignored. A header can be followed by a colon and space-separated dependency names on the same line. Relative names are resolved relative to the list file unless you supply --prefix.
mkdir -p /tmp/modularize-demo/include
cat > /tmp/modularize-demo/include/widget.h <<'EOF'
#ifndef WIDGET_H
#define WIDGET_H
int widget_value;
#endif
EOF
cat > /tmp/modularize-demo/headers.list <<'EOF'
include/widget.h
EOF
The commands above create disposable test material. For a real project, keep the list beside the source tree or in a build directory that is easy to review.
Run the list with the front-end arguments after it. The -x c++ argument tells Clang how to interpret the header; the tool otherwise assumes C++ for .h files. Pass -I for directories needed while checking includes.
modularize-20 /tmp/modularize-demo/headers.list \
-x c++ -I /tmp/modularize-demo/include
A clean run is quiet and returns status zero. Check the status immediately if you are testing a script:
printf '%s\n' "$?"
0
Checkpoint: If the command prints a compiler diagnostic, fix the include path, language mode, macro definitions, or header itself before moving on. Do not hide a real parse error with --no-coverage-check; that option controls a different check.
For a larger list, add --display-file-lists. The tool prints headers with no detected errors, headers with possible errors, and a combined list. In the combined list, a leading # marks a problem file.
modularize-20 --display-file-lists \
/tmp/modularize-demo/headers.list \
-x c++ -I /tmp/modularize-demo/include
This is a useful triage pass, not a proof that a header is safe in every build configuration. Header behaviour can depend on macros, target options and include order. Preserve the reported list, then repeat the check with the same front-end arguments used by your project.
Use --module-map-path when you want the tool to act as a module-map assistant. It skips the normal modularisation checks and writes a generated file from the header list.
modularize-20 \
--module-map-path=/tmp/modularize-demo/module.modulemap \
--root-module=demo \
/tmp/modularize-demo/headers.list \
-x c++ -I /tmp/modularize-demo/include
sed -n '1,120p' /tmp/modularize-demo/module.modulemap
For the example, expect a module named demo containing a submodule for the listed header and an export * line. The generated file is a starting point: inspect its paths, module names, visibility and dependencies before putting it into a build.
Warning: --module-map-path overwrites the named output. Choose a new path while experimenting, or make a backup first:
cp --preserve=all /path/to/module.modulemap /path/to/module.modulemap.bak
modularize-20 --module-map-path=/path/to/module.modulemap /path/to/headers.list \
-x c++
If the generated result is wrong, restore the backup with mv. Do not delete the backup until the replacement has been reviewed.
If the header list contains a module map, the normal run performs a coverage check using the include paths supplied with -I. To run only that part, use --coverage-check-only:
modularize-20 --coverage-check-only \
/path/to/headers.list -I /path/to/include -x c++
Use --no-coverage-check only when you deliberately want to skip coverage. These switches do not repair a module map and do not change compiler include paths.
-I path is wrong. Test the exact file with test -r /path/to/header.h before changing the module map.-x c; headers using project macros may need matching -DNAME=value arguments. Copy the relevant arguments from the real compile command.--display-file-lists flagged has been looked at, not ignored.