Merge branch 'wip/smcv/codegen-stdout' into 'main'

codegen: Use `-` instead of `stdout` for output to stdout

See merge request GNOME/glib!3886
This commit is contained in:
Philip Withnall 2024-02-06 15:13:59 +00:00
commit 20c7479cc8
4 changed files with 121 additions and 74 deletions

View File

@ -296,6 +296,22 @@ The following options are supported:
is not allowed along with ``--output``, because the latter is used to generate is not allowed along with ``--output``, because the latter is used to generate
only one file. only one file.
Since GLib 2.80, if *OUTFILE* is the literal string ``-``, the header
or source code will be written to standard output.
For ``--body`` and ``--interface-info-body``, the generated code will not
automatically ``#include`` a corresponding header file when writing to
standard output, because there is no obvious name for that header file.
This might make it necessary to use ``cc -include foo.h``, or generate a
filename like ``foo-impl.h`` and ``#include`` it into a wrapper ``.c`` file.
For ``--header`` and ``--interface-info-header``, there is no obvious
name for a traditional multiple-inclusion guard when writing to standard
output, so using the ``--pragma-once`` option is recommended.
In the rare situation that the intended output filename starts with ``-``,
it should be prefixed with ``./``.
``--annotate`` *ELEMENT* *KEY* *VALUE* ``--annotate`` *ELEMENT* *KEY* *VALUE*
Used to inject D-Bus annotations into the given XML files. It can be used with Used to inject D-Bus annotations into the given XML files. It can be used with
@ -802,4 +818,4 @@ Please send bug reports to either the distribution bug tracker or the
SEE ALSO SEE ALSO
-------- --------
`gdbus(1) <man:gdbus(1)>`_ `gdbus(1) <man:gdbus(1)>`_

View File

@ -57,6 +57,9 @@ def generate_namespace(namespace):
def generate_header_guard(header_name): def generate_header_guard(header_name):
if header_name == "-":
return "STDOUT"
# There might be more characters that are safe to use than these, but lets # There might be more characters that are safe to use than these, but lets
# stay conservative. # stay conservative.
safe_valid_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" safe_valid_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@ -1121,15 +1124,13 @@ class InterfaceInfoBodyCodeGenerator:
self.outfile.write("\n") self.outfile.write("\n")
self.outfile.write( self.outfile.write(
"#ifdef HAVE_CONFIG_H\n" "#ifdef HAVE_CONFIG_H\n" '# include "config.h"\n' "#endif\n" "\n"
'# include "config.h"\n'
"#endif\n"
"\n"
'#include "%s"\n'
"\n"
"#include <string.h>\n" % (self.header_name)
) )
self.outfile.write("\n")
if self.header_name:
self.outfile.write('#include "%s"\n\n' % (self.header_name))
self.outfile.write("#include <string.h>\n\n")
# ---------------------------------------------------------------------------------------------------- # ----------------------------------------------------------------------------------------------------
@ -1469,20 +1470,21 @@ class CodeGenerator:
def generate_body_preamble(self): def generate_body_preamble(self):
basenames = ", ".join(self.input_files_basenames) basenames = ", ".join(self.input_files_basenames)
self.outfile.write(LICENSE_STR.format(config.VERSION, basenames)) self.outfile.write(LICENSE_STR.format(config.VERSION, basenames))
if self.symbol_decoration_define is not None: if self.symbol_decoration_define is not None:
self.outfile.write("\n") self.outfile.write("\n")
self.outfile.write("#define %s\n" % self.symbol_decoration_define) self.outfile.write("#define %s\n" % self.symbol_decoration_define)
self.outfile.write("\n") self.outfile.write("\n")
self.outfile.write( self.outfile.write(
"#ifdef HAVE_CONFIG_H\n" "#ifdef HAVE_CONFIG_H\n" '# include "config.h"\n' "#endif\n" "\n"
'# include "config.h"\n'
"#endif\n"
"\n"
'#include "%s"\n'
"\n"
"#include <string.h>\n" % (self.header_name)
) )
if self.header_name:
self.outfile.write('#include "%s"\n\n' % (self.header_name))
self.outfile.write("#include <string.h>\n")
self.outfile.write( self.outfile.write(
"#ifdef G_OS_UNIX\n" "# include <gio/gunixfdlist.h>\n" "#endif\n" "\n" "#ifdef G_OS_UNIX\n" "# include <gio/gunixfdlist.h>\n" "#endif\n" "\n"
) )

