diff options
Diffstat (limited to 'third_party/libwebrtc/build/android/gyp/allot_native_libraries.py')
-rwxr-xr-x | third_party/libwebrtc/build/android/gyp/allot_native_libraries.py | 185 |
1 files changed, 185 insertions, 0 deletions
diff --git a/third_party/libwebrtc/build/android/gyp/allot_native_libraries.py b/third_party/libwebrtc/build/android/gyp/allot_native_libraries.py new file mode 100755 index 0000000000..978b173403 --- /dev/null +++ b/third_party/libwebrtc/build/android/gyp/allot_native_libraries.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Allots libraries to modules to be packaged into. + +All libraries that are depended on by a single module will be allotted to this +module. All other libraries will be allotted to the closest ancestor. + +Example: + Given the module dependency structure + + c + / \ + b d + / \ + a e + + and libraries assignment + + a: ['lib1.so'] + e: ['lib2.so', 'lib1.so'] + + will make the allotment decision + + c: ['lib1.so'] + e: ['lib2.so'] + + The above example is invoked via: + + ./allot_native_libraries \ + --libraries 'a,["1.so"]' \ + --libraries 'e,["2.so", "1.so"]' \ + --dep c:b \ + --dep b:a \ + --dep c:d \ + --dep d:e \ + --output <output JSON> +""" + +import argparse +import collections +import json +import sys + +from util import build_utils + + +def _ModuleLibrariesPair(arg): + pos = arg.find(',') + assert pos > 0 + return (arg[:pos], arg[pos + 1:]) + + +def _DepPair(arg): + parent, child = arg.split(':') + return (parent, child) + + +def _PathFromRoot(module_tree, module): + """Computes path from root to a module. + + Parameters: + module_tree: Dictionary mapping each module to its parent. + module: Module to which to compute the path. + + Returns: + Path from root the the module. + """ + path = [module] + while module_tree.get(module): + module = module_tree[module] + path = [module] + path + return path + + +def _ClosestCommonAncestor(module_tree, modules): + """Computes the common ancestor of a set of modules. + + Parameters: + module_tree: Dictionary mapping each module to its parent. + modules: Set of modules for which to find the closest common ancestor. + + Returns: + The closest common ancestor. + """ + paths = [_PathFromRoot(module_tree, m) for m in modules] + assert len(paths) > 0 + ancestor = None + for level in zip(*paths): + if len(set(level)) != 1: + return ancestor + ancestor = level[0] + return ancestor + + +def _AllotLibraries(module_tree, libraries_map): + """Allot all libraries to a module. + + Parameters: + module_tree: Dictionary mapping each module to its parent. Modules can map + to None, which is considered the root of the tree. + libraries_map: Dictionary mapping each library to a set of modules, which + depend on the library. + + Returns: + A dictionary mapping mapping each module name to a set of libraries allotted + to the module such that libraries with multiple dependees are allotted to + the closest ancestor. + + Raises: + Exception if some libraries can only be allotted to the None root. + """ + allotment_map = collections.defaultdict(set) + for library, modules in libraries_map.items(): + ancestor = _ClosestCommonAncestor(module_tree, modules) + if not ancestor: + raise Exception('Cannot allot libraries for given dependency tree') + allotment_map[ancestor].add(library) + return allotment_map + + +def main(args): + parser = argparse.ArgumentParser() + parser.add_argument( + '--libraries', + action='append', + type=_ModuleLibrariesPair, + required=True, + help='A pair of module name and GN list of libraries a module depends ' + 'on. Can be specified multiple times.') + parser.add_argument( + '--output', + required=True, + help='A JSON file with a key for each module mapping to a list of ' + 'libraries, which should be packaged into this module.') + parser.add_argument( + '--dep', + action='append', + type=_DepPair, + dest='deps', + default=[], + help='A pair of parent module name and child module name ' + '(format: "<parent>:<child>"). Can be specified multiple times.') + options = parser.parse_args(build_utils.ExpandFileArgs(args)) + options.libraries = [(m, build_utils.ParseGnList(l)) + for m, l in options.libraries] + + # Parse input creating libraries and dependency tree. + libraries_map = collections.defaultdict(set) # Maps each library to its + # dependee modules. + module_tree = {} # Maps each module name to its parent. + for module, libraries in options.libraries: + module_tree[module] = None + for library in libraries: + libraries_map[library].add(module) + for parent, child in options.deps: + if module_tree.get(child): + raise Exception('%s cannot have multiple parents' % child) + module_tree[child] = parent + module_tree[parent] = module_tree.get(parent) + + # Allot all libraries to a module such that libraries with multiple dependees + # are allotted to the closest ancestor. + allotment_map = _AllotLibraries(module_tree, libraries_map) + + # The build system expects there to be a set of libraries even for the modules + # that don't have any libraries allotted. + for module in module_tree: + # Creates missing sets because of defaultdict. + allotment_map[module] = allotment_map[module] + + with open(options.output, 'w') as f: + # Write native libraries config and ensure the output is deterministic. + json.dump({m: sorted(l) + for m, l in allotment_map.items()}, + f, + sort_keys=True, + indent=2) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) |