1#!/usr/bin/env python3 2 3# Copyright (C) 2022 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the 'License'); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an 'AS IS' BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import argparse 18import json 19import os 20import pathlib 21import subprocess 22import sys 23import xml.etree.ElementTree as ET 24from collections import OrderedDict 25from operator import itemgetter 26from ninja_metrics_proto import ninja_metrics 27 28def build_cmd(ninja_binary, ninja_file, target, exempted_file_list): 29 cmd = [ninja_binary, '-f', ninja_file, '-t', 'inputs'] 30 if exempted_file_list and exempted_file_list.exists(): 31 with open(exempted_file_list) as fin: 32 for l in map(str.strip, fin.readlines()): 33 if l and not l.startswith('#'): 34 cmd.extend(['-e', l]) 35 cmd.append(target) 36 37 return cmd 38 39 40def count_project(projects, input_files): 41 project_count = dict() 42 for p in projects: 43 file_count = sum(f.startswith(p + os.path.sep) for f in input_files) 44 if file_count > 0: 45 project_count[p] = file_count 46 47 return dict(sorted(project_count.items(), key=itemgetter(1), reverse=True)) 48 49 50parser = argparse.ArgumentParser() 51 52parser.add_argument('-n', '--ninja_binary', type=pathlib.Path, required=True) 53parser.add_argument('-f', '--ninja_file', type=pathlib.Path, required=True) 54parser.add_argument('-t', '--target', type=str, required=True) 55parser.add_argument('-e', '--exempted_file_list', type=pathlib.Path) 56parser.add_argument('-o', '--out', type=pathlib.Path) 57group = parser.add_mutually_exclusive_group() 58group.add_argument('-r', '--repo_project_list', type=pathlib.Path) 59group.add_argument('-m', '--repo_manifest', type=pathlib.Path) 60args = parser.parse_args() 61 62input_files = sorted( 63 subprocess.check_output( 64 build_cmd(args.ninja_binary, args.ninja_file, args.target, 65 args.exempted_file_list), text=True).strip().split('\n')) 66 67result = dict() 68result['input_files'] = input_files 69 70projects = None 71if args.repo_project_list and args.repo_project_list.exists(): 72 with open(args.repo_project_list) as fin: 73 projects = list(map(str.strip, fin.readlines())) 74elif args.repo_manifest and args.repo_manifest.exists(): 75 projects = [ 76 p.attrib['path'] 77 for p in ET.parse(args.repo_manifest).getroot().findall('project') 78 ] 79 80if projects: 81 project_to_count = count_project(projects, input_files) 82 result['project_count'] = project_to_count 83 result['total_project_count'] = len(project_to_count) 84 85result['total_input_count'] = len(input_files) 86 87if args.out: 88 with open(os.path.join(args.out.parent, args.out.name + '.json'), 'w') as json_file: 89 json.dump(result, json_file, indent=2) 90 with open(os.path.join(args.out.parent, args.out.name + '.pb'), 'wb') as pb_file: 91 pb_file.write(ninja_metrics.generate_proto(result).SerializeToString()) 92else: 93 print(json.dumps(result, indent=2)) 94