diff options
author | Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> | 2023-11-14 19:33:53 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2023-11-14 19:33:53 +0000 |
commit | d536cd883af1469b1ebf3a38f7492f70c8d4bd3f (patch) | |
tree | 8ba32bdb491d79a6820d109d4a7df03070d7fc88 | |
parent | 86cfa9a19dfde6cd3b40118a2fa9cc5fd42f8451 (diff) | |
parent | 6065c05b37f91e00145746aa0151a8ad29126b0e (diff) | |
download | glib-d536cd883af1469b1ebf3a38f7492f70c8d4bd3f.tar.gz |
Merge "Fix GLIB2 build" into emu-dev
-rw-r--r-- | BUILD | 146 | ||||
-rw-r--r-- | WORKSPACE | 1 | ||||
-rw-r--r-- | os/windows/config.h | 4 | ||||
-rw-r--r-- | os/windows/glib/gnulib/gnulib_math.h (renamed from os/windows/glib/gnulib_math.h) | 0 | ||||
-rw-r--r-- | os/windows/gmodule/gmoduleconf.h | 50 | ||||
-rw-r--r-- | os/windows/gobject/glib-genmarshal | 1080 | ||||
-rw-r--r-- | os/windows/gobject/glib-mkenums | 810 |
7 files changed, 2078 insertions, 13 deletions
@@ -1,3 +1,5 @@ +load("@bazel_skylib//rules:run_binary.bzl", "run_binary") + objc_library( name = "glib-darwin", srcs = [ @@ -13,22 +15,75 @@ objc_library( "-I $(execpath os/darwin)", "-I $(execpath os/darwin/glib)", "-I $(execpath glib)", + "-I $(execpath .)", ], data = [ # These paths are here so we can use them in copts with $(execpath ...) "os/darwin", "os/darwin/glib", "glib", + ".", ], includes = [ ".", ], ) +py_binary( + name = "gen-visibility-macros", + srcs = ["tools/gen-visibility-macros.py"], +) + +run_binary( + name = "gen_visibility_macros", + outs = ["gmodule/gmodule-visibility.h"], + args = [ + "2.77.2", + "visibility-macros", + "GMODULE", + "$(location gmodule/gmodule-visibility.h)", + ], + tool = ":gen-visibility-macros", +) + +cc_library( + name = "gnulib", + srcs = [ + "glib/gnulib/asnprintf.c", + "glib/gnulib/isnan.c", + "glib/gnulib/printf.c", + "glib/gnulib/printf-args.c", + "glib/gnulib/printf-frexp.c", + "glib/gnulib/printf-frexpl.c", + "glib/gnulib/printf-parse.c", + # "glib/gnulib/vasnprintf.c", + "glib/gnulib/xsize.c", + ] + glob([ + "glib/gnulib/*.h", + "glib/*.h", + "glib/deprecated/*.h", + ]), + hdrs = [ + "glib.h", + "glib/gnulib/g-gnulib.h", + "glib/gnulib/printf-frexp.c", + "os/windows/config.h", + "os/windows/glib/glibconfig.h", + "os/windows/glib/gnulib/gnulib_math.h", + ], + includes = [ + "os/windows", + "os/windows/glib", + "os/windows/glib/gnulib", + ], +) + +# Note we merge gmodule inside this. cc_library( # Named "glib2" so it doesn't shadow the "glib" directory in this package. - name = "glib2", + name = "glib-2.0", srcs = [ + "gmodule/gmodule.c", "glib/garcbox.c", "glib/garray.c", "glib/gasyncqueue.c", @@ -78,7 +133,7 @@ cc_library( "glib/grcbox.c", "glib/grefcount.c", "glib/grefstring.c", - # "gregex.c", + "glib/gregex.c", "glib/gscanner.c", "glib/gsequence.c", "glib/gshell.c", @@ -114,6 +169,7 @@ cc_library( "glib/gversion.c", "glib/gwakeup.c", "glib/libcharset/localcharset.c", + "gmodule/gmodule.h", ] + select({ "@platforms//os:macos": [ "glib/giounix.c", @@ -123,11 +179,35 @@ cc_library( "glib/gthread-posix.c", "os/darwin/config.h", "os/darwin/glib/glibconfig.h", + "os/darwin/gmodule/gmoduleconf.h", + ], + "@platforms//os:windows": [ + "glib/dirent/dirent.h", + "glib/dirent/wdirent.c", + "glib/giowin32.c", + "glib/gspawn-win32.c", + "glib/gthread-win32.c", + "glib/gwin32.c", + "os/windows/config.h", + "os/windows/glib/glibconfig.h", + "os/windows/gmodule/gmoduleconf.h", + ], + "@platforms//os:linux": [ + "glib/giounix.c", + "glib/gjournal-private.c", + "glib/glib-unix.c", + "glib/glib-unixprivate.h", + "glib/gspawn.c", + "glib/gthread-posix.c", + "os/linux/config.h", + "os/linux/glib/glibconfig.h", + "os/linux/gmodule/gmoduleconf.h", ], "//conditions:default": [], }) + glob( [ "glib/*.h", + "glib/gnulib/*.h", "glib/deprecated/*.h", "glib/libcharset/*.h", ], @@ -137,13 +217,22 @@ cc_library( ), hdrs = [ "glib.h", - ], + "gmodule/gmodule-dl.c", # TODO: this technically leaks out. + "gmodule/gmodule-visibility.h", + ] + select({ + "@platforms//os:windows": [ + "glib/dirent/dirent.c", + "glib/gstdio-private.c", + "glib/gwin32-private.c", + "glib/win_iconv.c", + "gmodule/gmodule-win32.c", + ], + "//conditions:default": [], + }), copts = [ - "-fvisibility=hidden", "-Winvalid-pch", "-Wextra", "-Wpedantic", - "-std=gnu99", "-fno-strict-aliasing", "-Wimplicit-fallthrough", "-Wmisleading-indentation", @@ -168,24 +257,58 @@ cc_library( "-Werror=pointer-sign", "-Wno-string-plus-int", ] + select({ + # Needed for using <glib/xxx> vs "glib/xxxx" "@platforms//os:macos": [ + "-fvisibility=hidden", + "-std=gnu99", "-I $(execpath os/darwin)", "-I $(execpath os/darwin/glib)", + "-I $(execpath os/darwin/gmodule)", "-I $(execpath glib)", + "-I $(execpath .)", + ], + "@platforms//os:windows": [ + "-Wno-inconsistent-dllimport", + "-Wno-implicit-fallthrough", + "-Wno-unused-function", + "-Wno-#pragma-messages", + ], + "@platforms//os:linux": [ + "-fvisibility=hidden", + "-std=gnu99", ], "//conditions:default": [], }), - data = select({ + data = [ + # These paths are here so we can use them in copts with $(execpath ...) + "os/darwin", + "os/darwin/glib", + "os/darwin/gmodule", + "os/windows/gmodule", + "glib", + ".", + ], + includes = select({ "@platforms//os:macos": [ - # These paths are here so we can use them in copts with $(execpath ...) "os/darwin", "os/darwin/glib", - "glib", + "os/darwin/gmodule", + ], + "@platforms//os:linux": [ + "os/linux", + "os/linux/glib", + "os/linux/gmodule", + ], + "@platforms//os:windows": [ + "os/windows", + "os/windows/glib", + "os/windows/gmodule", ], "//conditions:default": [], - }), - includes = [ + }) + [ ".", + "glib", + "gmodule", ], local_defines = [ "GLIB_COMPILATION", @@ -195,6 +318,7 @@ cc_library( ], deps = select({ "@platforms//os:macos": [":glib-darwin"], + "@platforms//os:windows": [":gnulib"], "//conditions:default": [], - }), + }) + ["@pcre2"], ) diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 000000000..4e7255e7a --- /dev/null +++ b/WORKSPACE @@ -0,0 +1 @@ +workspace(name = "glib") diff --git a/os/windows/config.h b/os/windows/config.h index 360cbb3de..9747b520f 100644 --- a/os/windows/config.h +++ b/os/windows/config.h @@ -177,7 +177,7 @@ #define _WIN32_WINNT 0x0601 -#define gl_extern_inline +#define gl_extern_inline /* Please see the Gnulib manual for how to use these macros. Suppress extern inline with HP-UX cc, as it appears to be broken; see @@ -271,7 +271,7 @@ #endif -#define gl_unused +#define gl_unused /* Define as a marker that can be attached to declarations that might not be used. This helps to reduce warnings, such as from GCC -Wunused-parameter. */ diff --git a/os/windows/glib/gnulib_math.h b/os/windows/glib/gnulib/gnulib_math.h index 7fa41b270..7fa41b270 100644 --- a/os/windows/glib/gnulib_math.h +++ b/os/windows/glib/gnulib/gnulib_math.h diff --git a/os/windows/gmodule/gmoduleconf.h b/os/windows/gmodule/gmoduleconf.h new file mode 100644 index 000000000..42aacaba0 --- /dev/null +++ b/os/windows/gmodule/gmoduleconf.h @@ -0,0 +1,50 @@ +/* GMODULE - GLIB wrapper code for dynamic module loading + * Copyright (C) 1998 Tim Janik + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef __G_MODULE_CONF_H__ +#define __G_MODULE_CONF_H__ + + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define G_MODULE_IMPL_NONE 0 +#define G_MODULE_IMPL_DL 1 +#define G_MODULE_IMPL_WIN32 3 +#define G_MODULE_IMPL_AR 7 + +#define G_MODULE_IMPL G_MODULE_IMPL_WIN32 +#undef G_MODULE_HAVE_DLERROR +#if (0) +#define G_MODULE_HAVE_DLERROR +#endif +#if (0) +#define G_MODULE_NEED_USCORE +#endif +#if (0) +#define G_MODULE_BROKEN_RTLD_GLOBAL +#endif + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __G_MODULE_CONF_H__ */ diff --git a/os/windows/gobject/glib-genmarshal b/os/windows/gobject/glib-genmarshal new file mode 100644 index 000000000..e88c461d9 --- /dev/null +++ b/os/windows/gobject/glib-genmarshal @@ -0,0 +1,1080 @@ +#!/usr/bin/env python3 + +# pylint: disable=too-many-lines, missing-docstring, invalid-name + +# This file is part of GLib +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, see <http://www.gnu.org/licenses/>. + +import argparse +import os +import re +import sys + +VERSION_STR = '''glib-genmarshal version 2.79.0 +glib-genmarshal comes with ABSOLUTELY NO WARRANTY. +You may redistribute copies of glib-genmarshal under the terms of +the GNU General Public License which can be found in the +GLib source package. Sources, examples and contact +information are available at http://www.gtk.org''' + +GETTERS_STR = '''#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) +#define g_marshal_value_peek_uchar(v) g_value_get_uchar (v) +#define g_marshal_value_peek_int(v) g_value_get_int (v) +#define g_marshal_value_peek_uint(v) g_value_get_uint (v) +#define g_marshal_value_peek_long(v) g_value_get_long (v) +#define g_marshal_value_peek_ulong(v) g_value_get_ulong (v) +#define g_marshal_value_peek_int64(v) g_value_get_int64 (v) +#define g_marshal_value_peek_uint64(v) g_value_get_uint64 (v) +#define g_marshal_value_peek_enum(v) g_value_get_enum (v) +#define g_marshal_value_peek_flags(v) g_value_get_flags (v) +#define g_marshal_value_peek_float(v) g_value_get_float (v) +#define g_marshal_value_peek_double(v) g_value_get_double (v) +#define g_marshal_value_peek_string(v) (char*) g_value_get_string (v) +#define g_marshal_value_peek_param(v) g_value_get_param (v) +#define g_marshal_value_peek_boxed(v) g_value_get_boxed (v) +#define g_marshal_value_peek_pointer(v) g_value_get_pointer (v) +#define g_marshal_value_peek_object(v) g_value_get_object (v) +#define g_marshal_value_peek_variant(v) g_value_get_variant (v) +#else /* !G_ENABLE_DEBUG */ +/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API. + * Do not access GValues directly in your code. Instead, use the + * g_value_get_*() functions + */ +#define g_marshal_value_peek_boolean(v) (v)->data[0].v_int +#define g_marshal_value_peek_char(v) (v)->data[0].v_int +#define g_marshal_value_peek_uchar(v) (v)->data[0].v_uint +#define g_marshal_value_peek_int(v) (v)->data[0].v_int +#define g_marshal_value_peek_uint(v) (v)->data[0].v_uint +#define g_marshal_value_peek_long(v) (v)->data[0].v_long +#define g_marshal_value_peek_ulong(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_int64(v) (v)->data[0].v_int64 +#define g_marshal_value_peek_uint64(v) (v)->data[0].v_uint64 +#define g_marshal_value_peek_enum(v) (v)->data[0].v_long +#define g_marshal_value_peek_flags(v) (v)->data[0].v_ulong +#define g_marshal_value_peek_float(v) (v)->data[0].v_float +#define g_marshal_value_peek_double(v) (v)->data[0].v_double +#define g_marshal_value_peek_string(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_param(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_boxed(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_pointer(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_object(v) (v)->data[0].v_pointer +#define g_marshal_value_peek_variant(v) (v)->data[0].v_pointer +#endif /* !G_ENABLE_DEBUG */''' + +DEPRECATED_MSG_STR = 'The token "{}" is deprecated; use "{}" instead' + +VA_ARG_STR = \ + ' arg{:d} = ({:s}) va_arg (args_copy, {:s});' +STATIC_CHECK_STR = \ + '(param_types[{:d}] & G_SIGNAL_TYPE_STATIC_SCOPE) == 0 && ' +BOX_TYPED_STR = \ + ' arg{idx:d} = {box_func} (param_types[{idx:d}] & ~G_SIGNAL_TYPE_STATIC_SCOPE, arg{idx:d});' +BOX_UNTYPED_STR = \ + ' arg{idx:d} = {box_func} (arg{idx:d});' +UNBOX_TYPED_STR = \ + ' {unbox_func} (param_types[{idx:d}] & ~G_SIGNAL_TYPE_STATIC_SCOPE, arg{idx:d});' +UNBOX_UNTYPED_STR = \ + ' {unbox_func} (arg{idx:d});' + +STD_PREFIX = 'g_cclosure_marshal' + +# These are part of our ABI; keep this in sync with gmarshal.h +GOBJECT_MARSHALLERS = { + 'g_cclosure_marshal_VOID__VOID', + 'g_cclosure_marshal_VOID__BOOLEAN', + 'g_cclosure_marshal_VOID__CHAR', + 'g_cclosure_marshal_VOID__UCHAR', + 'g_cclosure_marshal_VOID__INT', + 'g_cclosure_marshal_VOID__UINT', + 'g_cclosure_marshal_VOID__LONG', + 'g_cclosure_marshal_VOID__ULONG', + 'g_cclosure_marshal_VOID__ENUM', + 'g_cclosure_marshal_VOID__FLAGS', + 'g_cclosure_marshal_VOID__FLOAT', + 'g_cclosure_marshal_VOID__DOUBLE', + 'g_cclosure_marshal_VOID__STRING', + 'g_cclosure_marshal_VOID__PARAM', + 'g_cclosure_marshal_VOID__BOXED', + 'g_cclosure_marshal_VOID__POINTER', + 'g_cclosure_marshal_VOID__OBJECT', + 'g_cclosure_marshal_VOID__VARIANT', + 'g_cclosure_marshal_VOID__UINT_POINTER', + 'g_cclosure_marshal_BOOLEAN__FLAGS', + 'g_cclosure_marshal_STRING__OBJECT_POINTER', + 'g_cclosure_marshal_BOOLEAN__BOXED_BOXED', +} + + +# pylint: disable=too-few-public-methods +class Color: + '''ANSI Terminal colors''' + GREEN = '\033[1;32m' + BLUE = '\033[1;34m' + YELLOW = '\033[1;33m' + RED = '\033[1;31m' + END = '\033[0m' + + +def print_color(msg, color=Color.END, prefix='MESSAGE'): + '''Print a string with a color prefix''' + if os.isatty(sys.stderr.fileno()): + real_prefix = '{start}{prefix}{end}'.format(start=color, prefix=prefix, end=Color.END) + else: + real_prefix = prefix + sys.stderr.write('{prefix}: {msg}\n'.format(prefix=real_prefix, msg=msg)) + + +def print_error(msg): + '''Print an error, and terminate''' + print_color(msg, color=Color.RED, prefix='ERROR') + sys.exit(1) + + +def print_warning(msg, fatal=False): + '''Print a warning, and optionally terminate''' + if fatal: + color = Color.RED + prefix = 'ERROR' + else: + color = Color.YELLOW + prefix = 'WARNING' + print_color(msg, color, prefix) + if fatal: + sys.exit(1) + + +def print_info(msg): + '''Print a message''' + print_color(msg, color=Color.GREEN, prefix='INFO') + + +def generate_licensing_comment(outfile): + outfile.write('/* This file is generated by glib-genmarshal, do not ' + 'modify it. This code is licensed under the same license as ' + 'the containing project. Note that it links to GLib, so ' + 'must comply with the LGPL linking clauses. */\n') + + +def generate_header_preamble(outfile, prefix='', std_includes=True, use_pragma=False): + '''Generate the preamble for the marshallers header file''' + generate_licensing_comment(outfile) + + if use_pragma: + outfile.write('#pragma once\n') + outfile.write('\n') + else: + outfile.write('#ifndef __{}_MARSHAL_H__\n'.format(prefix.upper())) + outfile.write('#define __{}_MARSHAL_H__\n'.format(prefix.upper())) + outfile.write('\n') + # Maintain compatibility with the old C-based tool + if std_includes: + outfile.write('#include <glib-object.h>\n') + outfile.write('\n') + + outfile.write('G_BEGIN_DECLS\n') + outfile.write('\n') + + +def generate_header_postamble(outfile, prefix='', use_pragma=False): + '''Generate the postamble for the marshallers header file''' + outfile.write('\n') + outfile.write('G_END_DECLS\n') + + if not use_pragma: + outfile.write('\n') + outfile.write('#endif /* __{}_MARSHAL_H__ */\n'.format(prefix.upper())) + + +def generate_body_preamble(outfile, std_includes=True, include_headers=None, cpp_defines=None, cpp_undefines=None): + '''Generate the preamble for the marshallers source file''' + generate_licensing_comment(outfile) + + for header in (include_headers or []): + outfile.write('#include "{}"\n'.format(header)) + if include_headers: + outfile.write('\n') + + for define in (cpp_defines or []): + s = define.split('=') + symbol = s[0] + value = s[1] if len(s) > 1 else '1' + outfile.write('#define {} {}\n'.format(symbol, value)) + if cpp_defines: + outfile.write('\n') + + for undefine in (cpp_undefines or []): + outfile.write('#undef {}\n'.format(undefine)) + if cpp_undefines: + outfile.write('\n') + + if std_includes: + outfile.write('#include <glib-object.h>\n') + outfile.write('\n') + + outfile.write(GETTERS_STR) + outfile.write('\n\n') + + +# Marshaller arguments, as a dictionary where the key is the token used in +# the source file, and the value is another dictionary with the following +# keys: +# +# - signal: the token used in the marshaller prototype (mandatory) +# - ctype: the C type for the marshaller argument (mandatory) +# - getter: the function used to retrieve the argument from the GValue +# array when invoking the callback (optional) +# - promoted: the C type used by va_arg() to retrieve the argument from +# the va_list when invoking the callback (optional, only used when +# generating va_list marshallers) +# - box: an array of two elements, containing the boxing and unboxing +# functions for the given type (optional, only used when generating +# va_list marshallers) +# - static-check: a boolean value, if the given type should perform +# a static type check before boxing or unboxing the argument (optional, +# only used when generating va_list marshallers) +# - takes-type: a boolean value, if the boxing and unboxing functions +# for the given type require the type (optional, only used when +# generating va_list marshallers) +# - deprecated: whether the token has been deprecated (optional) +# - replaced-by: the token used to replace a deprecated token (optional, +# only used if deprecated is True) +IN_ARGS = { + 'VOID': { + 'signal': 'VOID', + 'ctype': 'void', + }, + 'BOOLEAN': { + 'signal': 'BOOLEAN', + 'ctype': 'gboolean', + 'getter': 'g_marshal_value_peek_boolean', + }, + 'CHAR': { + 'signal': 'CHAR', + 'ctype': 'gchar', + 'promoted': 'gint', + 'getter': 'g_marshal_value_peek_char', + }, + 'UCHAR': { + 'signal': 'UCHAR', + 'ctype': 'guchar', + 'promoted': 'guint', + 'getter': 'g_marshal_value_peek_uchar', + }, + 'INT': { + 'signal': 'INT', + 'ctype': 'gint', + 'getter': 'g_marshal_value_peek_int', + }, + 'UINT': { + 'signal': 'UINT', + 'ctype': 'guint', + 'getter': 'g_marshal_value_peek_uint', + }, + 'LONG': { + 'signal': 'LONG', + 'ctype': 'glong', + 'getter': 'g_marshal_value_peek_long', + }, + 'ULONG': { + 'signal': 'ULONG', + 'ctype': 'gulong', + 'getter': 'g_marshal_value_peek_ulong', + }, + 'INT64': { + 'signal': 'INT64', + 'ctype': 'gint64', + 'getter': 'g_marshal_value_peek_int64', + }, + 'UINT64': { + 'signal': 'UINT64', + 'ctype': 'guint64', + 'getter': 'g_marshal_value_peek_uint64', + }, + 'ENUM': { + 'signal': 'ENUM', + 'ctype': 'gint', + 'getter': 'g_marshal_value_peek_enum', + }, + 'FLAGS': { + 'signal': 'FLAGS', + 'ctype': 'guint', + 'getter': 'g_marshal_value_peek_flags', + }, + 'FLOAT': { + 'signal': 'FLOAT', + 'ctype': 'gfloat', + 'promoted': 'gdouble', + 'getter': 'g_marshal_value_peek_float', + }, + 'DOUBLE': { + 'signal': 'DOUBLE', + 'ctype': 'gdouble', + 'getter': 'g_marshal_value_peek_double', + }, + 'STRING': { + 'signal': 'STRING', + 'ctype': 'gpointer', + 'getter': 'g_marshal_value_peek_string', + 'box': ['g_strdup', 'g_free'], + 'static-check': True, + }, + 'PARAM': { + 'signal': 'PARAM', + 'ctype': 'gpointer', + 'getter': 'g_marshal_value_peek_param', + 'box': ['g_param_spec_ref', 'g_param_spec_unref'], + 'static-check': True, + }, + 'BOXED': { + 'signal': 'BOXED', + 'ctype': 'gpointer', + 'getter': 'g_marshal_value_peek_boxed', + 'box': ['g_boxed_copy', 'g_boxed_free'], + 'static-check': True, + 'takes-type': True, + }, + 'POINTER': { + 'signal': 'POINTER', + 'ctype': 'gpointer', + 'getter': 'g_marshal_value_peek_pointer', + }, + 'OBJECT': { + 'signal': 'OBJECT', + 'ctype': 'gpointer', + 'getter': 'g_marshal_value_peek_object', + 'box': ['g_object_ref', 'g_object_unref'], + }, + 'VARIANT': { + 'signal': 'VARIANT', + 'ctype': 'gpointer', + 'getter': 'g_marshal_value_peek_variant', + 'box': ['g_variant_ref_sink', 'g_variant_unref'], + 'static-check': True, + 'takes-type': False, + }, + + # Deprecated tokens + 'NONE': { + 'signal': 'VOID', + 'ctype': 'void', + 'deprecated': True, + 'replaced_by': 'VOID' + }, + 'BOOL': { + 'signal': 'BOOLEAN', + 'ctype': 'gboolean', + 'getter': 'g_marshal_value_peek_boolean', + 'deprecated': True, + 'replaced_by': 'BOOLEAN' + } +} + + +# Marshaller return values, as a dictionary where the key is the token used +# in the source file, and the value is another dictionary with the following +# keys: +# +# - signal: the token used in the marshaller prototype (mandatory) +# - ctype: the C type for the marshaller argument (mandatory) +# - setter: the function used to set the return value of the callback +# into a GValue (optional) +# - deprecated: whether the token has been deprecated (optional) +# - replaced-by: the token used to replace a deprecated token (optional, +# only used if deprecated is True) +OUT_ARGS = { + 'VOID': { + 'signal': 'VOID', + 'ctype': 'void', + }, + 'BOOLEAN': { + 'signal': 'BOOLEAN', + 'ctype': 'gboolean', + 'setter': 'g_value_set_boolean', + }, + 'CHAR': { + 'signal': 'CHAR', + 'ctype': 'gchar', + 'setter': 'g_value_set_char', + }, + 'UCHAR': { + 'signal': 'UCHAR', + 'ctype': 'guchar', + 'setter': 'g_value_set_uchar', + }, + 'INT': { + 'signal': 'INT', + 'ctype': 'gint', + 'setter': 'g_value_set_int', + }, + 'UINT': { + 'signal': 'UINT', + 'ctype': 'guint', + 'setter': 'g_value_set_uint', + }, + 'LONG': { + 'signal': 'LONG', + 'ctype': 'glong', + 'setter': 'g_value_set_long', + }, + 'ULONG': { + 'signal': 'ULONG', + 'ctype': 'gulong', + 'setter': 'g_value_set_ulong', + }, + 'INT64': { + 'signal': 'INT64', + 'ctype': 'gint64', + 'setter': 'g_value_set_int64', + }, + 'UINT64': { + 'signal': 'UINT64', + 'ctype': 'guint64', + 'setter': 'g_value_set_uint64', + }, + 'ENUM': { + 'signal': 'ENUM', + 'ctype': 'gint', + 'setter': 'g_value_set_enum', + }, + 'FLAGS': { + 'signal': 'FLAGS', + 'ctype': 'guint', + 'setter': 'g_value_set_flags', + }, + 'FLOAT': { + 'signal': 'FLOAT', + 'ctype': 'gfloat', + 'setter': 'g_value_set_float', + }, + 'DOUBLE': { + 'signal': 'DOUBLE', + 'ctype': 'gdouble', + 'setter': 'g_value_set_double', + }, + 'STRING': { + 'signal': 'STRING', + 'ctype': 'gchar*', + 'setter': 'g_value_take_string', + }, + 'PARAM': { + 'signal': 'PARAM', + 'ctype': 'GParamSpec*', + 'setter': 'g_value_take_param', + }, + 'BOXED': { + 'signal': 'BOXED', + 'ctype': 'gpointer', + 'setter': 'g_value_take_boxed', + }, + 'POINTER': { + 'signal': 'POINTER', + 'ctype': 'gpointer', + 'setter': 'g_value_set_pointer', + }, + 'OBJECT': { + 'signal': 'OBJECT', + 'ctype': 'GObject*', + 'setter': 'g_value_take_object', + }, + 'VARIANT': { + 'signal': 'VARIANT', + 'ctype': 'GVariant*', + 'setter': 'g_value_take_variant', + }, + + # Deprecated tokens + 'NONE': { + 'signal': 'VOID', + 'ctype': 'void', + 'setter': None, + 'deprecated': True, + 'replaced_by': 'VOID', + }, + 'BOOL': { + 'signal': 'BOOLEAN', + 'ctype': 'gboolean', + 'setter': 'g_value_set_boolean', + 'deprecated': True, + 'replaced_by': 'BOOLEAN', + }, +} + + +def check_args(retval, params, fatal_warnings=False): + '''Check the @retval and @params tokens for invalid and deprecated symbols.''' + if retval not in OUT_ARGS: + print_error('Unknown return value type "{}"'.format(retval)) + + if OUT_ARGS[retval].get('deprecated', False): + replaced_by = OUT_ARGS[retval]['replaced_by'] + print_warning(DEPRECATED_MSG_STR.format(retval, replaced_by), fatal_warnings) + + for param in params: + if param not in IN_ARGS: + print_error('Unknown parameter type "{}"'.format(param)) + else: + if IN_ARGS[param].get('deprecated', False): + replaced_by = IN_ARGS[param]['replaced_by'] + print_warning(DEPRECATED_MSG_STR.format(param, replaced_by), fatal_warnings) + + +def indent(text, level=0, fill=' '): + '''Indent @text by @level columns, using the @fill character''' + return ''.join([fill for x in range(level)]) + text + + +# pylint: disable=too-few-public-methods +class Visibility: + '''Symbol visibility options''' + NONE = 0 + INTERNAL = 1 + EXTERN = 2 + + +def generate_marshaller_name(prefix, retval, params, replace_deprecated=True): + '''Generate a marshaller name for the given @prefix, @retval, and @params. + If @replace_deprecated is True, the generated name will replace deprecated + tokens.''' + if replace_deprecated: + real_retval = OUT_ARGS[retval]['signal'] + real_params = [] + for param in params: + real_params.append(IN_ARGS[param]['signal']) + else: + real_retval = retval + real_params = params + return '{prefix}_{retval}__{args}'.format(prefix=prefix, + retval=real_retval, + args='_'.join(real_params)) + + +def generate_prototype(retval, params, + prefix='g_cclosure_user_marshal', + visibility=Visibility.NONE, + va_marshal=False): + '''Generate a marshaller declaration with the given @visibility. If @va_marshal + is True, the marshaller will use variadic arguments in place of a GValue array.''' + signature = [] + + if visibility == Visibility.INTERNAL: + signature += ['G_GNUC_INTERNAL'] + elif visibility == Visibility.EXTERN: + signature += ['extern'] + + function_name = generate_marshaller_name(prefix, retval, params) + + if not va_marshal: + signature += ['void ' + function_name + ' (GClosure *closure,'] + width = len('void ') + len(function_name) + 2 + + signature += [indent('GValue *return_value,', level=width, fill=' ')] + signature += [indent('guint n_param_values,', level=width, fill=' ')] + signature += [indent('const GValue *param_values,', level=width, fill=' ')] + signature += [indent('gpointer invocation_hint,', level=width, fill=' ')] + signature += [indent('gpointer marshal_data);', level=width, fill=' ')] + else: + signature += ['void ' + function_name + 'v (GClosure *closure,'] + width = len('void ') + len(function_name) + 3 + + signature += [indent('GValue *return_value,', level=width, fill=' ')] + signature += [indent('gpointer instance,', level=width, fill=' ')] + signature += [indent('va_list args,', level=width, fill=' ')] + signature += [indent('gpointer marshal_data,', level=width, fill=' ')] + signature += [indent('int n_params,', level=width, fill=' ')] + signature += [indent('GType *param_types);', level=width, fill=' ')] + + return signature + + +# pylint: disable=too-many-statements, too-many-locals, too-many-branches +def generate_body(retval, params, prefix, va_marshal=False): + '''Generate a marshaller definition. If @va_marshal is True, the marshaller + will use va_list and variadic arguments in place of a GValue array.''' + retval_setter = OUT_ARGS[retval].get('setter', None) + # If there's no return value then we can mark the retval argument as unused + # and get a minor optimisation, as well as avoid a compiler warning + if not retval_setter: + unused = ' G_GNUC_UNUSED' + else: + unused = '' + + body = ['void'] + + function_name = generate_marshaller_name(prefix, retval, params) + + if not va_marshal: + body += [function_name + ' (GClosure *closure,'] + width = len(function_name) + 2 + + body += [indent('GValue *return_value{},'.format(unused), level=width, fill=' ')] + body += [indent('guint n_param_values,', level=width, fill=' ')] + body += [indent('const GValue *param_values,', level=width, fill=' ')] + body += [indent('gpointer invocation_hint G_GNUC_UNUSED,', level=width, fill=' ')] + body += [indent('gpointer marshal_data)', level=width, fill=' ')] + else: + body += [function_name + 'v (GClosure *closure,'] + width = len(function_name) + 3 + + body += [indent('GValue *return_value{},'.format(unused), level=width, fill=' ')] + body += [indent('gpointer instance,', level=width, fill=' ')] + body += [indent('va_list args,', level=width, fill=' ')] + body += [indent('gpointer marshal_data,', level=width, fill=' ')] + body += [indent('int n_params,', level=width, fill=' ')] + body += [indent('GType *param_types)', level=width, fill=' ')] + + # Filter the arguments that have a getter + get_args = [x for x in params if IN_ARGS[x].get('getter', None) is not None] + + body += ['{'] + + # Generate the type of the marshaller function + typedef_marshal = generate_marshaller_name('GMarshalFunc', retval, params) + + typedef = ' typedef {ctype} (*{func_name}) ('.format(ctype=OUT_ARGS[retval]['ctype'], + func_name=typedef_marshal) + pad = len(typedef) + typedef += 'gpointer data1,' + body += [typedef] + + for idx, in_arg in enumerate(get_args): + body += [indent('{} arg{:d},'.format(IN_ARGS[in_arg]['ctype'], idx + 1), level=pad)] + + body += [indent('gpointer data2);', level=pad)] + + # Variable declarations + body += [' GCClosure *cc = (GCClosure *) closure;'] + body += [' gpointer data1, data2;'] + body += [' {} callback;'.format(typedef_marshal)] + + if retval_setter: + body += [' {} v_return;'.format(OUT_ARGS[retval]['ctype'])] + + if va_marshal: + for idx, arg in enumerate(get_args): + body += [' {} arg{:d};'.format(IN_ARGS[arg]['ctype'], idx)] + + if get_args: + body += [' va_list args_copy;'] + body += [''] + + body += [' va_copy (args_copy, args);'] + + for idx, arg in enumerate(get_args): + ctype = IN_ARGS[arg]['ctype'] + promoted_ctype = IN_ARGS[arg].get('promoted', ctype) + body += [VA_ARG_STR.format(idx, ctype, promoted_ctype)] + if IN_ARGS[arg].get('box', None): + box_func = IN_ARGS[arg]['box'][0] + if IN_ARGS[arg].get('static-check', False): + static_check = STATIC_CHECK_STR.format(idx) + else: + static_check = '' + arg_check = 'arg{:d} != NULL'.format(idx) + body += [' if ({}{})'.format(static_check, arg_check)] + if IN_ARGS[arg].get('takes-type', False): + body += [BOX_TYPED_STR.format(idx=idx, box_func=box_func)] + else: + body += [BOX_UNTYPED_STR.format(idx=idx, box_func=box_func)] + + body += [' va_end (args_copy);'] + + body += [''] + + # Preconditions check + if retval_setter: + body += [' g_return_if_fail (return_value != NULL);'] + + if not va_marshal: + body += [' g_return_if_fail (n_param_values == {:d});'.format(len(get_args) + 1)] + + body += [''] + + # Marshal instance, data, and callback set up + body += [' if (G_CCLOSURE_SWAP_DATA (closure))'] + body += [' {'] + body += [' data1 = closure->data;'] + if va_marshal: + body += [' data2 = instance;'] + else: + body += [' data2 = g_value_peek_pointer (param_values + 0);'] + body += [' }'] + body += [' else'] + body += [' {'] + if va_marshal: + body += [' data1 = instance;'] + else: + body += [' data1 = g_value_peek_pointer (param_values + 0);'] + body += [' data2 = closure->data;'] + body += [' }'] + # pylint: disable=line-too-long + body += [' callback = ({}) (marshal_data ? marshal_data : cc->callback);'.format(typedef_marshal)] + body += [''] + + # Marshal callback action + if retval_setter: + callback = ' {} callback ('.format(' v_return =') + else: + callback = ' callback (' + + pad = len(callback) + body += [callback + 'data1,'] + + if va_marshal: + for idx, arg in enumerate(get_args): + body += [indent('arg{:d},'.format(idx), level=pad)] + else: + for idx, arg in enumerate(get_args): + arg_getter = IN_ARGS[arg]['getter'] + body += [indent('{} (param_values + {:d}),'.format(arg_getter, idx + 1), level=pad)] + + body += [indent('data2);', level=pad)] + + if va_marshal: + boxed_args = [x for x in get_args if IN_ARGS[x].get('box', None) is not None] + if not boxed_args: + body += [''] + else: + for idx, arg in enumerate(get_args): + if not IN_ARGS[arg].get('box', None): + continue + unbox_func = IN_ARGS[arg]['box'][1] + if IN_ARGS[arg].get('static-check', False): + static_check = STATIC_CHECK_STR.format(idx) + else: + static_check = '' + arg_check = 'arg{:d} != NULL'.format(idx) + body += [' if ({}{})'.format(static_check, arg_check)] + if IN_ARGS[arg].get('takes-type', False): + body += [UNBOX_TYPED_STR.format(idx=idx, unbox_func=unbox_func)] + else: + body += [UNBOX_UNTYPED_STR.format(idx=idx, unbox_func=unbox_func)] + + if retval_setter: + body += [''] + body += [' {} (return_value, v_return);'.format(retval_setter)] + + body += ['}'] + + return body + + +def generate_marshaller_alias(outfile, marshaller, real_marshaller, + include_va=False, + source_location=None): + '''Generate an alias between @marshaller and @real_marshaller, including + an optional alias for va_list marshallers''' + if source_location: + outfile.write('/* {} */\n'.format(source_location)) + + outfile.write('#define {}\t{}\n'.format(marshaller, real_marshaller)) + + if include_va: + outfile.write('#define {}v\t{}v\n'.format(marshaller, real_marshaller)) + + outfile.write('\n') + + +def generate_marshallers_header(outfile, retval, params, + prefix='g_cclosure_user_marshal', + internal=False, + include_va=False, source_location=None): + '''Generate a declaration for a marshaller function, to be used in the header, + with the given @retval, @params, and @prefix. An optional va_list marshaller + for the same arguments is also generated. The generated buffer is written to + the @outfile stream object.''' + if source_location: + outfile.write('/* {} */\n'.format(source_location)) + + if internal: + visibility = Visibility.INTERNAL + else: + visibility = Visibility.EXTERN + + signature = generate_prototype(retval, params, prefix, visibility, False) + if include_va: + signature += generate_prototype(retval, params, prefix, visibility, True) + signature += [''] + + outfile.write('\n'.join(signature)) + outfile.write('\n') + + +def generate_marshallers_body(outfile, retval, params, + prefix='g_cclosure_user_marshal', + include_prototype=True, + internal=False, + include_va=False, source_location=None): + '''Generate a definition for a marshaller function, to be used in the source, + with the given @retval, @params, and @prefix. An optional va_list marshaller + for the same arguments is also generated. The generated buffer is written to + the @outfile stream object.''' + if source_location: + outfile.write('/* {} */\n'.format(source_location)) + + if include_prototype: + # Declaration visibility + if internal: + decl_visibility = Visibility.INTERNAL + else: + decl_visibility = Visibility.EXTERN + proto = ['/* Prototype for -Wmissing-prototypes */'] + # Add C++ guards in case somebody compiles the generated code + # with a C++ compiler + proto += ['G_BEGIN_DECLS'] + proto += generate_prototype(retval, params, prefix, decl_visibility, False) + proto += ['G_END_DECLS'] + outfile.write('\n'.join(proto)) + outfile.write('\n') + + body = generate_body(retval, params, prefix, False) + outfile.write('\n'.join(body)) + outfile.write('\n\n') + + if include_va: + if include_prototype: + # Declaration visibility + if internal: + decl_visibility = Visibility.INTERNAL + else: + decl_visibility = Visibility.EXTERN + proto = ['/* Prototype for -Wmissing-prototypes */'] + # Add C++ guards here as well + proto += ['G_BEGIN_DECLS'] + proto += generate_prototype(retval, params, prefix, decl_visibility, True) + proto += ['G_END_DECLS'] + outfile.write('\n'.join(proto)) + outfile.write('\n') + + body = generate_body(retval, params, prefix, True) + outfile.write('\n'.join(body)) + outfile.write('\n\n') + + +def parse_args(): + arg_parser = argparse.ArgumentParser(description='Generate signal marshallers for GObject') + arg_parser.add_argument('--prefix', metavar='STRING', + default='g_cclosure_user_marshal', + help='Specify marshaller prefix') + arg_parser.add_argument('--output', metavar='FILE', + type=argparse.FileType('w'), + default=sys.stdout, + help='Write output into the specified file') + arg_parser.add_argument('--skip-source', + action='store_true', + help='Skip source location comments') + arg_parser.add_argument('--internal', + action='store_true', + help='Mark generated functions as internal') + arg_parser.add_argument('--valist-marshallers', + action='store_true', + help='Generate va_list marshallers') + arg_parser.add_argument('-v', '--version', + action='store_true', + dest='show_version', + help='Print version information, and exit') + arg_parser.add_argument('--g-fatal-warnings', + action='store_true', + dest='fatal_warnings', + help='Make warnings fatal') + arg_parser.add_argument('--include-header', metavar='HEADER', nargs='?', + action='append', + dest='include_headers', + help='Include the specified header in the body') + arg_parser.add_argument('--pragma-once', + action='store_true', + help='Use "pragma once" as the inclusion guard') + arg_parser.add_argument('-D', + action='append', + dest='cpp_defines', + default=[], + help='Pre-processor define') + arg_parser.add_argument('-U', + action='append', + dest='cpp_undefines', + default=[], + help='Pre-processor undefine') + arg_parser.add_argument('files', metavar='FILE', nargs='*', + type=argparse.FileType('r'), + help='Files with lists of marshallers to generate, ' + + 'or "-" for standard input') + arg_parser.add_argument('--prototypes', + action='store_true', + help='Generate the marshallers prototype in the C code') + arg_parser.add_argument('--header', + action='store_true', + help='Generate C headers') + arg_parser.add_argument('--body', + action='store_true', + help='Generate C code') + + group = arg_parser.add_mutually_exclusive_group() + group.add_argument('--stdinc', + action='store_true', + dest='stdinc', default=True, + help='Include standard marshallers') + group.add_argument('--nostdinc', + action='store_false', + dest='stdinc', default=True, + help='Use standard marshallers') + + group = arg_parser.add_mutually_exclusive_group() + group.add_argument('--quiet', + action='store_true', + help='Only print warnings and errors') + group.add_argument('--verbose', + action='store_true', + help='Be verbose, and include debugging information') + + args = arg_parser.parse_args() + + if args.show_version: + print(VERSION_STR) + sys.exit(0) + + return args + + +def generate(args): + # Backward compatibility hack; some projects use both arguments to + # generate the marshallers prototype in the C source, even though + # it's not really a supported use case. We keep this behaviour by + # forcing the --prototypes and --body arguments instead. We make this + # warning non-fatal even with --g-fatal-warnings, as it's a deprecation + compatibility_mode = False + if args.header and args.body: + print_warning('Using --header and --body at the same time is deprecated; ' + + 'use --body --prototypes instead', False) + args.prototypes = True + args.header = False + compatibility_mode = True + + if args.header: + generate_header_preamble(args.output, + prefix=args.prefix, + std_includes=args.stdinc, + use_pragma=args.pragma_once) + elif args.body: + generate_body_preamble(args.output, + std_includes=args.stdinc, + include_headers=args.include_headers, + cpp_defines=args.cpp_defines, + cpp_undefines=args.cpp_undefines) + + seen_marshallers = set() + + for infile in args.files: + if not args.quiet: + print_info('Reading {}...'.format(infile.name)) + + line_count = 0 + for line in infile: + line_count += 1 + + if line == '\n' or line.startswith('#'): + continue + + matches = re.match(r'^([A-Z0-9]+)\s?:\s?([A-Z0-9,\s]+)$', line.strip()) + if not matches or len(matches.groups()) != 2: + print_warning('Invalid entry: "{}"'.format(line.strip()), args.fatal_warnings) + continue + + if not args.skip_source: + location = '{} ({}:{:d})'.format(line.strip(), infile.name, line_count) + else: + location = None + + retval = matches.group(1).strip() + params = [x.strip() for x in matches.group(2).split(',')] + check_args(retval, params, args.fatal_warnings) + + raw_marshaller = generate_marshaller_name(args.prefix, retval, params, False) + if raw_marshaller in seen_marshallers: + if args.verbose: + print_info('Skipping repeated marshaller {}'.format(line.strip())) + continue + + if args.header: + if args.verbose: + print_info('Generating declaration for {}'.format(line.strip())) + generate_std_alias = False + if args.stdinc: + std_marshaller = generate_marshaller_name(STD_PREFIX, retval, params) + if std_marshaller in GOBJECT_MARSHALLERS: + if args.verbose: + print_info('Skipping default marshaller {}'.format(line.strip())) + generate_std_alias = True + + marshaller = generate_marshaller_name(args.prefix, retval, params) + if generate_std_alias: + generate_marshaller_alias(args.output, marshaller, std_marshaller, + source_location=location, + include_va=args.valist_marshallers) + else: + generate_marshallers_header(args.output, retval, params, + prefix=args.prefix, + internal=args.internal, + include_va=args.valist_marshallers, + source_location=location) + # If the marshaller is defined using a deprecated token, we want to maintain + # compatibility and generate an alias for the old name pointing to the new + # one + if marshaller != raw_marshaller: + if args.verbose: + print_info('Generating alias for deprecated tokens') + generate_marshaller_alias(args.output, raw_marshaller, marshaller, + include_va=args.valist_marshallers) + elif args.body: + if args.verbose: + print_info('Generating definition for {}'.format(line.strip())) + generate_std_alias = False + if args.stdinc: + std_marshaller = generate_marshaller_name(STD_PREFIX, retval, params) + if std_marshaller in GOBJECT_MARSHALLERS: + if args.verbose: + print_info('Skipping default marshaller {}'.format(line.strip())) + generate_std_alias = True + marshaller = generate_marshaller_name(args.prefix, retval, params) + if generate_std_alias: + # We need to generate the alias if we are in compatibility mode + if compatibility_mode: + generate_marshaller_alias(args.output, marshaller, std_marshaller, + source_location=location, + include_va=args.valist_marshallers) + else: + generate_marshallers_body(args.output, retval, params, + prefix=args.prefix, + internal=args.internal, + include_prototype=args.prototypes, + include_va=args.valist_marshallers, + source_location=location) + if compatibility_mode and marshaller != raw_marshaller: + if args.verbose: + print_info('Generating alias for deprecated tokens') + generate_marshaller_alias(args.output, raw_marshaller, marshaller, + include_va=args.valist_marshallers) + + seen_marshallers.add(raw_marshaller) + + if args.header: + generate_header_postamble(args.output, prefix=args.prefix, use_pragma=args.pragma_once) + + +if __name__ == '__main__': + args = parse_args() + + with args.output: + generate(args) diff --git a/os/windows/gobject/glib-mkenums b/os/windows/gobject/glib-mkenums new file mode 100644 index 000000000..f3214553b --- /dev/null +++ b/os/windows/gobject/glib-mkenums @@ -0,0 +1,810 @@ +#!/usr/bin/env python3 + +# If the code below looks horrible and unpythonic, do not panic. +# +# It is. +# +# This is a manual conversion from the original Perl script to +# Python. Improvements are welcome. +# +from __future__ import print_function, unicode_literals + +import argparse +import os +import re +import sys +import tempfile +import io +import errno +import codecs +import locale + +# Non-english locale systems might complain to unrecognized character +sys.stdout = io.TextIOWrapper(sys.stdout.detach(), encoding='utf-8') + +VERSION_STR = '''glib-mkenums version 2.79.0 +glib-mkenums comes with ABSOLUTELY NO WARRANTY. +You may redistribute copies of glib-mkenums under the terms of +the GNU General Public License which can be found in the +GLib source package. Sources, examples and contact +information are available at http://www.gtk.org''' + +# pylint: disable=too-few-public-methods +class Color: + '''ANSI Terminal colors''' + GREEN = '\033[1;32m' + BLUE = '\033[1;34m' + YELLOW = '\033[1;33m' + RED = '\033[1;31m' + END = '\033[0m' + + +def print_color(msg, color=Color.END, prefix='MESSAGE'): + '''Print a string with a color prefix''' + if os.isatty(sys.stderr.fileno()): + real_prefix = '{start}{prefix}{end}'.format(start=color, prefix=prefix, end=Color.END) + else: + real_prefix = prefix + print('{prefix}: {msg}'.format(prefix=real_prefix, msg=msg), file=sys.stderr) + + +def print_error(msg): + '''Print an error, and terminate''' + print_color(msg, color=Color.RED, prefix='ERROR') + sys.exit(1) + + +def print_warning(msg, fatal=False): + '''Print a warning, and optionally terminate''' + if fatal: + color = Color.RED + prefix = 'ERROR' + else: + color = Color.YELLOW + prefix = 'WARNING' + print_color(msg, color, prefix) + if fatal: + sys.exit(1) + + +def print_info(msg): + '''Print a message''' + print_color(msg, color=Color.GREEN, prefix='INFO') + + +def get_rspfile_args(rspfile): + ''' + Response files are useful on Windows where there is a command-line character + limit of 8191 because when passing sources as arguments to glib-mkenums this + limit can be exceeded in large codebases. + + There is no specification for response files and each tool that supports it + generally writes them out in slightly different ways, but some sources are: + https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-response-files + https://docs.microsoft.com/en-us/windows/desktop/midl/the-response-file-command + ''' + import shlex + if not os.path.isfile(rspfile): + sys.exit('Response file {!r} does not exist'.format(rspfile)) + try: + with open(rspfile, 'r') as f: + cmdline = f.read() + except OSError as e: + sys.exit('Response file {!r} could not be read: {}' + .format(rspfile, e.strerror)) + return shlex.split(cmdline) + + +def write_output(output): + global output_stream + print(output, file=output_stream) + + +# Python 2 defaults to ASCII in case stdout is redirected. +# This should make it match Python 3, which uses the locale encoding. +if sys.stdout.encoding is None: + output_stream = codecs.getwriter( + locale.getpreferredencoding())(sys.stdout) +else: + output_stream = sys.stdout + + +# Some source files aren't UTF-8 and the old perl version didn't care. +# Replace invalid data with a replacement character to keep things working. +# https://bugzilla.gnome.org/show_bug.cgi?id=785113#c20 +def replace_and_warn(err): + # 7 characters of context either side of the offending character + print_warning('UnicodeWarning: {} at {} ({})'.format( + err.reason, err.start, + err.object[err.start - 7:err.end + 7])) + return ('?', err.end) + +codecs.register_error('replace_and_warn', replace_and_warn) + + +# glib-mkenums.py +# Information about the current enumeration +flags = None # Is enumeration a bitmask? +option_underscore_name = '' # Overridden underscore variant of the enum name + # for example to fix the cases we don't get the + # mixed-case -> underscorized transform right. +option_lowercase_name = '' # DEPRECATED. A lower case name to use as part + # of the *_get_type() function, instead of the + # one that we guess. For instance, when an enum + # uses abnormal capitalization and we can not + # guess where to put the underscores. +option_since = '' # User provided version info for the enum. +seenbitshift = 0 # Have we seen bitshift operators? +seenprivate = False # Have we seen a private option? +enum_prefix = None # Prefix for this enumeration +enumname = '' # Name for this enumeration +enumshort = '' # $enumname without prefix +enumname_prefix = '' # prefix of $enumname +enumindex = 0 # Global enum counter +firstenum = 1 # Is this the first enumeration per file? +entries = [] # [ name, val ] for each entry +c_namespace = {} # C symbols namespace. + +output = '' # Filename to write result into + +def parse_trigraph(opts): + result = {} + for opt in re.findall(r'(?:[^\s,"]|"(?:\\.|[^"])*")+', opts): + opt = re.sub(r'^\s*', '', opt) + opt = re.sub(r'\s*$', '', opt) + m = re.search(r'(\w+)(?:=(.+))?', opt) + assert m is not None + groups = m.groups() + key = groups[0] + if len(groups) > 1: + val = groups[1] + else: + val = 1 + result[key] = val.strip('"') if val is not None else None + return result + +def parse_entries(file, file_name): + global entries, enumindex, enumname, seenbitshift, seenprivate, flags + looking_for_name = False + + while True: + line = file.readline() + if not line: + break + + line = line.strip() + + # read lines until we have no open comments + while re.search(r'/\*([^*]|\*(?!/))*$', line): + line += file.readline() + + # strip comments w/o options + line = re.sub(r'''/\*(?!<) + ([^*]+|\*(?!/))* + \*/''', '', line, flags=re.X) + + line = line.rstrip() + + # skip empty lines + if len(line.strip()) == 0: + continue + + if looking_for_name: + m = re.match(r'\s*(\w+)', line) + if m: + enumname = m.group(1) + return True + + # Handle include files + m = re.match(r'\#include\s*<([^>]*)>', line) + if m: + newfilename = os.path.join("..", m.group(1)) + newfile = io.open(newfilename, encoding="utf-8", + errors="replace_and_warn") + + if not parse_entries(newfile, newfilename): + return False + else: + continue + + m = re.match(r'\s*\}\s*(\w+)', line) + if m: + enumname = m.group(1) + enumindex += 1 + return 1 + + m = re.match(r'\s*\}', line) + if m: + enumindex += 1 + looking_for_name = True + continue + + m = re.match(r'''\s* + (\w+)\s* # name + (\s+[A-Z]+_(?:AVAILABLE|DEPRECATED)_ENUMERATOR_IN_[0-9_]+(?:_FOR\s*\(\s*\w+\s*\))?\s*)? # availability + (?:=( # value + \s*\w+\s*\(.*\)\s* # macro with multiple args + | # OR + (?:[^,/]|/(?!\*))* # anything but a comma or comment + ))?,?\s* + (?:/\*< # options + (([^*]|\*(?!/))*) + >\s*\*/)?,? + \s*$''', line, flags=re.X) + if m: + groups = m.groups() + name = groups[0] + availability = None + value = None + options = None + if len(groups) > 1: + availability = groups[1] + if len(groups) > 2: + value = groups[2] + if len(groups) > 3: + options = groups[3] + if flags is None and value is not None and '<<' in value: + seenbitshift = 1 + + if options is not None: + options = parse_trigraph(options) + if 'skip' not in options: + entries.append((name, value, seenprivate, options.get('nick'))) + else: + entries.append((name, value, seenprivate)) + else: + m = re.match(r'''\s* + /\*< (([^*]|\*(?!/))*) >\s*\*/ + \s*$''', line, flags=re.X) + if m: + options = m.groups()[0] + if options is not None: + options = parse_trigraph(options) + if 'private' in options: + seenprivate = True + continue + if 'public' in options: + seenprivate = False + continue + if re.match(r's*\#', line): + pass + else: + print_warning('Failed to parse "{}" in {}'.format(line, file_name)) + return False + +help_epilog = '''Production text substitutions: + \u0040EnumName\u0040 PrefixTheXEnum + \u0040enum_name\u0040 prefix_the_xenum + \u0040ENUMNAME\u0040 PREFIX_THE_XENUM + \u0040ENUMSHORT\u0040 THE_XENUM + \u0040ENUMPREFIX\u0040 PREFIX + \u0040enumsince\u0040 the user-provided since value given + \u0040VALUENAME\u0040 PREFIX_THE_XVALUE + \u0040valuenick\u0040 the-xvalue + \u0040valuenum\u0040 the integer value (limited support, Since: 2.26) + \u0040type\u0040 either enum or flags + \u0040Type\u0040 either Enum or Flags + \u0040TYPE\u0040 either ENUM or FLAGS + \u0040filename\u0040 name of current input file + \u0040basename\u0040 base name of the current input file (Since: 2.22) +''' + + +# production variables: +idprefix = "" # "G", "Gtk", etc +symprefix = "" # "g", "gtk", etc, if not just lc($idprefix) +fhead = "" # output file header +fprod = "" # per input file production +ftail = "" # output file trailer +eprod = "" # per enum text (produced prior to value itarations) +vhead = "" # value header, produced before iterating over enum values +vprod = "" # value text, produced for each enum value +vtail = "" # value tail, produced after iterating over enum values +comment_tmpl = "" # comment template + +def read_template_file(file): + global idprefix, symprefix, fhead, fprod, ftail, eprod, vhead, vprod, vtail, comment_tmpl + tmpl = {'file-header': fhead, + 'file-production': fprod, + 'file-tail': ftail, + 'enumeration-production': eprod, + 'value-header': vhead, + 'value-production': vprod, + 'value-tail': vtail, + 'comment': comment_tmpl, + } + in_ = 'junk' + + ifile = io.open(file, encoding="utf-8", errors="replace_and_warn") + for line in ifile: + m = re.match(r'\/\*\*\*\s+(BEGIN|END)\s+([\w-]+)\s+\*\*\*\/', line) + if m: + if in_ == 'junk' and m.group(1) == 'BEGIN' and m.group(2) in tmpl: + in_ = m.group(2) + continue + elif in_ == m.group(2) and m.group(1) == 'END' and m.group(2) in tmpl: + in_ = 'junk' + continue + else: + sys.exit("Malformed template file " + file) + + if in_ != 'junk': + tmpl[in_] += line + + if in_ != 'junk': + sys.exit("Malformed template file " + file) + + fhead = tmpl['file-header'] + fprod = tmpl['file-production'] + ftail = tmpl['file-tail'] + eprod = tmpl['enumeration-production'] + vhead = tmpl['value-header'] + vprod = tmpl['value-production'] + vtail = tmpl['value-tail'] + comment_tmpl = tmpl['comment'] + +parser = argparse.ArgumentParser(epilog=help_epilog, + formatter_class=argparse.RawDescriptionHelpFormatter) + +parser.add_argument('--identifier-prefix', default='', dest='idprefix', + help='Identifier prefix') +parser.add_argument('--symbol-prefix', default='', dest='symprefix', + help='Symbol prefix') +parser.add_argument('--fhead', default=[], dest='fhead', action='append', + help='Output file header') +parser.add_argument('--ftail', default=[], dest='ftail', action='append', + help='Output file footer') +parser.add_argument('--fprod', default=[], dest='fprod', action='append', + help='Put out TEXT every time a new input file is being processed.') +parser.add_argument('--eprod', default=[], dest='eprod', action='append', + help='Per enum text, produced prior to value iterations') +parser.add_argument('--vhead', default=[], dest='vhead', action='append', + help='Value header, produced before iterating over enum values') +parser.add_argument('--vprod', default=[], dest='vprod', action='append', + help='Value text, produced for each enum value.') +parser.add_argument('--vtail', default=[], dest='vtail', action='append', + help='Value tail, produced after iterating over enum values') +parser.add_argument('--comments', default='', dest='comment_tmpl', + help='Comment structure') +parser.add_argument('--template', default='', dest='template', + help='Template file') +parser.add_argument('--output', default=None, dest='output') +parser.add_argument('--version', '-v', default=False, action='store_true', dest='version', + help='Print version information') +parser.add_argument('args', nargs='*', + help='One or more input files, or a single argument @rspfile_path ' + 'pointing to a file that contains the actual arguments') + +# Support reading an rspfile of the form @filename which contains the args +# to be parsed +if len(sys.argv) == 2 and sys.argv[1].startswith('@'): + args = get_rspfile_args(sys.argv[1][1:]) +else: + args = sys.argv[1:] + +options = parser.parse_args(args) + +if options.version: + print(VERSION_STR) + sys.exit(0) + +def unescape_cmdline_args(arg): + arg = arg.replace('\\n', '\n') + arg = arg.replace('\\r', '\r') + return arg.replace('\\t', '\t') + +if options.template != '': + read_template_file(options.template) + +idprefix += options.idprefix +symprefix += options.symprefix + +# This is a hack to maintain some semblance of backward compatibility with +# the old, Perl-based glib-mkenums. The old tool had an implicit ordering +# on the arguments and templates; each argument was parsed in order, and +# all the strings appended. This allowed developers to write: +# +# glib-mkenums \ +# --fhead ... \ +# --template a-template-file.c.in \ +# --ftail ... +# +# And have the fhead be prepended to the file-head stanza in the template, +# as well as the ftail be appended to the file-tail stanza in the template. +# Short of throwing away ArgumentParser and going over sys.argv[] element +# by element, we can simulate that behaviour by ensuring some ordering in +# how we build the template strings: +# +# - the head stanzas are always prepended to the template +# - the prod stanzas are always appended to the template +# - the tail stanzas are always appended to the template +# +# Within each instance of the command line argument, we append each value +# to the array in the order in which it appears on the command line. +fhead = ''.join([unescape_cmdline_args(x) for x in options.fhead]) + fhead +vhead = ''.join([unescape_cmdline_args(x) for x in options.vhead]) + vhead + +fprod += ''.join([unescape_cmdline_args(x) for x in options.fprod]) +eprod += ''.join([unescape_cmdline_args(x) for x in options.eprod]) +vprod += ''.join([unescape_cmdline_args(x) for x in options.vprod]) + +ftail = ftail + ''.join([unescape_cmdline_args(x) for x in options.ftail]) +vtail = vtail + ''.join([unescape_cmdline_args(x) for x in options.vtail]) + +if options.comment_tmpl != '': + comment_tmpl = unescape_cmdline_args(options.comment_tmpl) +elif comment_tmpl == "": + # default to C-style comments + comment_tmpl = "/* \u0040comment\u0040 */" + +output = options.output + +if output is not None: + (out_dir, out_fn) = os.path.split(options.output) + out_suffix = '_' + os.path.splitext(out_fn)[1] + if out_dir == '': + out_dir = '.' + fd, filename = tempfile.mkstemp(dir=out_dir) + os.close(fd) + tmpfile = io.open(filename, "w", encoding="utf-8") + output_stream = tmpfile +else: + tmpfile = None + +# put auto-generation comment +comment = comment_tmpl.replace('\u0040comment\u0040', + 'This file is generated by glib-mkenums, do ' + 'not modify it. This code is licensed under ' + 'the same license as the containing project. ' + 'Note that it links to GLib, so must comply ' + 'with the LGPL linking clauses.') +write_output("\n" + comment + '\n') + +def replace_specials(prod): + prod = prod.replace(r'\\a', r'\a') + prod = prod.replace(r'\\b', r'\b') + prod = prod.replace(r'\\t', r'\t') + prod = prod.replace(r'\\n', r'\n') + prod = prod.replace(r'\\f', r'\f') + prod = prod.replace(r'\\r', r'\r') + prod = prod.rstrip() + return prod + + +def warn_if_filename_basename_used(section, prod): + for substitution in ('\u0040filename\u0040', + '\u0040basename\u0040'): + if substitution in prod: + print_warning('{} used in {} section.'.format(substitution, + section)) + +if len(fhead) > 0: + prod = fhead + warn_if_filename_basename_used('file-header', prod) + prod = replace_specials(prod) + write_output(prod) + +def process_file(curfilename): + global entries, flags, seenbitshift, seenprivate, enum_prefix, c_namespace + firstenum = True + + try: + curfile = io.open(curfilename, encoding="utf-8", + errors="replace_and_warn") + except IOError as e: + if e.errno == errno.ENOENT: + print_warning('No file "{}" found.'.format(curfilename)) + return + raise + + while True: + line = curfile.readline() + if not line: + break + + line = line.strip() + + # read lines until we have no open comments + while re.search(r'/\*([^*]|\*(?!/))*$', line): + line += curfile.readline() + + # strip comments w/o options + line = re.sub(r'''/\*(?!<) + ([^*]+|\*(?!/))* + \*/''', '', line) + + # ignore forward declarations + if re.match(r'\s*typedef\s+enum.*;', line): + continue + + m = re.match(r'''\s*typedef\s+enum\s*[_A-Za-z]*[_A-Za-z0-9]*\s* + ({)?\s* + (?:/\*< + (([^*]|\*(?!/))*) + >\s*\*/)? + \s*({)?''', line, flags=re.X) + if m: + groups = m.groups() + if len(groups) >= 2 and groups[1] is not None: + options = parse_trigraph(groups[1]) + if 'skip' in options: + continue + enum_prefix = options.get('prefix', None) + flags = options.get('flags', None) + if 'flags' in options: + if flags is None: + flags = 1 + else: + flags = int(flags) + option_lowercase_name = options.get('lowercase_name', None) + option_underscore_name = options.get('underscore_name', None) + option_since = options.get('since', None) + else: + enum_prefix = None + flags = None + option_lowercase_name = None + option_underscore_name = None + option_since = None + + if option_lowercase_name is not None: + if option_underscore_name is not None: + print_warning("lowercase_name overridden with underscore_name") + option_lowercase_name = None + else: + print_warning("lowercase_name is deprecated, use underscore_name") + + # Didn't have trailing '{' look on next lines + if groups[0] is None and (len(groups) < 4 or groups[3] is None): + while True: + line = curfile.readline() + if not line: + print_error("Syntax error when looking for opening { in enum") + if re.match(r'\s*\{', line): + break + + seenbitshift = 0 + seenprivate = False + entries = [] + + # Now parse the entries + parse_entries(curfile, curfilename) + + # figure out if this was a flags or enums enumeration + if flags is None: + flags = seenbitshift + + # Autogenerate a prefix + if enum_prefix is None: + for entry in entries: + if not entry[2] and (len(entry) < 4 or entry[3] is None): + name = entry[0] + if enum_prefix is not None: + enum_prefix = os.path.commonprefix([name, enum_prefix]) + else: + enum_prefix = name + if enum_prefix is None: + enum_prefix = "" + else: + # Trim so that it ends in an underscore + enum_prefix = re.sub(r'_[^_]*$', '_', enum_prefix) + else: + # canonicalize user defined prefixes + enum_prefix = enum_prefix.upper() + enum_prefix = enum_prefix.replace('-', '_') + enum_prefix = re.sub(r'(.*)([^_])$', r'\1\2_', enum_prefix) + + fixed_entries = [] + for e in entries: + name = e[0] + num = e[1] + private = e[2] + if len(e) < 4 or e[3] is None: + nick = re.sub(r'^' + enum_prefix, '', name) + nick = nick.replace('_', '-').lower() + e = (name, num, private, nick) + fixed_entries.append(e) + entries = fixed_entries + + # Spit out the output + if option_underscore_name is not None: + enumlong = option_underscore_name.upper() + enumsym = option_underscore_name.lower() + enumshort = re.sub(r'^[A-Z][A-Z0-9]*_', '', enumlong) + + enumname_prefix = re.sub('_' + enumshort + '$', '', enumlong) + elif symprefix == '' and idprefix == '': + # enumname is e.g. GMatchType + enspace = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname) + + enumshort = re.sub(r'^[A-Z][a-z]*', '', enumname) + enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort) + enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort) + enumshort = enumshort.upper() + + enumname_prefix = re.sub(r'^([A-Z][a-z]*).*$', r'\1', enumname).upper() + + enumlong = enspace.upper() + "_" + enumshort + enumsym = enspace.lower() + "_" + enumshort.lower() + + if option_lowercase_name is not None: + enumsym = option_lowercase_name + else: + enumshort = enumname + if idprefix: + enumshort = re.sub(r'^' + idprefix, '', enumshort) + else: + enumshort = re.sub(r'/^[A-Z][a-z]*', '', enumshort) + + enumshort = re.sub(r'([^A-Z])([A-Z])', r'\1_\2', enumshort) + enumshort = re.sub(r'([A-Z][A-Z])([A-Z][0-9a-z])', r'\1_\2', enumshort) + enumshort = enumshort.upper() + + if symprefix: + enumname_prefix = symprefix.upper() + else: + enumname_prefix = idprefix.upper() + + enumlong = enumname_prefix + "_" + enumshort + enumsym = enumlong.lower() + + if option_since is not None: + enumsince = option_since + else: + enumsince = "" + + if firstenum: + firstenum = False + + if len(fprod) > 0: + prod = fprod + base = os.path.basename(curfilename) + + prod = prod.replace('\u0040filename\u0040', curfilename) + prod = prod.replace('\u0040basename\u0040', base) + prod = replace_specials(prod) + + write_output(prod) + + if len(eprod) > 0: + prod = eprod + + prod = prod.replace('\u0040enum_name\u0040', enumsym) + prod = prod.replace('\u0040EnumName\u0040', enumname) + prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort) + prod = prod.replace('\u0040ENUMNAME\u0040', enumlong) + prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix) + prod = prod.replace('\u0040enumsince\u0040', enumsince) + if flags: + prod = prod.replace('\u0040type\u0040', 'flags') + else: + prod = prod.replace('\u0040type\u0040', 'enum') + if flags: + prod = prod.replace('\u0040Type\u0040', 'Flags') + else: + prod = prod.replace('\u0040Type\u0040', 'Enum') + if flags: + prod = prod.replace('\u0040TYPE\u0040', 'FLAGS') + else: + prod = prod.replace('\u0040TYPE\u0040', 'ENUM') + prod = replace_specials(prod) + write_output(prod) + + if len(vhead) > 0: + prod = vhead + prod = prod.replace('\u0040enum_name\u0040', enumsym) + prod = prod.replace('\u0040EnumName\u0040', enumname) + prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort) + prod = prod.replace('\u0040ENUMNAME\u0040', enumlong) + prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix) + prod = prod.replace('\u0040enumsince\u0040', enumsince) + if flags: + prod = prod.replace('\u0040type\u0040', 'flags') + else: + prod = prod.replace('\u0040type\u0040', 'enum') + if flags: + prod = prod.replace('\u0040Type\u0040', 'Flags') + else: + prod = prod.replace('\u0040Type\u0040', 'Enum') + if flags: + prod = prod.replace('\u0040TYPE\u0040', 'FLAGS') + else: + prod = prod.replace('\u0040TYPE\u0040', 'ENUM') + prod = replace_specials(prod) + write_output(prod) + + if len(vprod) > 0: + prod = vprod + next_num = 0 + + prod = replace_specials(prod) + for name, num, private, nick in entries: + tmp_prod = prod + + if '\u0040valuenum\u0040' in prod: + # only attempt to eval the value if it is requested + # this prevents us from throwing errors otherwise + if num is not None: + # use sandboxed evaluation as a reasonable + # approximation to C constant folding + inum = eval(num, {}, c_namespace) + + # make sure it parsed to an integer + if not isinstance(inum, int): + sys.exit("Unable to parse enum value '%s'" % num) + num = inum + else: + num = next_num + + c_namespace[name] = num + tmp_prod = tmp_prod.replace('\u0040valuenum\u0040', str(num)) + next_num = int(num) + 1 + + if private: + continue + + tmp_prod = tmp_prod.replace('\u0040VALUENAME\u0040', name) + tmp_prod = tmp_prod.replace('\u0040valuenick\u0040', nick) + if flags: + tmp_prod = tmp_prod.replace('\u0040type\u0040', 'flags') + else: + tmp_prod = tmp_prod.replace('\u0040type\u0040', 'enum') + if flags: + tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Flags') + else: + tmp_prod = tmp_prod.replace('\u0040Type\u0040', 'Enum') + if flags: + tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'FLAGS') + else: + tmp_prod = tmp_prod.replace('\u0040TYPE\u0040', 'ENUM') + tmp_prod = tmp_prod.rstrip() + + write_output(tmp_prod) + + if len(vtail) > 0: + prod = vtail + prod = prod.replace('\u0040enum_name\u0040', enumsym) + prod = prod.replace('\u0040EnumName\u0040', enumname) + prod = prod.replace('\u0040ENUMSHORT\u0040', enumshort) + prod = prod.replace('\u0040ENUMNAME\u0040', enumlong) + prod = prod.replace('\u0040ENUMPREFIX\u0040', enumname_prefix) + prod = prod.replace('\u0040enumsince\u0040', enumsince) + if flags: + prod = prod.replace('\u0040type\u0040', 'flags') + else: + prod = prod.replace('\u0040type\u0040', 'enum') + if flags: + prod = prod.replace('\u0040Type\u0040', 'Flags') + else: + prod = prod.replace('\u0040Type\u0040', 'Enum') + if flags: + prod = prod.replace('\u0040TYPE\u0040', 'FLAGS') + else: + prod = prod.replace('\u0040TYPE\u0040', 'ENUM') + prod = replace_specials(prod) + write_output(prod) + +for fname in sorted(options.args): + process_file(fname) + +if len(ftail) > 0: + prod = ftail + warn_if_filename_basename_used('file-tail', prod) + prod = replace_specials(prod) + write_output(prod) + +# put auto-generation comment +comment = comment_tmpl +comment = comment.replace('\u0040comment\u0040', 'Generated data ends here') +write_output("\n" + comment + "\n") + +if tmpfile is not None: + tmpfilename = tmpfile.name + tmpfile.close() + + try: + os.unlink(options.output) + except OSError as error: + if error.errno != errno.ENOENT: + raise error + + os.rename(tmpfilename, options.output) |