| #!/usr/bin/env python3 | |
| # Copyright (C) 2019 The Android Open Source Project | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| # This script generates a C header file with build flags. | |
| # | |
| # It takes a response file as input, which contains the build flags. | |
| # The response file format is: | |
| # --flags <list of one or more flag values> | |
| # | |
| # The generated header file defines macros for each build flag, which can be | |
| # used in C code to conditionally compile code based on the build flags. | |
| # | |
| # See the gen_buildflags target in /gn/BUILD.gn for usage. | |
| import argparse | |
| import os | |
| import shlex | |
| import sys | |
| import re | |
| from dataclasses import dataclass | |
| COPYRIGHT_HEADER = '''/* | |
| * Copyright (C) 2019 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| ''' | |
| @dataclass | |
| class Flag: | |
| name: str | |
| raw_value: str | |
| class HeaderGenerator: | |
| """Generates the build flag header file.""" | |
| def __init__(self, out_path: str, flags: list[Flag]): | |
| self._out_path = out_path | |
| self._flags = flags | |
| self._guard = re.sub(r'[^A-Z0-9_]', '_', out_path.upper()) + '_' | |
| def _get_define_value(self, value: str) -> str: | |
| if value == 'true': | |
| return '1' | |
| if value == 'false': | |
| return '0' | |
| return value | |
| def _escape_value(self, value: str) -> str: | |
| return value.replace('\\', '\\\\').replace('"', '\\"') | |
| def _generate_defines(self) -> str: | |
| defines = [] | |
| for flag in self._flags: | |
| define = (f'#define PERFETTO_BUILDFLAG_DEFINE_{flag.name}() ' | |
| f'({self._get_define_value(flag.raw_value)})') | |
| defines.append(define) | |
| return '\n'.join(defines) | |
| def generate(self) -> None: | |
| boolean_flags = [f for f in self._flags if f.raw_value in ('true', 'false')] | |
| num_boolean_flags = len(boolean_flags) | |
| items = [] | |
| for flag in boolean_flags: | |
| items.append( | |
| f' {{"{flag.name}", PERFETTO_BUILDFLAG_DEFINE_{flag.name}()}},') | |
| all_build_flags_str = '\n'.join(items) | |
| header_template = f"""// Generated by {os.path.basename(__file__)} | |
| // fix_include_guards: off | |
| #ifndef {self._guard} | |
| #define {self._guard} | |
| // clang-format off | |
| {self._generate_defines()} | |
| struct PerfettoBuildFlag {{ | |
| const char* name; | |
| int value; | |
| }}; | |
| static const struct PerfettoBuildFlag kPerfettoBuildFlags[] = {{ | |
| {all_build_flags_str} | |
| }}; | |
| static const int kPerfettoBuildFlagsCount = sizeof(kPerfettoBuildFlags) / sizeof(struct PerfettoBuildFlag); | |
| // clang-format on | |
| #endif // {self._guard} | |
| """ | |
| with open(self._out_path, 'w', newline='\n') as out: | |
| out.write(COPYRIGHT_HEADER) | |
| out.write(header_template) | |
| def parse_flags_from_rsp(rsp_file: str) -> list[Flag]: | |
| """Parses the response file and returns a list of flags.""" | |
| with open(rsp_file, 'r') as f: | |
| content = f.read() | |
| parts = shlex.split(content) | |
| if not parts or parts[0] != '--flags': | |
| raise ValueError("Invalid response file format: --flags not found.") | |
| flags: list[Flag] = [] | |
| for flag_str in parts[1:]: | |
| if '=' not in flag_str: | |
| raise ValueError(f"Invalid flag format: {flag_str}") | |
| key, raw_value = flag_str.split('=', 1) | |
| flags.append(Flag(name=key, raw_value=raw_value)) | |
| return flags | |
| def main(): | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument('--rsp', help='Input response file containing the flags.') | |
| parser.add_argument( | |
| '--out', required=True, help='Output path of the generated header file.') | |
| args = parser.parse_args() | |
| flags = parse_flags_from_rsp(args.rsp) | |
| HeaderGenerator(args.out, flags).generate() | |
| if __name__ == '__main__': | |
| sys.exit(main()) |