| # 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() |