View File

@ -66,7 +66,7 @@ def find_prop(iface, prop):
@contextmanager @contextmanager
def file_or_stdout(filename): def file_or_stdout(filename):
if filename is None or filename == "stdout": if filename is None or filename == "-":
yield sys.stdout yield sys.stdout
else: else:
with open(filename, "w") as outfile: with open(filename, "w") as outfile:
@ -336,7 +336,11 @@ def codegen_main():
print_error("Using --body requires --output") print_error("Using --body requires --output")
c_file = args.output c_file = args.output
header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
if c_file == "-":
header_name = ""
else:
header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
elif args.interface_info_header: elif args.interface_info_header:
if args.output is None: if args.output is None:
print_error("Using --interface-info-header requires --output") print_error("Using --interface-info-header requires --output")
@ -358,7 +362,11 @@ def codegen_main():
) )
c_file = args.output c_file = args.output
header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
if c_file == "-":
header_name = ""
else:
header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
# Check the minimum GLib version. The minimum --glib-min-required is 2.30, # Check the minimum GLib version. The minimum --glib-min-required is 2.30,
# because thats when gdbus-codegen was introduced. Support 1, 2 or 3 # because thats when gdbus-codegen was introduced. Support 1, 2 or 3

View File

@ -145,6 +145,7 @@ class TestCodegen(unittest.TestCase):
"#ifdef G_OS_UNIX\n" "#ifdef G_OS_UNIX\n"
"# include <gio/gunixfdlist.h>\n" "# include <gio/gunixfdlist.h>\n"
"#endif", "#endif",
"interface_info_header_includes": "#include <string.h>",
"private_gvalues_getters": """#ifdef G_ENABLE_DEBUG "private_gvalues_getters": """#ifdef G_ENABLE_DEBUG
#define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v)
#define g_marshal_value_peek_char(v) g_value_get_schar (v) #define g_marshal_value_peek_char(v) g_value_get_schar (v)
@ -353,7 +354,32 @@ class TestCodegen(unittest.TestCase):
def test_empty_interface_header(self): def test_empty_interface_header(self):
"""Test generating a header with an empty interface file.""" """Test generating a header with an empty interface file."""
result = self.runCodegenWithInterface("", "--output", "stdout", "--header") result = self.runCodegenWithInterface("", "--output", "-", "--header")
self.assertEqual("", result.err)
self.assertEqual(
"""{standard_top_comment}
#ifndef __STDOUT__
#define __STDOUT__
#include <gio/gio.h>
G_BEGIN_DECLS
G_END_DECLS
#endif /* __STDOUT__ */""".format(
**result.subs
),
result.out.strip(),
)
def test_empty_interface_info_header(self):
"""Test generating a header with an empty interface file."""
result = self.runCodegenWithInterface(
"", "--output", "-", "--interface-info-header"
)
self.assertEqual("", result.err) self.assertEqual("", result.err)
self.assertEqual( self.assertEqual(
"""{standard_top_comment} """{standard_top_comment}
@ -376,15 +402,13 @@ G_END_DECLS
def test_empty_interface_body(self): def test_empty_interface_body(self):
"""Test generating a body with an empty interface file.""" """Test generating a body with an empty interface file."""
result = self.runCodegenWithInterface("", "--output", "stdout", "--body") result = self.runCodegenWithInterface("", "--output", "-", "--body")
self.assertEqual("", result.err) self.assertEqual("", result.err)
self.assertEqual( self.assertEqual(
"""{standard_top_comment} """{standard_top_comment}
{standard_config_h_include} {standard_config_h_include}
#include "stdout.h"
{standard_header_includes} {standard_header_includes}
{private_gvalues_getters} {private_gvalues_getters}
@ -395,6 +419,23 @@ G_END_DECLS
result.out.strip(), result.out.strip(),
) )
def test_empty_interface_info_body(self):
"""Test generating a body with an empty interface file."""
result = self.runCodegenWithInterface(
"", "--output", "-", "--interface-info-body"
)
self.assertEqual("", result.err)
self.assertEqual(
"""{standard_top_comment}
{standard_config_h_include}
{interface_info_header_includes}""".format(
**result.subs
),
result.out.strip(),
)
def test_reproducible(self): def test_reproducible(self):
"""Test builds are reproducible regardless of file ordering.""" """Test builds are reproducible regardless of file ordering."""
xml_contents1 = """ xml_contents1 = """
@ -437,7 +478,7 @@ G_END_DECLS
xml_file1.name, xml_file1.name,
xml_file2.name, xml_file2.name,
"--output", "--output",
"stdout", "-",
header_or_body, header_or_body,
) )
self.assertEqual("", result1.err) self.assertEqual("", result1.err)
@ -446,7 +487,7 @@ G_END_DECLS
xml_file2.name, xml_file2.name,
xml_file1.name, xml_file1.name,
"--output", "--output",
"stdout", "-",
header_or_body, header_or_body,
) )
self.assertEqual("", result2.err) self.assertEqual("", result2.err)
@ -652,7 +693,7 @@ G_END_DECLS
self.runCodegenWithInterface( self.runCodegenWithInterface(
"", "",
"--output", "--output",
"stdout", "-",
"--body", "--body",
"--glib-min-required", "--glib-min-required",
"hello mum", "hello mum",
@ -663,7 +704,7 @@ G_END_DECLS
probably a typo).""" probably a typo)."""
with self.assertRaises(subprocess.CalledProcessError): with self.assertRaises(subprocess.CalledProcessError):
self.runCodegenWithInterface( self.runCodegenWithInterface(
"", "--output", "stdout", "--body", "--glib-min-required", "2.6" "", "--output", "-", "--body", "--glib-min-required", "2.6"
) )
def test_glib_min_required_major_only(self): def test_glib_min_required_major_only(self):
@ -671,7 +712,7 @@ G_END_DECLS
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
"", "",
"--output", "--output",
"stdout", "-",
"--header", "--header",
"--glib-min-required", "--glib-min-required",
"3", "3",
@ -684,7 +725,7 @@ G_END_DECLS
def test_glib_min_required_with_micro(self): def test_glib_min_required_with_micro(self):
"""Test running with a --glib-min-required which contains a micro version.""" """Test running with a --glib-min-required which contains a micro version."""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
"", "--output", "stdout", "--header", "--glib-min-required", "2.46.2" "", "--output", "-", "--header", "--glib-min-required", "2.46.2"
) )
self.assertEqual("", result.err) self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip()) self.assertNotEqual("", result.out.strip())
@ -694,13 +735,13 @@ G_END_DECLS
probably a typo).""" probably a typo)."""
with self.assertRaises(subprocess.CalledProcessError): with self.assertRaises(subprocess.CalledProcessError):
self.runCodegenWithInterface( self.runCodegenWithInterface(
"", "--output", "stdout", "--body", "--glib-max-allowed", "2.6" "", "--output", "-", "--body", "--glib-max-allowed", "2.6"
) )
def test_glib_max_allowed_major_only(self): def test_glib_max_allowed_major_only(self):
"""Test running with a --glib-max-allowed which contains only a major version.""" """Test running with a --glib-max-allowed which contains only a major version."""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
"", "--output", "stdout", "--header", "--glib-max-allowed", "3" "", "--output", "-", "--header", "--glib-max-allowed", "3"
) )
self.assertEqual("", result.err) self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip()) self.assertNotEqual("", result.out.strip())
@ -708,7 +749,7 @@ G_END_DECLS
def test_glib_max_allowed_with_micro(self): def test_glib_max_allowed_with_micro(self):
"""Test running with a --glib-max-allowed which contains a micro version.""" """Test running with a --glib-max-allowed which contains a micro version."""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
"", "--output", "stdout", "--header", "--glib-max-allowed", "2.46.2" "", "--output", "-", "--header", "--glib-max-allowed", "2.46.2"
) )
self.assertEqual("", result.err) self.assertEqual("", result.err)
self.assertNotEqual("", result.out.strip()) self.assertNotEqual("", result.out.strip())
@ -720,7 +761,7 @@ G_END_DECLS
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
"", "",
"--output", "--output",
"stdout", "-",
"--header", "--header",
"--glib-max-allowed", "--glib-max-allowed",
"2.63", "2.63",
@ -737,7 +778,7 @@ G_END_DECLS
self.runCodegenWithInterface( self.runCodegenWithInterface(
"", "",
"--output", "--output",
"stdout", "-",
"--body", "--body",
"--glib-max-allowed", "--glib-max-allowed",
"2.62", "2.62",
@ -780,9 +821,7 @@ G_END_DECLS
</interface> </interface>
</node>""" </node>"""
with self.assertRaises(subprocess.CalledProcessError): with self.assertRaises(subprocess.CalledProcessError):
self.runCodegenWithInterface( self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
interface_xml, "--output", "stdout", "--body"
)
good_types = [ good_types = [
"si{s{b(ybnqiuxtdh)}}{yv}{nv}{dv}", "si{s{b(ybnqiuxtdh)}}{yv}{nv}{dv}",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
@ -798,7 +837,7 @@ G_END_DECLS
</interface> </interface>
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
interface_xml, "--output", "stdout", "--body" interface_xml, "--output", "-", "--body"
) )
self.assertEqual("", result.err) self.assertEqual("", result.err)
@ -829,7 +868,7 @@ G_END_DECLS
# Try without specifying --glib-min-required. # Try without specifying --glib-min-required.
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
interface_xml, "--output", "stdout", "--header" interface_xml, "--output", "-", "--header"
) )
self.assertEqual("", result.err) self.assertEqual("", result.err)
self.assertEqual(result.out.strip().count("GUnixFDList"), 6) self.assertEqual(result.out.strip().count("GUnixFDList"), 6)
@ -838,7 +877,7 @@ G_END_DECLS
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
interface_xml, interface_xml,
"--output", "--output",
"stdout", "-",
"--header", "--header",
"--glib-min-required", "--glib-min-required",
"2.32", "2.32",
@ -852,7 +891,7 @@ G_END_DECLS
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
interface_xml, interface_xml,
"--output", "--output",
"stdout", "-",
"--header", "--header",
"--glib-min-required", "--glib-min-required",
"2.64", "2.64",
@ -873,7 +912,7 @@ G_END_DECLS
# Try without specifying --glib-min-required. # Try without specifying --glib-min-required.
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
interface_xml, "--output", "stdout", "--header" interface_xml, "--output", "-", "--header"
) )
self.assertEqual("", result.err) self.assertEqual("", result.err)
self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 0) self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 0)
@ -883,7 +922,7 @@ G_END_DECLS
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
interface_xml, interface_xml,
"--output", "--output",
"stdout", "-",
"--header", "--header",
"--glib-min-required", "--glib-min-required",
"2.32", "2.32",
@ -897,7 +936,7 @@ G_END_DECLS
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
interface_xml, interface_xml,
"--output", "--output",
"stdout", "-",
"--header", "--header",
"--glib-min-required", "--glib-min-required",
"2.64", "2.64",
@ -918,9 +957,7 @@ G_END_DECLS
</interface> </interface>
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
interface_xml, "--output", "stdout", "--body"
)
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0) self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
@ -956,9 +993,7 @@ G_END_DECLS
</interface> </interface>
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
interface_xml, "--output", "stdout", "--body"
)
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0) self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
@ -993,9 +1028,7 @@ G_END_DECLS
</interface> </interface>
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
interface_xml, "--output", "stdout", "--body"
)
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0) self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0)
@ -1026,9 +1059,7 @@ G_END_DECLS
</interface> </interface>
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
interface_xml, "--output", "stdout", "--body"
)
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
@ -1058,7 +1089,7 @@ G_END_DECLS
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
interface_xml, "--output", "stdout", "--body" interface_xml, "--output", "-", "--body"
) )
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
@ -1106,9 +1137,7 @@ G_END_DECLS
</interface> </interface>
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
interface_xml, "--output", "stdout", "--body"
)
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
@ -1144,9 +1173,7 @@ G_END_DECLS
</interface> </interface>
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
interface_xml, "--output", "stdout", "--body"
)
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
@ -1190,7 +1217,7 @@ G_END_DECLS
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
interface_xml, "--output", "stdout", "--body" interface_xml, "--output", "-", "--body"
) )
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
@ -1228,7 +1255,7 @@ G_END_DECLS
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(
interface_xml, "--output", "stdout", "--body" interface_xml, "--output", "-", "--body"
) )
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
@ -1271,9 +1298,7 @@ G_END_DECLS
</interface> </interface>
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
interface_xml, "--output", "stdout", "--body"
)
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
@ -1331,9 +1356,7 @@ G_END_DECLS
</interface> </interface>
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
interface_xml, "--output", "stdout", "--body"
)
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)
@ -1375,9 +1398,7 @@ G_END_DECLS
</interface> </interface>
</node>""" </node>"""
result = self.runCodegenWithInterface( result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body")
interface_xml, "--output", "stdout", "--body"
)
stripped_out = result.out.strip() stripped_out = result.out.strip()
self.assertFalse(result.err) self.assertFalse(result.err)
self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0)