From 6a1fdb814504a64ee71930258d652032b9cc9cea Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 6 Feb 2024 10:54:51 +0000 Subject: [PATCH 1/6] codegen: Use `-` instead of `stdout` for output to stdout In command-line tools, ordinary filenames normally do not have special-cased meanings, so commit 3ef742eb "Don't skip dbus-codegen tests on Win32" was a command-line API break: in the unlikely event that a user wanted to write to a file named exactly `stdout`, this would have been an incompatible change. There is a conventional pseudo-filename to represent standard output, which is `-` (for example `cat -` is a no-op filter). Adding support for this is technically also a command-line API break (in the very unlikely event that a user wants to write to a file named exactly `-`, they would now have to write it as `./-`), but filenames starting with a dash often require special treatment anyway, so this probably will not come as a surprise to anyone. When the output filename is `-` we don't want to use `#ifdef _____` as a header guard, so special-case it as `__STDOUT__` as before. Signed-off-by: Simon McVittie --- gio/gdbus-2.0/codegen/codegen.py | 3 + gio/gdbus-2.0/codegen/codegen_main.py | 2 +- gio/tests/codegen.py | 88 +++++++++++---------------- 3 files changed, 38 insertions(+), 55 deletions(-) diff --git a/gio/gdbus-2.0/codegen/codegen.py b/gio/gdbus-2.0/codegen/codegen.py index 9bdcf183e..b1c5fb929 100644 --- a/gio/gdbus-2.0/codegen/codegen.py +++ b/gio/gdbus-2.0/codegen/codegen.py @@ -57,6 +57,9 @@ def generate_namespace(namespace): 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 # stay conservative. safe_valid_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ" diff --git a/gio/gdbus-2.0/codegen/codegen_main.py b/gio/gdbus-2.0/codegen/codegen_main.py index cefa4b7e5..58af5c654 100644 --- a/gio/gdbus-2.0/codegen/codegen_main.py +++ b/gio/gdbus-2.0/codegen/codegen_main.py @@ -66,7 +66,7 @@ def find_prop(iface, prop): @contextmanager def file_or_stdout(filename): - if filename is None or filename == "stdout": + if filename is None or filename == "-": yield sys.stdout else: with open(filename, "w") as outfile: diff --git a/gio/tests/codegen.py b/gio/tests/codegen.py index 701728f83..055887c64 100644 --- a/gio/tests/codegen.py +++ b/gio/tests/codegen.py @@ -353,7 +353,7 @@ class TestCodegen(unittest.TestCase): def test_empty_interface_header(self): """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} @@ -376,14 +376,14 @@ G_END_DECLS def test_empty_interface_body(self): """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( """{standard_top_comment} {standard_config_h_include} -#include "stdout.h" +#include "-.h" {standard_header_includes} @@ -437,7 +437,7 @@ G_END_DECLS xml_file1.name, xml_file2.name, "--output", - "stdout", + "-", header_or_body, ) self.assertEqual("", result1.err) @@ -446,7 +446,7 @@ G_END_DECLS xml_file2.name, xml_file1.name, "--output", - "stdout", + "-", header_or_body, ) self.assertEqual("", result2.err) @@ -652,7 +652,7 @@ G_END_DECLS self.runCodegenWithInterface( "", "--output", - "stdout", + "-", "--body", "--glib-min-required", "hello mum", @@ -663,7 +663,7 @@ G_END_DECLS probably a typo).""" with self.assertRaises(subprocess.CalledProcessError): 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): @@ -671,7 +671,7 @@ G_END_DECLS result = self.runCodegenWithInterface( "", "--output", - "stdout", + "-", "--header", "--glib-min-required", "3", @@ -684,7 +684,7 @@ G_END_DECLS def test_glib_min_required_with_micro(self): """Test running with a --glib-min-required which contains a micro version.""" 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.assertNotEqual("", result.out.strip()) @@ -694,13 +694,13 @@ G_END_DECLS probably a typo).""" with self.assertRaises(subprocess.CalledProcessError): 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): """Test running with a --glib-max-allowed which contains only a major version.""" result = self.runCodegenWithInterface( - "", "--output", "stdout", "--header", "--glib-max-allowed", "3" + "", "--output", "-", "--header", "--glib-max-allowed", "3" ) self.assertEqual("", result.err) self.assertNotEqual("", result.out.strip()) @@ -708,7 +708,7 @@ G_END_DECLS def test_glib_max_allowed_with_micro(self): """Test running with a --glib-max-allowed which contains a micro version.""" 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.assertNotEqual("", result.out.strip()) @@ -720,7 +720,7 @@ G_END_DECLS result = self.runCodegenWithInterface( "", "--output", - "stdout", + "-", "--header", "--glib-max-allowed", "2.63", @@ -737,7 +737,7 @@ G_END_DECLS self.runCodegenWithInterface( "", "--output", - "stdout", + "-", "--body", "--glib-max-allowed", "2.62", @@ -780,9 +780,7 @@ G_END_DECLS """ with self.assertRaises(subprocess.CalledProcessError): - self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" - ) + self.runCodegenWithInterface(interface_xml, "--output", "-", "--body") good_types = [ "si{s{b(ybnqiuxtdh)}}{yv}{nv}{dv}", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" @@ -798,7 +796,7 @@ G_END_DECLS """ result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" + interface_xml, "--output", "-", "--body" ) self.assertEqual("", result.err) @@ -829,7 +827,7 @@ G_END_DECLS # Try without specifying --glib-min-required. result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--header" + interface_xml, "--output", "-", "--header" ) self.assertEqual("", result.err) self.assertEqual(result.out.strip().count("GUnixFDList"), 6) @@ -838,7 +836,7 @@ G_END_DECLS result = self.runCodegenWithInterface( interface_xml, "--output", - "stdout", + "-", "--header", "--glib-min-required", "2.32", @@ -852,7 +850,7 @@ G_END_DECLS result = self.runCodegenWithInterface( interface_xml, "--output", - "stdout", + "-", "--header", "--glib-min-required", "2.64", @@ -873,7 +871,7 @@ G_END_DECLS # Try without specifying --glib-min-required. result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--header" + interface_xml, "--output", "-", "--header" ) self.assertEqual("", result.err) self.assertEqual(result.out.strip().count("GDBusCallFlags call_flags,"), 0) @@ -883,7 +881,7 @@ G_END_DECLS result = self.runCodegenWithInterface( interface_xml, "--output", - "stdout", + "-", "--header", "--glib-min-required", "2.32", @@ -897,7 +895,7 @@ G_END_DECLS result = self.runCodegenWithInterface( interface_xml, "--output", - "stdout", + "-", "--header", "--glib-min-required", "2.64", @@ -918,9 +916,7 @@ G_END_DECLS """ - result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" - ) + result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body") stripped_out = result.out.strip() self.assertFalse(result.err) self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0) @@ -956,9 +952,7 @@ G_END_DECLS """ - result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" - ) + result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body") stripped_out = result.out.strip() self.assertFalse(result.err) self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0) @@ -993,9 +987,7 @@ G_END_DECLS """ - result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" - ) + result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body") stripped_out = result.out.strip() self.assertFalse(result.err) self.assertIs(stripped_out.count("g_signal_emit_by_name ("), 0) @@ -1026,9 +1018,7 @@ G_END_DECLS """ - result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" - ) + result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body") stripped_out = result.out.strip() self.assertFalse(result.err) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) @@ -1058,7 +1048,7 @@ G_END_DECLS """ result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" + interface_xml, "--output", "-", "--body" ) stripped_out = result.out.strip() self.assertFalse(result.err) @@ -1106,9 +1096,7 @@ G_END_DECLS """ - result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" - ) + result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body") stripped_out = result.out.strip() self.assertFalse(result.err) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) @@ -1144,9 +1132,7 @@ G_END_DECLS """ - result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" - ) + result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body") stripped_out = result.out.strip() self.assertFalse(result.err) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) @@ -1190,7 +1176,7 @@ G_END_DECLS """ result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" + interface_xml, "--output", "-", "--body" ) stripped_out = result.out.strip() self.assertFalse(result.err) @@ -1228,7 +1214,7 @@ G_END_DECLS """ result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" + interface_xml, "--output", "-", "--body" ) stripped_out = result.out.strip() self.assertFalse(result.err) @@ -1271,9 +1257,7 @@ G_END_DECLS """ - result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" - ) + result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body") stripped_out = result.out.strip() self.assertFalse(result.err) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) @@ -1331,9 +1315,7 @@ G_END_DECLS """ - result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" - ) + result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body") stripped_out = result.out.strip() self.assertFalse(result.err) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) @@ -1375,9 +1357,7 @@ G_END_DECLS """ - result = self.runCodegenWithInterface( - interface_xml, "--output", "stdout", "--body" - ) + result = self.runCodegenWithInterface(interface_xml, "--output", "-", "--body") stripped_out = result.out.strip() self.assertFalse(result.err) self.assertIs(stripped_out.count("g_cclosure_marshal_generic"), 0) From 1ba8386886aeea0c6cf983f6272dae16a6b995dd Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 6 Feb 2024 11:05:24 +0000 Subject: [PATCH 2/6] codegen: Document `--output -` Signed-off-by: Simon McVittie --- docs/reference/gio/gdbus-codegen.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/reference/gio/gdbus-codegen.rst b/docs/reference/gio/gdbus-codegen.rst index 4a4c07f27..8c6eab985 100644 --- a/docs/reference/gio/gdbus-codegen.rst +++ b/docs/reference/gio/gdbus-codegen.rst @@ -296,6 +296,10 @@ The following options are supported: is not allowed along with ``--output``, because the latter is used to generate only one file. + Since GLib 2.80, if *OUTFILE* is the literal string ``-``, the header + or source code will be written to standard output. To write to a file + starting with ``-``, it should be prefixed with ``./``. + ``--annotate`` *ELEMENT* *KEY* *VALUE* Used to inject D-Bus annotations into the given XML files. It can be used with @@ -802,4 +806,4 @@ Please send bug reports to either the distribution bug tracker or the SEE ALSO -------- -`gdbus(1) `_ \ No newline at end of file +`gdbus(1) `_ From 02a3417ac4e3ea8b79c09e9c052d600101769d3d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 6 Feb 2024 13:40:33 +0000 Subject: [PATCH 3/6] tests: Exercise gdbus-codegen --interface-info-body with empty input Signed-off-by: Simon McVittie --- gio/tests/codegen.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/gio/tests/codegen.py b/gio/tests/codegen.py index 055887c64..725b44080 100644 --- a/gio/tests/codegen.py +++ b/gio/tests/codegen.py @@ -145,6 +145,7 @@ class TestCodegen(unittest.TestCase): "#ifdef G_OS_UNIX\n" "# include \n" "#endif", + "interface_info_header_includes": "#include ", "private_gvalues_getters": """#ifdef G_ENABLE_DEBUG #define g_marshal_value_peek_boolean(v) g_value_get_boolean (v) #define g_marshal_value_peek_char(v) g_value_get_schar (v) @@ -395,6 +396,25 @@ G_END_DECLS 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} + +#include "-.h" + +{interface_info_header_includes}""".format( + **result.subs + ), + result.out.strip(), + ) + def test_reproducible(self): """Test builds are reproducible regardless of file ordering.""" xml_contents1 = """ From 5e8f053d3340fb181df8a0e7842ac539eda56e9d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 6 Feb 2024 13:40:41 +0000 Subject: [PATCH 4/6] tests: Exercise gdbus-codegen --interface-info-header with empty input Signed-off-by: Simon McVittie --- gio/tests/codegen.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/gio/tests/codegen.py b/gio/tests/codegen.py index 725b44080..f66b92c8c 100644 --- a/gio/tests/codegen.py +++ b/gio/tests/codegen.py @@ -367,6 +367,31 @@ class TestCodegen(unittest.TestCase): 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( + """{standard_top_comment} + +#ifndef __STDOUT__ +#define __STDOUT__ + +#include + +G_BEGIN_DECLS + + G_END_DECLS #endif /* __STDOUT__ */""".format( From fc7942f46b117027b27ba52d8a563b329174d85d Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 6 Feb 2024 13:48:45 +0000 Subject: [PATCH 5/6] gdbus-codegen: If writing body to stdout, don't try to include header If we're writing the body to standard output, we cannot know what the filename of the corresponding header is going to be, but it seems vanishingly unlikely that it will be either `stdout.h` (which we would traditionally have generated) or `-.h` (which we would have generated since !3886). This makes some of the output snippets sufficiently short that black(1) requires that they are folded into a single line. Signed-off-by: Simon McVittie --- docs/reference/gio/gdbus-codegen.rst | 12 +++++++++-- gio/gdbus-2.0/codegen/codegen.py | 29 +++++++++++++-------------- gio/gdbus-2.0/codegen/codegen_main.py | 12 +++++++++-- gio/tests/codegen.py | 4 ---- 4 files changed, 34 insertions(+), 23 deletions(-) diff --git a/docs/reference/gio/gdbus-codegen.rst b/docs/reference/gio/gdbus-codegen.rst index 8c6eab985..7fd5011b1 100644 --- a/docs/reference/gio/gdbus-codegen.rst +++ b/docs/reference/gio/gdbus-codegen.rst @@ -297,8 +297,16 @@ The following options are supported: only one file. Since GLib 2.80, if *OUTFILE* is the literal string ``-``, the header - or source code will be written to standard output. To write to a file - starting with ``-``, it should be prefixed with ``./``. + 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. + + In the rare situation that the intended output filename starts with ``-``, + it should be prefixed with ``./``. ``--annotate`` *ELEMENT* *KEY* *VALUE* diff --git a/gio/gdbus-2.0/codegen/codegen.py b/gio/gdbus-2.0/codegen/codegen.py index b1c5fb929..2e8ef8e2a 100644 --- a/gio/gdbus-2.0/codegen/codegen.py +++ b/gio/gdbus-2.0/codegen/codegen.py @@ -1124,15 +1124,13 @@ class InterfaceInfoBodyCodeGenerator: self.outfile.write("\n") self.outfile.write( - "#ifdef HAVE_CONFIG_H\n" - '# include "config.h"\n' - "#endif\n" - "\n" - '#include "%s"\n' - "\n" - "#include \n" % (self.header_name) + "#ifdef HAVE_CONFIG_H\n" '# include "config.h"\n' "#endif\n" "\n" ) - self.outfile.write("\n") + + if self.header_name: + self.outfile.write('#include "%s"\n\n' % (self.header_name)) + + self.outfile.write("#include \n\n") # ---------------------------------------------------------------------------------------------------- @@ -1472,20 +1470,21 @@ class CodeGenerator: def generate_body_preamble(self): basenames = ", ".join(self.input_files_basenames) self.outfile.write(LICENSE_STR.format(config.VERSION, basenames)) + if self.symbol_decoration_define is not None: self.outfile.write("\n") self.outfile.write("#define %s\n" % self.symbol_decoration_define) + self.outfile.write("\n") self.outfile.write( - "#ifdef HAVE_CONFIG_H\n" - '# include "config.h"\n' - "#endif\n" - "\n" - '#include "%s"\n' - "\n" - "#include \n" % (self.header_name) + "#ifdef HAVE_CONFIG_H\n" '# include "config.h"\n' "#endif\n" "\n" ) + if self.header_name: + self.outfile.write('#include "%s"\n\n' % (self.header_name)) + + self.outfile.write("#include \n") + self.outfile.write( "#ifdef G_OS_UNIX\n" "# include \n" "#endif\n" "\n" ) diff --git a/gio/gdbus-2.0/codegen/codegen_main.py b/gio/gdbus-2.0/codegen/codegen_main.py index 58af5c654..d5353f570 100644 --- a/gio/gdbus-2.0/codegen/codegen_main.py +++ b/gio/gdbus-2.0/codegen/codegen_main.py @@ -336,7 +336,11 @@ def codegen_main(): print_error("Using --body requires --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: if args.output is None: print_error("Using --interface-info-header requires --output") @@ -358,7 +362,11 @@ def codegen_main(): ) 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, # because that’s when gdbus-codegen was introduced. Support 1, 2 or 3 diff --git a/gio/tests/codegen.py b/gio/tests/codegen.py index f66b92c8c..a18dcb1a9 100644 --- a/gio/tests/codegen.py +++ b/gio/tests/codegen.py @@ -409,8 +409,6 @@ G_END_DECLS {standard_config_h_include} -#include "-.h" - {standard_header_includes} {private_gvalues_getters} @@ -432,8 +430,6 @@ G_END_DECLS {standard_config_h_include} -#include "-.h" - {interface_info_header_includes}""".format( **result.subs ), From 040caa5f6ae9e3237c9df7a0b1429081006be0f2 Mon Sep 17 00:00:00 2001 From: Simon McVittie Date: Tue, 6 Feb 2024 13:50:16 +0000 Subject: [PATCH 6/6] gdbus-codegen(1): Suggest --pragma-once for headers written to stdout Otherwise we would generate a multiple-inclusion guard of the form `#ifndef __STDOUT__ ...`, which can only work for one D-Bus interface per translation unit. Signed-off-by: Simon McVittie --- docs/reference/gio/gdbus-codegen.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/reference/gio/gdbus-codegen.rst b/docs/reference/gio/gdbus-codegen.rst index 7fd5011b1..2f973657a 100644 --- a/docs/reference/gio/gdbus-codegen.rst +++ b/docs/reference/gio/gdbus-codegen.rst @@ -305,6 +305,10 @@ The following options are supported: 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 ``./``.