1#!/usr/bin/env python3 2# Copyright (C) 2022 The Android Open Source Project 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import argparse 17import json 18import os 19import sys 20from typing import Any 21from typing import Dict 22from typing import Optional 23 24# Allow importing of root-relative modules. 25ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 26sys.path.append(os.path.join(ROOT_DIR)) 27 28#pylint: disable=wrong-import-position 29from python.generators.trace_processor_table.public import ColumnDoc 30from python.generators.trace_processor_table.public import ColumnFlag 31import python.generators.trace_processor_table.util as util 32from python.generators.trace_processor_table.util import ParsedTable 33from python.generators.trace_processor_table.util import ParsedColumn 34#pylint: enable=wrong-import-position 35 36 37def gen_json_for_column(table: ParsedTable, 38 col: ParsedColumn) -> Optional[Dict[str, Any]]: 39 """Generates the JSON documentation for a column in a table.""" 40 assert table.table.tabledoc 41 42 # id and type columns should be skipped if the table specifies so. 43 is_skippable_col = col.is_implicit_id or col.is_implicit_type 44 if table.table.tabledoc.skip_id_and_type and is_skippable_col: 45 return None 46 47 # Ignore hidden columns in the documentation. 48 if ColumnFlag.HIDDEN in col.column.flags: 49 return None 50 51 # Our default assumption is the documentation for a column is a plain string 52 # so just make the comment for the column equal to that. 53 54 if isinstance(col.doc, ColumnDoc): 55 comment = col.doc.doc 56 if col.doc.joinable: 57 join_table, join_col = col.doc.joinable.split('.') 58 else: 59 join_table, join_col = None, None 60 elif isinstance(col.doc, str): 61 comment = col.doc 62 join_table, join_col = None, None 63 else: 64 raise Exception('Unknown column documentation type ' 65 f'{table.table.class_name}::{col.column.name}') 66 67 parsed_type = util.parse_type_with_cols(table.table, 68 [c.column for c in table.columns], 69 col.column.type) 70 docs_type = parsed_type.cpp_type 71 if docs_type == 'StringPool::Id': 72 docs_type = 'string' 73 74 ref_class_name = None 75 if parsed_type.id_table and not col.is_implicit_id: 76 id_table_name = util.public_sql_name(parsed_type.id_table) 77 ref_class_name = parsed_type.id_table.class_name 78 79 if not join_table and not join_col: 80 join_table = id_table_name 81 join_col = "id" 82 83 return { 84 'name': col.column.name, 85 'type': docs_type, 86 'comment': comment, 87 'optional': parsed_type.is_optional, 88 'refTableCppName': ref_class_name, 89 'joinTable': join_table, 90 'joinCol': join_col, 91 } 92 93 94def main(): 95 parser = argparse.ArgumentParser() 96 parser.add_argument('--out', required=True) 97 parser.add_argument('inputs', nargs='*') 98 parser.add_argument('--relative-input-dir') 99 args = parser.parse_args() 100 101 def get_relin_path(in_path: str): 102 if not args.relative_input_dir: 103 return in_path 104 return os.path.relpath(in_path, args.relative_input_dir) 105 106 modules = [ 107 os.path.splitext(get_relin_path(i).replace('/', '.'))[0] 108 for i in args.inputs 109 ] 110 table_docs = [] 111 for parsed in util.parse_tables_from_modules(modules): 112 table = parsed.table 113 114 # If there is no non-intrinsic alias for the table, don't 115 # include the table in the docs. 116 name = util.public_sql_name(table) 117 if name.startswith('__intrinsic_') or name.startswith('experimental_'): 118 continue 119 120 doc = table.tabledoc 121 assert doc 122 cols = ( 123 gen_json_for_column(parsed, c) 124 for c in parsed.columns 125 if not c.is_ancestor) 126 table_docs.append({ 127 'name': name, 128 'cppClassName': table.class_name, 129 'defMacro': table.class_name, 130 'comment': '\n'.join(l.strip() for l in doc.doc.splitlines()), 131 'parent': None, 132 'parentDefName': table.parent.class_name if table.parent else '', 133 'tablegroup': doc.group, 134 'cols': [c for c in cols if c] 135 }) 136 137 with open(args.out, 'w') as out: 138 json.dump(table_docs, out, indent=2) 139 out.write('\n') 140 141 142if __name__ == '__main__': 143 exit(main()) 144