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