Generate GLib Enum Types Safely with glib-mkenums
You will finish with a repeatable command that reads C enum definitions and emits generated text, plus a template you can put into a build. This is useful for generating GObject enum and flags registration code without hand-maintaining the repetitive parts.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need the libglib2.0-dev-bin package, a readable C header containing enums, and a writable build directory. The examples use glib-mkenums 2.86.4 as installed on this machine. The package database reports libglib2.0-dev-bin 2.80.0-6ubuntu3.9, so check your executable and package separately when diagnosing a different host.
This tool reads source text and writes generated text. It does not need root privileges. Do not run it with sudo just because the generated file will later be compiled.
1. Check the executable and version
Start with a read-only check. It confirms which binary is first in your path and shows the option names supported by this installation:
$ command -v glib-mkenums
/usr/bin/glib-mkenums
$ glib-mkenums --version
glib-mkenums version 2.86.4
$ dpkg-query -W -f='${Package} ${Version}\n' libglib2.0-dev-bin
libglib2.0-dev-bin 2.80.0-6ubuntu3.9
Check the command output, rather than assuming the package version tells you which executable is being used. A locally built or separately installed GLib can take precedence in PATH.
Checkpoint
Continue only when command -v identifies the intended executable and the input headers are the ones belonging to your project.
2. Prepare a small header to inspect
glib-mkenums accepts one or more valid C files. It recognises ordinary enum definitions and can treat an enum as flags when its values use bit shifts. Create a test header in your project or build scratch directory:
typedef enum
{
DEMO_COLOUR_RED,
DEMO_COLOUR_BLUE
} DemoColour;
typedef enum /*< flags,prefix=DEMO,since=1.0 >*/
{
DEMO_OPTION_NONE = 0,
DEMO_OPTION_FAST = 1 << 0,
DEMO_OPTION_SAFE = 1 << 1
} DemoOption;
The comment between /*< and >*/ is a glib-mkenums annotation. Here it explicitly marks the second type as flags, sets the value-name prefix, and supplies the version text available as @enumsince@ in templates. Value annotations can also use skip or nick=some-name.
Run a minimal production command before designing a template:
$ glib-mkenums \
--fhead='/* generated file */' \
--vprod='@VALUENAME@ @valuenum@ @type@\n' \
demo.h
DEMO_COLOUR_RED 0 enum
DEMO_COLOUR_BLUE 1 enum
DEMO_OPTION_NONE 0 flags
DEMO_OPTION_FAST 1 flags
DEMO_OPTION_SAFE 2 flags
The output goes to standard output unless you select --output. The command adds its own generated-file prologue and footer. The \n in the shell argument is interpreted by glib-mkenums as a newline in the emitted text.
3. Move repeated output into a template
Use a template file for real builds. It is easier to review than a long shell command and separates the file, enum and value sections:
/*** BEGIN file-header ***/
/* generated from @basename@ */
/*** END file-header ***/
/*** BEGIN value-header ***/
GType @enum_name@_get_type (void);
/*** END value-header ***/
/*** BEGIN value-production ***/
/* @VALUENAME@ = @valuenum@, nick=@valuenick@, kind=@type@ */
/*** END value-production ***/
Save it as demo.h.in, then generate a separate output file:
$ glib-mkenums \
--template=demo.h.in \
--output=demo-enum-types.c \
demo.h
WARNING: @basename@ used in file-header section.
The warning is a useful trap: @basename@ belongs in a per-input-file production section, not the file-header section. Move that comment into file-production if you need the basename in generated output. The other substitutions in this example still demonstrate the important split: @enum_name@ becomes a lower-case function stem, @valuenick@ becomes a lower-case hyphenated nickname, and @type@ becomes enum or flags.
Check the generated file immediately:
$ test -s demo-enum-types.c
$ grep -F 'DEMO_OPTION_FAST = 1, nick=option-fast, kind=flags' demo-enum-types.c
/* DEMO_OPTION_FAST = 1, nick=option-fast, kind=flags */
4. Protect the destination during rebuilds
Warning
--output writes the named destination, and shell redirection with > truncates its target before glib-mkenums starts. Do not overwrite a checked-in generated file until the new output has succeeded and been reviewed.
Generate into a temporary name in the same directory, inspect it, then replace the old file deliberately:
$ glib-mkenums --template=demo.h.in --output=demo-enum-types.c.new demo.h
$ test -s demo-enum-types.c.new
$ diff -u demo-enum-types.c demo-enum-types.c.new
$ mv demo-enum-types.c.new demo-enum-types.c
If generation or review fails, remove only demo-enum-types.c.new. The existing generated file remains available. If you used a version-control checkout, recovery is normally git restore -- demo-enum-types.c, but that discards local changes to that file, so inspect git diff first.
5. Connect it to Meson
For a Meson project, prefer the GNOME module rather than reproducing the command line in a custom run target. The simple form generates a source and header target from project headers:
project_headers = [
'project-foo.h',
'project-bar.h',
]
gnome = import('gnome')
enum_files = gnome.mkenums_simple('enum-types',
sources: project_headers,
)
mainlib = library('project',
sources: project_sources + enum_files,
)
The returned array contains the generated source target first and the generated header target second. Include both where the library is built. If another target includes the generated header through a dependency, expose that header as a source:
mainlib_dep = declare_dependency(
sources: enum_files[1],
link_with: mainlib,
)
Do not add the generated source again to every dependent target. That can cause the same source to be generated or compiled more than once. For custom output, use gnome.mkenums() with h_template and c_template, then keep the generated targets in the dependency graph.
6. Diagnose the usual failures
- If no enum appears, verify that the input is valid C and that the enum definition is in the file passed on the command line. glib-mkenums parses source text; it is not a C preprocessor.
- If a nickname or function name is surprising, inspect the enum and value prefixes. Use
--identifier-prefixor--symbol-prefixwhen automatic prefix detection does not match your naming scheme. - If
@valuenum@causes an error, check that every value expression can be evaluated by the installed utility. Leave that substitution out when the numeric value is not needed. - If output is empty or stale, check the input paths, template section markers, and the exit status before allowing the build to continue.
Done means
- The intended glib-mkenums executable and version have been checked.
- The header contains the expected enum definitions and any flags annotations are explicit.
- A template produces a non-empty output file, and representative names, values and kinds were verified.
- Rebuilds use a temporary destination or a build directory, so a failed generation cannot silently destroy the previous output.
- Meson targets depend on the generated header and source exactly once.