mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-09 19:06:15 +01:00
dbf0a56670
... in glib-mkenums python port. Was parsed correctly but then skipped due to inverted condition. https://bugzilla.gnome.org/show_bug.cgi?id=779332
596 lines
22 KiB
Python
Executable File
596 lines
22 KiB
Python
Executable File
#!/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.
|
|
|
|
import os, sys, re, argparse, tempfile
|
|
|
|
output_stream = sys.stdout
|
|
|
|
def write_output(output):
|
|
global output_stream
|
|
print(output, file=output_stream)
|
|
|
|
version = '@GLIB_VERSION@'
|
|
|
|
# glib-mkenums.py
|
|
# Information about the current enumeration
|
|
flags = False # Is enumeration a bitmask?
|
|
option_underscore_name = '' # Overriden 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.
|
|
seenbitshift = 0 # Have we seen bitshift operators?
|
|
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
|
|
sandbox = None # sandbox for safe evaluation of expressions
|
|
|
|
output = '' # Filename to write result into
|
|
|
|
def parse_trigraph(opts):
|
|
result = {}
|
|
|
|
for opt in re.split(r'\s*,\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
|
|
return result
|
|
|
|
def parse_entries(file, file_name):
|
|
global entries, enumindex, enumname, seenbitshift, flags
|
|
looking_for_name = False
|
|
|
|
for line in file:
|
|
# 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('\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 = open(newfilename)
|
|
|
|
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
|
|
(?:=( # 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]
|
|
value = None
|
|
options = None
|
|
if len(groups) > 1:
|
|
value = groups[1]
|
|
if len(groups) > 2:
|
|
options = groups[2]
|
|
if not flags 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, options['nick']));
|
|
else:
|
|
entries.append((name, value))
|
|
elif re.match('s*\#', line):
|
|
pass
|
|
else:
|
|
sys.exit("Failed to parse %s." % file_name)
|
|
return False
|
|
|
|
def print_version():
|
|
print("glib-mkenums version glib-" + version)
|
|
print("glib-mkenums comes with ABSOLUTELY NO WARRANTY.")
|
|
print("You may redistribute copies of glib-mkenums under the terms of")
|
|
print("the GNU General Public License which can be found in the")
|
|
print("GLib source package. Sources, examples and contact")
|
|
print("information are available at http://www.gtk.org")
|
|
sys.exit(0)
|
|
|
|
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
|
|
\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 = open(file)
|
|
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']
|
|
|
|
# default to C-style comments
|
|
if comment_tmpl == "":
|
|
comment_tmpl = "/* \u0040comment\u0040 */"
|
|
|
|
if len(sys.argv) == 1:
|
|
usage()
|
|
|
|
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',
|
|
help='Output file header')
|
|
parser.add_argument('--ftail', default='', dest='ftail',
|
|
help='Per input file production')
|
|
parser.add_argument('--fprod', default='', dest='fprod',
|
|
help='Put out TEXT everytime a new input file is being processed.')
|
|
parser.add_argument('--eprod', default='', dest='eprod',
|
|
help='Per enum text (produced prior to value iterations)')
|
|
parser.add_argument('--vhead', default='', dest='vhead',
|
|
help='Value header, produced before iterating over enum values')
|
|
parser.add_argument('--vprod', default='', dest='vprod',
|
|
help='Value text, produced for each enum value.')
|
|
parser.add_argument('--vtail', default='', dest='vtail',
|
|
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 informations')
|
|
parser.add_argument('args', nargs='*')
|
|
|
|
options = parser.parse_args()
|
|
|
|
if options.version:
|
|
print_version()
|
|
|
|
if options.template != '':
|
|
read_template_file(options.template)
|
|
|
|
idprefix += options.idprefix
|
|
symprefix += options.symprefix
|
|
fhead += options.fhead
|
|
ftail += options.ftail
|
|
eprod += options.eprod
|
|
vhead += options.vhead
|
|
vprod += options.vprod
|
|
vtail += options.vtail
|
|
if options.comment_tmpl != '':
|
|
comment_tmpl = options.comment_tmpl
|
|
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 = '.'
|
|
tmpfile = tempfile.NamedTemporaryFile(dir=out_dir, delete=False)
|
|
output_stream = tmpfile
|
|
else:
|
|
tmpfile = None
|
|
|
|
# put auto-generation comment
|
|
comment = comment_tmpl.replace('\u0040comment\u0040', 'Generated data (by glib-mkenums)')
|
|
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
|
|
|
|
if len(fhead) > 0:
|
|
prod = fhead;
|
|
base = os.path.basename(options.args[0])
|
|
|
|
prod = prod.replace('\u0040filename\u0040', options.args[0])
|
|
prod = prod.replace('\u0040basename\u0040', base)
|
|
prod = replace_specials(prod)
|
|
write_output(prod)
|
|
|
|
def process_file(curfilename):
|
|
global entries, flags, seenbitshift, enum_prefix
|
|
firstenum = True
|
|
curfile = open(curfilename)
|
|
for line in curfile:
|
|
# 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*
|
|
({)?\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 = 'flags' in options
|
|
option_lowercase_name = options.get('lowercase_name', None)
|
|
option_underscore_name = options.get('underscore_name', None)
|
|
else:
|
|
enum_prefix = None
|
|
flags = False
|
|
option_lowercase_name = None
|
|
option_underscore_name = None
|
|
|
|
if option_lowercase_name is not None:
|
|
if option_underscore_name is not None:
|
|
print("$0: $ARGV:$.: lowercase_name overriden with underscore_name", file=sys.stderr)
|
|
option_lowercase_name = None
|
|
else:
|
|
print("$0: $ARGV:$.: lowercase_name is deprecated, use underscore_name", file=sys.stderr)
|
|
|
|
# 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 re.match(r'\s*\{', line):
|
|
break
|
|
|
|
seenbitshift = 0;
|
|
entries = [];
|
|
|
|
# Now parse the entries
|
|
parse_entries(curfile, curfilename);
|
|
|
|
# figure out if this was a flags or enums enumeration
|
|
if not flags:
|
|
flags = seenbitshift
|
|
|
|
# Autogenerate a prefix
|
|
if enum_prefix is None:
|
|
for entry in entries:
|
|
if len(entry) < 3 or entry[2] 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_')
|
|
|
|
fixed_entries = []
|
|
for e in entries:
|
|
name = e[0]
|
|
num = e[1]
|
|
if len(e) < 3 or e[2] is None:
|
|
nick = re.sub(r'^' + enum_prefix, '', name)
|
|
nick = nick.replace('_', '-').lower()
|
|
e = (name, num, 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 is not None:
|
|
enumname_prefix = symprefix.upper()
|
|
else:
|
|
enumname_prefix = idprefix.upper()
|
|
|
|
enumlong = enumname_prefix + "_" + enumshort
|
|
enumsym = enumlong.lower()
|
|
|
|
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)
|
|
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)
|
|
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, 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, {}, {});
|
|
|
|
# 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
|
|
|
|
tmp_prod = tmp_prod.replace('\u0040valuenum\u0040', str(num))
|
|
next_num = int(num) + 1
|
|
|
|
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)
|
|
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 options.args:
|
|
process_file(fname)
|
|
|
|
if len(ftail) > 0:
|
|
prod = ftail
|
|
base = os.path.basename(options.args[-1]) # FIXME, wrong
|
|
|
|
prod = prod.replace('\u0040filename\u0040', 'ARGV') # wrong too
|
|
prod = prod.replace('\u0040basename\u0040', base)
|
|
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()
|
|
os.unlink(options.output)
|
|
os.rename(tmpfilename, options.output)
|