| #!/usr/bin/env python3 | 
 | # Copyright (C) 2022 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. | 
 |  | 
 | import argparse | 
 | import os | 
 | import sys | 
 | import json | 
 | from collections import defaultdict | 
 | from typing import Dict | 
 |  | 
 | ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | 
 | sys.path.append(os.path.join(ROOT_DIR)) | 
 |  | 
 | from python.generators.sql_processing.docs_parse import parse_file | 
 |  | 
 |  | 
 | def main(): | 
 |   parser = argparse.ArgumentParser() | 
 |   parser.add_argument('--json-out', required=True) | 
 |   parser.add_argument('--input-list-file') | 
 |   parser.add_argument('sql_files', nargs='*') | 
 |   args = parser.parse_args() | 
 |  | 
 |   if args.input_list_file and args.sql_files: | 
 |     print("Only one of --input-list-file and list of SQL files expected") | 
 |     return 1 | 
 |  | 
 |   sql_files = [] | 
 |   if args.input_list_file: | 
 |     with open(args.input_list_file, 'r') as input_list_file: | 
 |       for line in input_list_file.read().splitlines(): | 
 |         sql_files.append(line) | 
 |   else: | 
 |     sql_files = args.sql_files | 
 |  | 
 |   # Unfortunately we cannot pass this in as an arg as soong does not provide | 
 |   # us a way to get the path to the Perfetto source directory. This fails on | 
 |   # empty path but it's a price worth paying to have to use gross hacks in | 
 |   # Soong. | 
 |   root_dir = os.path.commonpath(sql_files) | 
 |  | 
 |   # Extract the SQL output from each file. | 
 |   sql_outputs: Dict[str, str] = {} | 
 |   for file_name in sql_files: | 
 |     with open(file_name, 'r') as f: | 
 |       relpath = os.path.relpath(file_name, root_dir) | 
 |  | 
 |       # We've had bugs (e.g. b/264711057) when Soong's common path logic breaks | 
 |       # and ends up with a bunch of ../ prefixing the path: disallow any ../ | 
 |       # as this should never be a valid in our C++ output. | 
 |       assert '../' not in relpath | 
 |  | 
 |       sql_outputs[relpath] = f.read() | 
 |  | 
 |   modules = defaultdict(list) | 
 |   # Add documentation from each file | 
 |   for path, sql in sql_outputs.items(): | 
 |     module_name = path.split("/")[0] | 
 |     import_key = path.split(".sql")[0].replace("/", ".") | 
 |  | 
 |     docs = parse_file(path, sql) | 
 |  | 
 |     # Some modules (i.e `deprecated`) should not generate docs. | 
 |     if not docs: | 
 |       continue | 
 |  | 
 |     if len(docs.errors) > 0: | 
 |       for e in docs.errors: | 
 |         print(e) | 
 |       return 1 | 
 |  | 
 |     file_dict = { | 
 |         'import_key': | 
 |             import_key, | 
 |         'imports': [{ | 
 |             'name': table.name, | 
 |             'desc': table.desc, | 
 |             'summary_desc': table.desc.split('\n\n')[0].replace('\n', ' '), | 
 |             'type': table.type, | 
 |             'cols': { | 
 |                 col_name: { | 
 |                     'type': col.type, | 
 |                     'desc': col.description, | 
 |                 } for (col_name, col) in table.cols.items() | 
 |             }, | 
 |         } for table in docs.table_views], | 
 |         'functions': [{ | 
 |             'name': function.name, | 
 |             'desc': function.desc, | 
 |             'summary_desc': function.desc.split('\n\n')[0].replace('\n', ' '), | 
 |             'args': { | 
 |                 arg_name: { | 
 |                     'type': arg.type, | 
 |                     'desc': arg.description, | 
 |                 } for (arg_name, arg) in function.args.items() | 
 |             }, | 
 |             'return_type': function.return_type, | 
 |             'return_desc': function.return_desc, | 
 |         } for function in docs.functions], | 
 |         'table_functions': [{ | 
 |             'name': function.name, | 
 |             'desc': function.desc, | 
 |             'summary_desc': function.desc.split('\n\n')[0].replace('\n', ' '), | 
 |             'args': { | 
 |                 arg_name: { | 
 |                     'type': arg.type, | 
 |                     'desc': arg.description, | 
 |                 } for (arg_name, arg) in function.args.items() | 
 |             }, | 
 |             'cols': { | 
 |                 col_name: { | 
 |                     'type': col.type, | 
 |                     'desc': col.description, | 
 |                 } for (col_name, col) in function.cols.items() | 
 |             }, | 
 |         } for function in docs.table_functions], | 
 |         'macros': [{ | 
 |             'name': macro.name, | 
 |             'desc': macro.desc, | 
 |             'summary_desc': macro.desc.split('\n\n')[0].replace('\n', ' '), | 
 |             'return_desc': macro.return_desc, | 
 |             'return_type': macro.return_type, | 
 |             'args': { | 
 |                 arg_name: { | 
 |                     'type': arg.type, | 
 |                     'desc': arg.description, | 
 |                 } for (arg_name, arg) in macro.args.items() | 
 |             }, | 
 |         } for macro in docs.macros], | 
 |     } | 
 |     modules[module_name].append(file_dict) | 
 |  | 
 |   with open(args.json_out, 'w+') as f: | 
 |     json.dump(modules, f, indent=4) | 
 |  | 
 |   return 0 | 
 |  | 
 |  | 
 | if __name__ == '__main__': | 
 |   sys.exit(main()) |