blob: 1bab79865a37c5b383d567410ba5e32c167309bf [file] [log] [blame]
#!/usr/bin/env python3
# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""check_cronet_dependencies.py - Keep track of Cronet's dependencies."""
import argparse
import os
import subprocess
import sys
REPOSITORY_ROOT = os.path.abspath(
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, os.pardir))
sys.path.insert(0, os.path.join(REPOSITORY_ROOT, 'build/android/gyp'))
from util import build_utils # pylint: disable=wrong-import-position
def normalize_third_party_dep(dependency):
third_party_str = 'third_party/'
if third_party_str not in dependency:
raise ValueError('Dependency is not third_party dependency')
root_end_index = dependency.rfind(third_party_str) + len(third_party_str)
dependency_name_end_index = dependency.find("/", root_end_index)
if dependency_name_end_index == -1:
return dependency
return dependency[:dependency_name_end_index]
def dedup_third_party_deps_internal(dependencies, root_deps=None):
if root_deps is None:
root_deps = set()
deduped_deps = []
for dependency in dependencies:
# crbug.com(1406537): `gn desc deps` can spit out non-deps stuff if it finds unknown
# GN args
if not dependency or not dependency.startswith('//'):
continue
if dependency[-1] == '/':
raise ValueError('Dependencies must not have a trailing forward slash')
if 'third_party/' not in dependency:
# We don't apply any filtering to non third_party deps.
deduped_deps.append(dependency)
continue
# Take the last occurrence to consider //third_party/foo and
# //third_party/foo/third_party/bar as two distinct dependencies.
third_party_dep_segments = dependency.split('third_party/')
third_party_dep = third_party_dep_segments[-1]
if '/' not in third_party_dep:
# Root dependencies are always unique.
# Note: We append the amount of splits to differentiate between
# //third_party/foo and //third_party/bar/third_party/foo.
root_dep = str(len(third_party_dep_segments)) + third_party_dep
root_deps.add(root_dep)
deduped_deps.append(normalize_third_party_dep(dependency))
else:
third_party_dep_root = (str(len(third_party_dep_segments)) +
third_party_dep.split('/')[0])
if third_party_dep_root not in root_deps:
root_deps.add(third_party_dep_root)
deduped_deps.append(normalize_third_party_dep(dependency))
return (deduped_deps, root_deps)
def dedup_third_party_deps(old_dependencies, new_dependencies):
"""Maintains only a single target for each third_party dependency."""
(_, root_deps) = dedup_third_party_deps_internal(old_dependencies)
(deduped_deps, _) = dedup_third_party_deps_internal(new_dependencies,
root_deps=root_deps)
return '\n'.join(deduped_deps)
def main():
parser = argparse.ArgumentParser(
prog='Check cronet dependencies',
description=
"Checks whether Cronet's current dependencies match the known ones.")
parser.add_argument(
'--new_dependencies_script',
type=str,
help='Relative path to the script that outputs new dependencies',
required=True,
)
parser.add_argument(
'--old_dependencies',
type=str,
help='Relative path to file that contains the old dependencies',
required=True,
)
parser.add_argument(
'--stamp',
type=str,
help='Path to touch on success',
)
parser.add_argument('--is_cronet_build',
action='store_true',
help='Whether this is an official Cronet build or not')
parser.set_defaults(is_cronet_build=False)
args = parser.parse_args()
if not args.is_cronet_build:
build_utils.Touch(args.stamp)
return
new_dependencies = subprocess.check_output(
[args.new_dependencies_script, args.old_dependencies]).decode('utf-8')
with open(args.old_dependencies, 'r') as f:
new_dependencies = dedup_third_party_deps(f.read().splitlines(),
new_dependencies.splitlines())
if new_dependencies != '':
print('New dependencies detected:')
print(new_dependencies)
print('Please update: ' + args.old_dependencies)
sys.exit(-1)
else:
build_utils.Touch(args.stamp)
if __name__ == '__main__':
main()