mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-12-26 23:46:15 +01:00
013980d839
The python interpreter found by `/usr/bin/env python3` is not necessarily the same installation as the one that's found by meson's `pymod.find_installation('python')`. This means that even though meson is checking that the python installation it found includes the 'packaging' module, the scripts might not have access to that module when run. For distribution packaging, it's usually desirable to have python script interpreters be fully specified paths, rather than use `/usr/bin/env`, to ensure the scripts run using the expected python installation (i.e. the one where the python 'packaging' dependency is installed). The easiest way to fix this is to set the script interpreter to the `full_path()` of the python interpreter found by meson. The specific python interpreter that will be used can be selected through the use of a meson machine file by overriding the "python" program. Many distributions already have this set up using meson packaging helpers.
817 lines
31 KiB
Plaintext
Executable File
817 lines
31 KiB
Plaintext
Executable File
#!@PYTHON@
|
|
|
|
# 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 @VERSION@
|
|
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*'[^']*'\s* # char
|
|
| # OR
|
|
\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)
|
|
|
|
# Support character literals
|
|
if isinstance(inum, str) and len(inum) == 1:
|
|
inum = ord(inum)
|
|
|
|
# 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)
|