1#!/usr/bin/env python3 2# Copyright 2022 The Chromium Authors 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5"""check_cronet_dependencies.py - Keep track of Cronet's dependencies.""" 6 7import argparse 8import os 9import subprocess 10import sys 11 12REPOSITORY_ROOT = os.path.abspath( 13 os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir)) 14 15sys.path.insert(0, os.path.join(REPOSITORY_ROOT, 'build/android/gyp')) 16from util import build_utils # pylint: disable=wrong-import-position 17 18 19def normalize_third_party_dep(dependency): 20 third_party_str = 'third_party/' 21 if third_party_str not in dependency: 22 raise ValueError('Dependency is not third_party dependency') 23 root_end_index = dependency.rfind(third_party_str) + len(third_party_str) 24 dependency_name_end_index = dependency.find("/", root_end_index) 25 if dependency_name_end_index == -1: 26 return dependency 27 return dependency[:dependency_name_end_index] 28 29 30def dedup_third_party_deps_internal(dependencies, root_deps=None): 31 if root_deps is None: 32 root_deps = set() 33 deduped_deps = [] 34 for dependency in dependencies: 35 # crbug.com(1406537): `gn desc deps` can spit out non-deps stuff if it finds unknown 36 # GN args 37 if not dependency or not dependency.startswith('//'): 38 continue 39 if dependency[-1] == '/': 40 raise ValueError('Dependencies must not have a trailing forward slash') 41 42 if 'third_party/' not in dependency: 43 # We don't apply any filtering to non third_party deps. 44 deduped_deps.append(dependency) 45 continue 46 47 # Take the last occurrence to consider //third_party/foo and 48 # //third_party/foo/third_party/bar as two distinct dependencies. 49 third_party_dep_segments = dependency.split('third_party/') 50 third_party_dep = third_party_dep_segments[-1] 51 if '/' not in third_party_dep: 52 # Root dependencies are always unique. 53 # Note: We append the amount of splits to differentiate between 54 # //third_party/foo and //third_party/bar/third_party/foo. 55 root_dep = str(len(third_party_dep_segments)) + third_party_dep 56 root_deps.add(root_dep) 57 deduped_deps.append(normalize_third_party_dep(dependency)) 58 else: 59 third_party_dep_root = (str(len(third_party_dep_segments)) + 60 third_party_dep.split('/')[0]) 61 if third_party_dep_root not in root_deps: 62 root_deps.add(third_party_dep_root) 63 deduped_deps.append(normalize_third_party_dep(dependency)) 64 return (deduped_deps, root_deps) 65 66 67def dedup_third_party_deps(old_dependencies, new_dependencies): 68 """Maintains only a single target for each third_party dependency.""" 69 (_, root_deps) = dedup_third_party_deps_internal(old_dependencies) 70 (deduped_deps, _) = dedup_third_party_deps_internal(new_dependencies, 71 root_deps=root_deps) 72 return '\n'.join(deduped_deps) 73 74 75def main(): 76 parser = argparse.ArgumentParser( 77 prog='Check cronet dependencies', 78 description= 79 "Checks whether Cronet's current dependencies match the known ones.") 80 parser.add_argument( 81 '--new_dependencies_script', 82 type=str, 83 help='Relative path to the script that outputs new dependencies', 84 required=True, 85 ) 86 parser.add_argument( 87 '--old_dependencies', 88 type=str, 89 help='Relative path to file that contains the old dependencies', 90 required=True, 91 ) 92 parser.add_argument( 93 '--stamp', 94 type=str, 95 help='Path to touch on success', 96 ) 97 args = parser.parse_args() 98 99 new_dependencies = subprocess.check_output( 100 [args.new_dependencies_script, args.old_dependencies]).decode('utf-8') 101 with open(args.old_dependencies, 'r') as f: 102 new_dependencies = dedup_third_party_deps(f.read().splitlines(), 103 new_dependencies.splitlines()) 104 if new_dependencies != '': 105 print('New dependencies detected:') 106 print(new_dependencies) 107 print('Please update: ' + args.old_dependencies) 108 sys.exit(-1) 109 else: 110 build_utils.Touch(args.stamp) 111 112 113if __name__ == '__main__': 114 main() 115