Reimplementation of the Vala GIR parser and VAPI generator: a new backend for
vapigen
. The tool is designed with a focus on easy maintainability.
Requires libvala-0.56
to be installed where pkg-config
can find it.
Build with meson:
meson setup _build
meson compile -C _build
Use it like you would use vapigen
:
_build/vapigen --library libfoo-1.0 Foo-1.0.gir
The tool can be used as a drop-in replacement for the existing vapigen
tool.
Technically, it acts as a new back-end that vapigen
will call.
Please be aware that this project is still in development and is not yet finished.
The tool is split in three separate parts:
- A parser that generates a GIR node tree from a GIR file
- A GIR metadata processor, that applies metadata rules to the GIR node tree
- A series of Vala AST (abstract syntax tree) builders from the GIR node tree, that generate the Vala code that is later written into a VAPI file.
These three parts are found in three directories below src/
. The top-level
file vapigen.vala
was copied from Vala 0.56 and updated to launch the new gir
parser, and is otherwise identical to the original.
The GIR parser is invoked from src/gir-parser.vala
, a CodeVisitor that
accepts source files with a ".gir" extension. The XML parser itself is found in
src/gir/parser.vala
. It uses the MarkupReader
XML parser from libvala to
build a tree of Gir.Node
objects (see src/gir/node.vala
) with the following
properties:
- A parent Node
- The XML tag name (
namespace
,method
,return-type
, etc) - A map of attributes (
name
,c:type
, etc) - A list of child nodes
- The text contents of the XML element (for example in a
<doc>
element) - A Vala
SourceReference
with the location in the GIR XML file (for error reporting)
The Gir.Node class contains helper methods to get and set attributes, and to
iterate child nodes: Node.any_of (tag)
will return a single child node, while
Node.all_of (tag)
will return a list (actually a "filtered view") of all
child nodes with the requested tag.
The GIR node tree can be displayed in an easy-to-read text format with
to_string ()
, or in XML format with to_xml ()
. The generated XML is
identical to the original GIR XML file, except the XML element attributes have
a different ordering (the attributes are kept in a Vala.Map
, an unordered
collection).
After the GIR file has been parsed, a few transformations will always be
applied to the GIR node tree. For example, methods with one out-parameter that
return void, are updated to return the out-parameter value. These
transformations can be found in src/gir/transformations.vala
.
Next, all instructions from the GIR Metadata file (when available for this GIR
file) are applied to the GIR node tree. The Metadata parser and processor are
located in src/gir-metadata/
.
This is a major departure from the current GIR parser: The metadata transforms the GIR data, and the resulting GIR data is used to generate the VAPI.
Most metadata rules can applied 1-on-1 in the GIR. In some cases (like compact
classes), a "custom" GIR attribute (e.g. <class ... vala:compact="1">
) is
added.
The final step is to generate a VAPI file from the GIR data on which the metadata was applied.
A Vala AST is generated by a series of Builder classes that convert a GIR node
into Vala symbols. For example, ClassBuilder
generates a Vala Class with all
its fields, methods etc. from a GIR class. In other words: each Builder
converts a GIR node (e.g. <record>
) into a Vala Symbol (e.g. Vala.Struct
).
- To avoid confusion, most variables that refer to a GIR node are prefixed with
g_
(e.g.g_class
), while Vala symbols are prefixed withv_
(v_class
).
The NamespaceBuilder
class ties the output of all the builders together into
a new namespace. The namespace is added to the current CodeContext, and then
written to a VAPI file by vapigen.vala
(this part is completely unchanged).
There are a few differences in the vapigen
output that are not planned to be
fixed:
Different numbering of hidden (decimal) parameter positions
The current gir parser often uses 0.5
or 0.9
to indicate the position of a
"hidden" parameter index (for example the array_length_pos
) between two
"visible" parameters. However, I found it much simpler to always start counting
from 0.1
. The actual parameter ordering will still be the same. (If not,
please log an issue.)
Example: instance_pos
of Adw.AnimationTargetFunc
When a signal declaration has different parameter names than its emitter method, the current gir parser uses the names of the emitter method parameters for the Vala signal declaration, but I prefer to use the names of the signal parameters.
Example: Soup.Server.request_finished
For unknown reasons, the current gir parser often omits the default null
value for trailing GLib.Cancellable parameters. This is fixed in the new gir
parser.
Example: Adw.AlertDialog.choose
Multiple first_...
parameters in varargs functions will be hidden
When variadic functions declare the "first variadic parameter" like
first_param, ...
, the gir parser hides the first_param
in Vala. However,
when multiple "first params" exist, like first_key, first_value, ...
, it only
hides one. This is fixed in the new gir parser.
Example: Adw.Breakpoint.add_setters
The current gir parser sometimes generates a suboptimal cprefix
that, when
combined with the names of the enum members, is different from the enum member
names in C, requiring cname
annotations on the enum members or a metadata
override. This is fixed in the new gir parser.
Example: Soup.HTTPVersion
When a function is deprecated and has a documented replacement (with the "moved-to" gir attribute), the current gir parser sometimes merges the original function with its replacement, and flags the replacement as deprecated. This is fixed in the new gir parser.
Example: Graphene.Rect.alloc
The following items are still on the backlog:
When a function returns a struct, valac will generate incorrect C code because it assumes the struct is actually an out argument. To prevent this, the return value must be marked as nullable. I haven't yet implemented this.
Example: Graphene.Box.init, and many other Graphene functions
When a function returns void and has a trailing out argument, it's
automatically changed into a return value. This is an automatic transformation
in the GIR data. The return_void
metadata rule should revert this, but I
haven't yet implemented this.
The current GIR parser allows to override the type of a parameter, field or
constant declaration with a metadata type
rule that uses Vala syntax (e.g.
unowned List<string?>
). This hasn't been implemented yet.
The current GIR parser contains special treatment for off_t
, time_t
,
dev_t
, pid_t
, uid_t
and socklen_t
that I have not reimplemented yet.
Contributions are welcome. The code is LGPL-licensed. Please post issues and changes on GitHub.