blob: db72660d4718819eb349511f0ab51588fd3a434a [file] [log] [blame]
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Invokes gperf for the GN build.
# Usage: gperf.py gperf ...
import argparse
import os
import re
import subprocess
import sys
import template_expander
def generate_gperf(gperf_path, gperf_input, gperf_args):
# FIXME: If we could depend on Python 3.4, we would use
# subprocess.check_output
# If gperf isn't in the path we get an OSError. We don't want to use
# the normal solution of shell=True (as this has to run on many
# platforms), so instead we catch the error and raise a
# CalledProcessError like subprocess would do when shell=True is set.
cmd = [gperf_path] + gperf_args
try:
gperf = subprocess.Popen(
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
universal_newlines=True)
gperf_output = gperf.communicate(gperf_input)[0]
# Massage gperf output to be more palatable for modern compilers.
# TODO(thakis): Upstream these to gperf so we don't need massaging.
# `register` is deprecated in C++11 and removed in C++17, so remove
# it from gperf's output.
# https://savannah.gnu.org/bugs/index.php?53028
gperf_output = re.sub(r'\bregister ', '', gperf_output)
# -Wimplicit-fallthrough needs an explicit fallthrough statement,
# so replace gperf's /*FALLTHROUGH*/ comment with the statement.
# https://savannah.gnu.org/bugs/index.php?53029
gperf_output = gperf_output.replace('/*FALLTHROUGH*/',
' FALLTHROUGH;')
# -Wpointer-to-int-cast warns about casting pointers to smaller ints
# Replace {(int)(long)&(foo), bar} with
# {static_cast<int>(reinterpret_cast<uintptr_t>(&(foo)), bar}
gperf_output = re.sub(
r'\(int\)\(long\)(.*?),',
r'static_cast<int>(reinterpret_cast<uintptr_t>(\1)),',
gperf_output)
script = 'third_party/blink/renderer/build/scripts/gperf.py'
return '// Generated by %s\n' % script + gperf_output
except OSError:
raise subprocess.CalledProcessError(
127, cmd, output='Command not found.')
def use_jinja_gperf_template(template_path, gperf_extra_args=None):
def wrapper(generator):
def generator_internal(*args, **kwargs):
parameters = generator(*args, **kwargs)
assert 'gperf_path' in parameters, 'Must specify gperf_path in ' \
'template map returned from decorated function'
gperf_path = parameters['gperf_path']
gperf_input = template_expander.apply_template(
template_path, parameters)
gperf_args = ['--key-positions=*', '-P', '-n']
gperf_args.extend(['-m', '50']) # Pick best of 50 attempts.
# Allow duplicate hashes -> More compact code.
gperf_args.append('-D')
if gperf_extra_args:
gperf_args.extend(gperf_extra_args)
return generate_gperf(gperf_path, gperf_input, gperf_args)
generator_internal.__name__ = generator.__name__
return generator_internal
return wrapper
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--output-file")
args, unknownargs = parser.parse_known_args()
gperf_path, gperf_args = unknownargs[0], unknownargs[1:]
infile = None
for arg in gperf_args:
if os.path.isfile(arg):
assert infile is None, 'duplicate inputs? %s, %s' % (infile, arg)
infile = arg
assert infile is not None, 'no input found'
# Since we're passing the input file on stdin, remove it from the args.
gperf_args.remove(infile)
open(args.output_file, 'wb').write(
generate_gperf(gperf_path,
open(infile).read(), gperf_args).encode('utf-8'))
if __name__ == '__main__':
main()