vndk-def: Add deps-insight
deps-insight is a HTML-based tool to show the dependencies between the
libraries. This is designed to ease the module dependencies review
work.
Test: Run deps-insight subcommand against sailfish tree.
Change-Id: I20f23f7f604da289a22477ed82abe4361e5252c7
diff --git a/vndk/tools/definition-tool/assets/index.html b/vndk/tools/definition-tool/assets/index.html
new file mode 100644
index 0000000..bd935cc
--- /dev/null
+++ b/vndk/tools/definition-tool/assets/index.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+
+<html>
+ <head>
+ <meta charset="utf-8" />
+ <title>VNDK Dependency Insight</title>
+ <link rel="stylesheet" href="insight.css" media="all" />
+ <script type="text/javascript" src="insight.js"></script>
+ <script type="text/javascript" src="insight-data.js"></script>
+ </head>
+
+ <body></body>
+</html>
diff --git a/vndk/tools/definition-tool/assets/insight-data.js b/vndk/tools/definition-tool/assets/insight-data.js
new file mode 100644
index 0000000..9ef7a65
--- /dev/null
+++ b/vndk/tools/definition-tool/assets/insight-data.js
@@ -0,0 +1,21 @@
+(function () {
+ var strs = [
+ '/system/lib/libc.so',
+ '/system/lib/libm.so',
+ '/system/lib/libdl.so',
+ '/system/lib64/libc.so',
+ '/system/lib64/libm.so',
+ '/system/lib64/libdl.so',
+ 'll-ndk',
+ 'hl-ndk',
+ ];
+ var mods = [
+ [0, 32, [6], [[1, 2]], []],
+ [1, 32, [6], [], [0]],
+ [2, 32, [6], [], [0]],
+ [3, 64, [6], [[5], [4]], []],
+ [4, 64, [7], [[5]], [3]],
+ [5, 64, [7], [], [3, 4]],
+ ];
+ insight.init(document, strs, mods);
+})();
diff --git a/vndk/tools/definition-tool/assets/insight.css b/vndk/tools/definition-tool/assets/insight.css
new file mode 100644
index 0000000..7cff186
--- /dev/null
+++ b/vndk/tools/definition-tool/assets/insight.css
@@ -0,0 +1,103 @@
+p, li, td, th, h1, h2, h3 {
+ line-height: 1.6;
+}
+
+#control {
+ background-color: #eefaff;
+ border: 1px solid #3399ff;
+ border-collapse: collapse;
+ margin: 0px 0px 20px 0px;
+ width: 100%;
+}
+
+#control td {
+ padding: 3px;
+}
+
+#control_menu:link {
+ display: block;
+ width: 5em;
+ height: 20pt;
+ text-align: center;
+ line-height: 20pt;
+ color: #ffffff;
+ background-color: #3399ff;
+ position: fixed;
+ top: 0px;
+ right: 0px;
+}
+
+#control_menu:visited {
+ color: #ffffff;
+}
+
+#control_menu:hover {
+ color: #ffffff;
+ background-color: #0000ff;
+}
+
+.menu {
+ list-style-type: none;
+ margin: 0px;
+ padding: 0px;
+}
+
+.menu li {
+ display: inline;
+ padding: 0px 1em 0px 0px;
+}
+
+a:link {
+ color: #2244ff;
+ text-decoration: none;
+ font-family: monospace;
+}
+
+a:visited {
+ color: #2244ff;
+ text-decoration: none;
+}
+
+a:hover {
+ color: #ff3322;
+ text-decoration: underline;
+}
+
+a:active {
+ color: #ff3322;
+ text-decoration: underline;
+}
+
+#module_table {
+ border-collapse: collapse;
+}
+
+#module_tbody td,
+#module_tbody th {
+ border: 1px solid #dddddd;
+ padding: 3px 20px;
+ vertical-align: top;
+}
+
+#module_tbody th {
+ background-color: #eeeeee;
+}
+
+#module_tbody h1,
+#module_tbody h2,
+#module_tbody p {
+ font-size: 100%;
+ margin: 0px;
+ padding: 0px;
+}
+
+#module_tbody ol,
+#module_tbody ul {
+ margin: .3em 0px;
+ padding: 0px 0px 0px 2em;
+}
+
+#module_tbody tr:target {
+ background-color: #fffff0;
+ transition: background-color 800ms ease;
+}
diff --git a/vndk/tools/definition-tool/assets/insight.js b/vndk/tools/definition-tool/assets/insight.js
new file mode 100644
index 0000000..2a9202f
--- /dev/null
+++ b/vndk/tools/definition-tool/assets/insight.js
@@ -0,0 +1,439 @@
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define([], factory);
+ } else if (typeof module === 'object' && module.exports) {
+ module.exports = factory();
+ } else {
+ root.insight = factory();
+ }
+} (this, function () {
+ 'use strict';
+
+ let document;
+ let strsData, mods, tagIds;
+ let domPathInput;
+ let domTBody;
+
+ //--------------------------------------------------------------------------
+ // DOM Helper Functions
+ //--------------------------------------------------------------------------
+ function domNewText(text) {
+ return document.createTextNode(text);
+ }
+
+ function domNewElem(type) {
+ let dom = document.createElement(type);
+ for (let i = 1; i < arguments.length; ++i) {
+ let arg = arguments[i];
+ if (typeof(arg) == 'string' || typeof(arg) == 'number') {
+ arg = domNewText(arg)
+ }
+ dom.appendChild(arg);
+ }
+ return dom;
+ }
+
+ function domNewLink(text, onClick) {
+ let dom = domNewElem('a', text);
+ dom.setAttribute('href', '#');
+ dom.addEventListener('click', onClick);
+ return dom;
+ }
+
+ //--------------------------------------------------------------------------
+ // Module Row
+ //--------------------------------------------------------------------------
+ function countDeps(deps) {
+ let direct = 0;
+ let indirect = 0;
+ if (deps.length > 0) {
+ direct = deps[0].length;
+ for (let i = 1; i < deps.length; ++i) {
+ indirect += deps[i].length;
+ }
+ }
+ return [direct, indirect];
+ }
+
+ function Module(id, modData) {
+ this.id = id;
+ this.path = strsData[modData[0]];
+ this.cls = modData[1];
+ this.tagIds = new Set(modData[2]);
+ this.deps = modData[3];
+ this.users = modData[4];
+
+ [this.numDirectDeps, this.numIndirectDeps] = countDeps(this.deps);
+ this.numUsers = this.users.length;
+
+ this.dom = null;
+ this.visible = false;
+
+ this.linkDoms = Object.create(null);
+ }
+
+ Module.prototype.isTagged = function (tagId) {
+ return this.tagIds.has(tagId);
+ }
+
+ Module.prototype.createModuleLinkDom = function (mod) {
+ let dom = domNewElem('a', mod.path);
+ dom.setAttribute('href', '#mod_' + mod.id);
+ dom.setAttribute('data-mod-id', mod.id);
+ dom.setAttribute('data-owner-id', this.id);
+ dom.addEventListener('click', onModuleLinkClicked);
+ dom.addEventListener('mouseover', onModuleLinkMouseOver);
+ dom.addEventListener('mouseout', onModuleLinkMouseOut);
+
+ this.linkDoms[mod.id] = dom;
+
+ return dom;
+ }
+
+ Module.prototype.createModuleRelationsDom = function (parent, label,
+ modIds) {
+ parent.appendChild(domNewElem('h2', label));
+
+ let domOl = domNewElem('ol');
+ parent.appendChild(domOl);
+ for (let modId of modIds) {
+ domOl.appendChild(
+ domNewElem('li', this.createModuleLinkDom(mods[modId])));
+ }
+ }
+
+ Module.prototype.createModulePathTdDom = function (parent) {
+ parent.appendChild(domNewElem('td', this.createModuleLinkDom(this)));
+ }
+
+ Module.prototype.createTagsTdDom = function (parent) {
+ let domTd = domNewElem('td');
+ for (let tag of this.tagIds) {
+ domTd.appendChild(domNewElem('p', strsData[tag]));
+ }
+ parent.appendChild(domTd);
+ }
+
+ Module.prototype.createDepsTdDom = function (parent) {
+ let domTd = domNewElem(
+ 'td', this.numDirectDeps + ' + ' + this.numIndirectDeps);
+
+ let deps = this.deps;
+ if (deps.length > 0) {
+ this.createModuleRelationsDom(domTd, 'Direct', deps[0]);
+
+ for (let i = 1; i < deps.length; ++i) {
+ this.createModuleRelationsDom(domTd, 'Indirect #' + i, deps[i]);
+ }
+ }
+
+ parent.appendChild(domTd);
+ }
+
+ Module.prototype.createUsersTdDom = function (parent) {
+ let domTd = domNewElem('td', this.numUsers);
+
+ let users = this.users;
+ if (users.length > 0) {
+ this.createModuleRelationsDom(domTd, 'Direct', users);
+ }
+
+ parent.appendChild(domTd);
+ }
+
+ Module.prototype.createDom = function () {
+ let dom = this.dom = domNewElem('tr');
+ dom.setAttribute('id', 'mod_' + this.id);
+
+ this.createModulePathTdDom(dom);
+ this.createTagsTdDom(dom);
+ this.createDepsTdDom(dom);
+ this.createUsersTdDom(dom)
+ }
+
+ Module.prototype.showDom = function () {
+ if (this.visible) {
+ return;
+ }
+ domTBody.appendChild(this.dom);
+ this.visible = true;
+ }
+
+ Module.prototype.hideDom = function () {
+ if (!this.visible) {
+ return;
+ }
+ this.dom.parentNode.removeChild(this.dom);
+ this.visible = false;
+ }
+
+ function createModulesFromData(stringsData, modulesData) {
+ return modulesData.map(function (modData, id) {
+ return new Module(id, modData);
+ });
+ }
+
+ function createTagIdsFromData(stringsData, mods) {
+ let tagIds = new Set();
+ for (let mod of mods) {
+ for (let tag of mod.tagIds) {
+ tagIds.add(tag);
+ }
+ }
+
+ tagIds = Array.from(tagIds);
+ tagIds.sort(function (a, b) {
+ return strsData[a].localeCompare(strsData[b]);
+ });
+
+ return tagIds;
+ }
+
+ //--------------------------------------------------------------------------
+ // Data
+ //--------------------------------------------------------------------------
+ function init(doc, stringsData, modulesData) {
+ document = doc;
+ strsData = stringsData;
+
+ mods = createModulesFromData(stringsData, modulesData);
+ tagIds = createTagIdsFromData(stringsData, mods);
+
+ document.addEventListener('DOMContentLoaded', function (evt) {
+ createControlDom(document.body);
+ createTableDom(document.body);
+ });
+ }
+
+ //--------------------------------------------------------------------------
+ // Control
+ //--------------------------------------------------------------------------
+ function createControlDom(parent) {
+ let domTBody = domNewElem('tbody');
+
+ createSelectionTrDom(domTBody);
+ createAddByTagsTrDom(domTBody);
+ createAddByPathTrDom(domTBody);
+
+ let domTable = domNewElem('table', domTBody);
+ domTable.id = 'control';
+
+ let domFixedLink = domNewElem('a', 'Menu');
+ domFixedLink.href = '#control';
+ domFixedLink.id = 'control_menu';
+
+ parent.appendChild(domFixedLink);
+ parent.appendChild(domTable);
+ }
+
+ function createControlMenuTr(parent, label, items) {
+ let domUl = domNewElem('ul');
+ domUl.className = 'menu';
+ for (let [txt, callback] of items) {
+ domUl.appendChild(domNewElem('li', domNewLink(txt, callback)));
+ }
+
+ let domTr = domNewElem('tr',
+ createControlLabelTdDom(label),
+ domNewElem('td', domUl));
+
+ parent.appendChild(domTr);
+ }
+
+ function createSelectionTrDom(parent) {
+ const items = [
+ ['All', onAddAll],
+ ['32-bit', onAddAll32],
+ ['64-bit', onAddAll64],
+ ['Clear', onClear],
+ ];
+
+ createControlMenuTr(parent, 'Selection:', items);
+ }
+
+ function createAddByTagsTrDom(parent) {
+ if (tagIds.length == 0) {
+ return;
+ }
+
+ const items = tagIds.map(function (tagId) {
+ return [strsData[tagId], function (evt) {
+ evt.preventDefault(true);
+ showModulesByTagId(tagId);
+ }];
+ });
+
+ createControlMenuTr(parent, 'Add by Tags:', items);
+ }
+
+ function createAddByPathTrDom(parent) {
+ let domForm = domNewElem('form');
+ domForm.addEventListener('submit', onAddModuleByPath);
+
+ domPathInput = domNewElem('input');
+ domPathInput.type = 'text';
+ domForm.appendChild(domPathInput);
+
+ let domBtn = domNewElem('input');
+ domBtn.type = 'submit';
+ domBtn.value = 'Add';
+ domForm.appendChild(domBtn);
+
+ let domTr = domNewElem('tr',
+ createControlLabelTdDom('Add by Path:'),
+ domNewElem('td', domForm));
+
+ parent.appendChild(domTr);
+ }
+
+ function createControlLabelTdDom(text) {
+ return domNewElem('td', domNewElem('strong', text));
+ }
+
+
+ //--------------------------------------------------------------------------
+ // Table
+ //--------------------------------------------------------------------------
+ function createTableDom(parent) {
+ domTBody = domNewElem('tbody');
+ domTBody.id = 'module_tbody';
+
+ createTableHeaderDom(domTBody);
+ createAllModulesDom();
+ showAllModules();
+
+ let domTable = domNewElem('table', domTBody);
+ domTable.id = 'module_table';
+
+ parent.appendChild(domTable);
+ }
+
+ function createTableHeaderDom(parent) {
+ const labels = [
+ 'Name',
+ 'Tags',
+ 'Dependencies (Direct + Indirect)',
+ 'Users',
+ ];
+
+ let domTr = domNewElem('tr');
+ for (let label of labels) {
+ domTr.appendChild(domNewElem('th', label));
+ }
+
+ parent.appendChild(domTr);
+ }
+
+ function createAllModulesDom() {
+ for (let mod of mods) {
+ mod.createDom();
+ }
+ }
+
+ function hideAllModules() {
+ for (let mod of mods) {
+ mod.hideDom();
+ }
+ }
+
+ function showAllModules() {
+ for (let mod of mods) {
+ mod.showDom();
+ }
+ }
+
+ function showModulesByFilter(pred) {
+ for (let mod of mods) {
+ if (pred(mod)) {
+ mod.showDom();
+ }
+ }
+ }
+
+ function showModulesByTagId(tagId) {
+ showModulesByFilter(function (mod) {
+ return mod.isTagged(tagId);
+ });
+ }
+
+
+ //--------------------------------------------------------------------------
+ // Events
+ //--------------------------------------------------------------------------
+
+ function onAddModuleByPath(evt) {
+ evt.preventDefault();
+
+ let path = domPathInput.value;
+ domPathInput.value = '';
+
+ for (let mod of mods) {
+ if (mod.path == path) {
+ mod.showDom();
+ return;
+ }
+ }
+ alert('Path not found: ' + path);
+ }
+
+ function onAddAll(evt) {
+ evt.preventDefault(true);
+ hideAllModules();
+ showAllModules();
+ }
+
+ function onAddAllClass(evt, cls) {
+ evt.preventDefault(true);
+ hideAllModules();
+ showModulesByFilter(function (mod) {
+ return mod.cls == cls;
+ });
+ }
+
+ function onAddAll32(evt) {
+ onAddAllClass(evt, 32);
+ }
+
+ function onAddAll64(evt) {
+ onAddAllClass(evt, 64);
+ }
+
+ function onClear(evt) {
+ evt.preventDefault(true);
+ hideAllModules();
+ }
+
+ function onModuleLinkClicked(evt) {
+ let modId = parseInt(evt.target.getAttribute('data-mod-id'), 10);
+ mods[modId].showDom();
+ }
+
+ function setDirectDepBackgroundColor(modId, ownerId, color) {
+ let mod = mods[modId];
+ let owner = mods[ownerId];
+ let ownerLinkDoms = owner.linkDoms;
+ if (mod.deps.length > 0) {
+ for (let depId of mod.deps[0]) {
+ if (depId in ownerLinkDoms) {
+ ownerLinkDoms[depId].style.backgroundColor = color;
+ }
+ }
+ }
+ }
+
+ function onModuleLinkMouseOver(evt) {
+ let modId = parseInt(evt.target.getAttribute('data-mod-id'), 10);
+ let ownerId = parseInt(evt.target.getAttribute('data-owner-id'), 10);
+ setDirectDepBackgroundColor(modId, ownerId, '#ffff00');
+ }
+
+ function onModuleLinkMouseOut(evt) {
+ let modId = parseInt(evt.target.getAttribute('data-mod-id'), 10);
+ let ownerId = parseInt(evt.target.getAttribute('data-owner-id'), 10);
+ setDirectDepBackgroundColor(modId, ownerId, 'transparent');
+ }
+
+ return {
+ 'init': init,
+ };
+}));
diff --git a/vndk/tools/definition-tool/vndk_definition_tool.py b/vndk/tools/definition-tool/vndk_definition_tool.py
index 75b8f79..f125f9b 100755
--- a/vndk/tools/definition-tool/vndk_definition_tool.py
+++ b/vndk/tools/definition-tool/vndk_definition_tool.py
@@ -5,8 +5,10 @@
import argparse
import collections
import itertools
+import json
import os
import re
+import shutil
import stat
import struct
import sys
@@ -1739,31 +1741,15 @@
help='sub directory of vendor partition that has system files')
-class VNDKCommand(ELFGraphCommand):
- def __init__(self):
- super(VNDKCommand, self).__init__(
- 'vndk', help='Compute VNDK libraries set')
-
+class VNDKCommandBase(ELFGraphCommand):
def add_argparser_options(self, parser):
- super(VNDKCommand, self).add_argparser_options(parser)
+ super(VNDKCommandBase, self).add_argparser_options(parser)
parser.add_argument(
'--load-generic-refs',
help='compare with generic reference symbols')
parser.add_argument(
- '--warn-incorrect-partition', action='store_true',
- help='warn about libraries only have cross partition linkages')
-
- parser.add_argument(
- '--warn-high-level-ndk-deps', action='store_true',
- help='warn about VNDK depends on high-level NDK')
-
- parser.add_argument(
- '--warn-banned-vendor-lib-deps', action='store_true',
- help='warn when a vendor binaries depends on banned lib')
-
- parser.add_argument(
'--ban-vendor-lib-dep', action='append',
help='library that must not be used by vendor binaries')
@@ -1779,6 +1765,98 @@
'--outward-customization-for-vendor', action='append',
help='outward customized vndk for vendor partition')
+ def _check_arg_dir_exists(self, arg_name, dirs):
+ for path in dirs:
+ if not os.path.exists(path):
+ print('error: Failed to find the directory "{}" specified in {}'
+ .format(path, arg_name), file=sys.stderr)
+ sys.exit(1)
+ if not os.path.isdir(path):
+ print('error: Path "{}" specified in {} is not a directory'
+ .format(path, arg_name), file=sys.stderr)
+ sys.exit(1)
+
+ def check_dirs_from_args(self, args):
+ self._check_arg_dir_exists('--system', args.system)
+ self._check_arg_dir_exists('--vendor', args.vendor)
+
+ def _get_generic_refs_from_args(self, args):
+ if not args.load_generic_refs:
+ return None
+ return GenericRefs.create_from_dir(args.load_generic_refs)
+
+ def _get_banned_libs_from_args(self, args):
+ if not args.ban_vendor_lib_dep:
+ return BannedLibDict.create_default()
+
+ banned_libs = BannedLibDict()
+ for name in args.ban_vendor_lib_dep:
+ banned_libs.add(name, 'user-banned', BA_WARN)
+ return banned_libs
+
+ def _get_outward_customized_sets_from_args(self, args, graph):
+ vndk_customized_for_system = set()
+ vndk_customized_for_vendor = set()
+ system_libs = graph.lib_pt[PT_SYSTEM].values()
+
+ if args.outward_customization_default_partition in {'system', 'both'}:
+ vndk_customized_for_system.update(system_libs)
+
+ if args.outward_customization_default_partition in {'vendor', 'both'}:
+ vndk_customized_for_vendor.update(system_libs)
+
+ if args.outward_customization_for_system:
+ vndk_customized_for_system.update(
+ graph.get_libs(
+ args.outward_customization_for_system, lambda x: None))
+
+ if args.outward_customization_for_vendor:
+ vndk_customized_for_vendor.update(
+ graph.get_libs(
+ args.outward_customization_for_vendor, lambda x: None))
+ return (vndk_customized_for_system, vndk_customized_for_vendor)
+
+ def create_from_args(self, args):
+ """Create all essential data structures for VNDK computation."""
+
+ self.check_dirs_from_args(args)
+
+ generic_refs = self._get_generic_refs_from_args(args)
+ banned_libs = self._get_banned_libs_from_args(args)
+
+ graph = ELFLinker.create(args.system, args.system_dir_as_vendor,
+ args.vendor, args.vendor_dir_as_system,
+ args.load_extra_deps,
+ generic_refs=generic_refs)
+
+ vndk_customized_for_system, vndk_customized_for_vendor = \
+ self._get_outward_customized_sets_from_args(args, graph)
+
+ return (generic_refs, banned_libs, graph, vndk_customized_for_system,
+ vndk_customized_for_vendor)
+
+
+class VNDKCommand(VNDKCommandBase):
+ def __init__(self):
+ super(VNDKCommand, self).__init__(
+ 'vndk', help='Compute VNDK libraries set')
+
+ def add_argparser_options(self, parser):
+ super(VNDKCommand, self).add_argparser_options(parser)
+
+ parser.add_argument(
+ '--warn-incorrect-partition', action='store_true',
+ help='warn about libraries only have cross partition linkages')
+
+ parser.add_argument(
+ '--warn-high-level-ndk-deps', action='store_true',
+ help='warn about VNDK depends on high-level NDK')
+
+ parser.add_argument(
+ '--warn-banned-vendor-lib-deps', action='store_true',
+ help='warn when a vendor binaries depends on banned lib')
+
+
def _warn_incorrect_partition_lib_set(self, lib_set, partition, error_msg):
for lib in lib_set.values():
if not lib.num_users:
@@ -1822,73 +1900,20 @@
print('warning: {}: NDK library should not be extended.'
.format(lib.path), file=sys.stderr)
- def _check_arg_dir_exists(self, arg_name, dirs):
- for path in dirs:
- if not os.path.exists(path):
- print('error: Failed to find the directory "{}" specified in {}'
- .format(path, arg_name), file=sys.stderr)
- sys.exit(1)
- if not os.path.isdir(path):
- print('error: Path "{}" specified in {} is not a directory'
- .format(path, arg_name), file=sys.stderr)
- sys.exit(1)
-
def main(self, args):
- # Check the command line options.
- self._check_arg_dir_exists('--system', args.system)
- self._check_arg_dir_exists('--vendor', args.vendor)
-
- # Load the generic reference.
- generic_refs = None
- if args.load_generic_refs:
- generic_refs = GenericRefs.create_from_dir(args.load_generic_refs)
-
- # Link ELF objects.
- graph = ELFLinker.create(args.system, args.system_dir_as_vendor,
- args.vendor, args.vendor_dir_as_system,
- args.load_extra_deps,
- generic_refs=generic_refs)
+ generic_refs, banned_libs, graph, vndk_customized_for_system, \
+ vndk_customized_for_vendor = self.create_from_args(args)
# Check the API extensions to NDK libraries.
if generic_refs:
self._check_ndk_extensions(graph, generic_refs)
- # Create banned libraries.
- if not args.ban_vendor_lib_dep:
- banned_libs = BannedLibDict.create_default()
- else:
- banned_libs = BannedLibDict()
- for name in args.ban_vendor_lib_dep:
- banned_libs.add(name, 'user-banned', BA_WARN)
-
if args.warn_incorrect_partition:
self._warn_incorrect_partition(graph)
if args.warn_banned_vendor_lib_deps:
self._warn_banned_vendor_lib_deps(graph, banned_libs)
- # User may specify the partition for outward-customized vndk libs. The
- # following code converts the path into ELFLinkData.
- vndk_customized_for_system = set()
- vndk_customized_for_vendor = set()
-
- system_libs = graph.lib_pt[PT_SYSTEM].values()
- if args.outward_customization_default_partition in {'system', 'both'}:
- vndk_customized_for_system.update(system_libs)
-
- if args.outward_customization_default_partition in {'vendor', 'both'}:
- vndk_customized_for_vendor.update(system_libs)
-
- if args.outward_customization_for_system:
- vndk_customized_for_system.update(
- graph.get_libs(
- args.outward_customization_for_system, lambda x: None))
-
- if args.outward_customization_for_vendor:
- vndk_customized_for_vendor.update(
- graph.get_libs(
- args.outward_customization_for_vendor, lambda x: None))
-
# Compute vndk heuristics.
vndk_lib = graph.compute_vndk(vndk_customized_for_system,
vndk_customized_for_vendor, generic_refs,
@@ -1904,6 +1929,128 @@
return 0
+class DepsInsightCommand(VNDKCommandBase):
+ def __init__(self):
+ super(DepsInsightCommand, self).__init__(
+ 'deps-insight', help='Generate HTML to show dependencies')
+
+ def add_argparser_options(self, parser):
+ super(DepsInsightCommand, self).add_argparser_options(parser)
+
+ parser.add_argument(
+ '--output', '-o', help='output directory')
+
+ def main(self, args):
+ generic_refs, banned_libs, graph, vndk_customized_for_system, \
+ vndk_customized_for_vendor = self.create_from_args(args)
+
+ # Compute vndk heuristics.
+ vndk_lib = graph.compute_vndk(vndk_customized_for_system,
+ vndk_customized_for_vendor, generic_refs,
+ banned_libs)
+
+ # Serialize data.
+ strs = []
+ strs_dict = dict()
+
+ libs = list(graph.lib32.values()) + list(graph.lib64.values())
+ libs.sort(key=lambda lib: lib.path)
+ libs_dict = {lib: i for i, lib in enumerate(libs)}
+
+ def get_str_idx(s):
+ try:
+ return strs_dict[s]
+ except KeyError:
+ idx = len(strs)
+ strs_dict[s] = idx
+ strs.append(s)
+ return idx
+
+ def collect_path_sorted_lib_idxs(libs):
+ libs = sorted(libs, key=lambda lib: lib.path)
+ return [libs_dict[lib] for lib in libs]
+
+ def collect_deps(lib):
+ queue = list(lib.deps)
+ visited = set(queue)
+ visited.add(lib)
+ deps = []
+
+ # Traverse dependencies with breadth-first search.
+ while queue:
+ # Collect dependencies for next queue.
+ next_queue = []
+ for lib in queue:
+ for dep in lib.deps:
+ if dep not in visited:
+ next_queue.append(dep)
+ visited.add(dep)
+
+ # Append current queue to result.
+ deps.append(collect_path_sorted_lib_idxs(queue))
+
+ queue = next_queue
+
+ return deps
+
+ def collect_tags(lib):
+ tags = []
+ if lib.is_ll_ndk:
+ tags.append(get_str_idx('ll-ndk'))
+ if lib.is_sp_ndk:
+ tags.append(get_str_idx('sp-ndk'))
+ if lib.is_hl_ndk:
+ tags.append(get_str_idx('hl-ndk'))
+
+ if lib in vndk_lib.sp_hal:
+ tags.append(get_str_idx('sp-hal'))
+ if lib in vndk_lib.sp_hal_dep:
+ tags.append(get_str_idx('sp-hal-dep'))
+ if lib in vndk_lib.sp_hal_vndk_stable:
+ tags.append(get_str_idx('sp-hal-vndk-stable'))
+
+ if lib in vndk_lib.sp_ndk_vndk_stable:
+ tags.append(get_str_idx('sp-ndk-vndk-stable'))
+ if lib in vndk_lib.sp_both_vndk_stable:
+ tags.append(get_str_idx('sp-both-vndk-stable'))
+
+ if lib in vndk_lib.vndk_core:
+ tags.append(get_str_idx('vndk-core'))
+ if lib in vndk_lib.vndk_indirect:
+ tags.append(get_str_idx('vndk-indirect'))
+ if lib in vndk_lib.vndk_fwk_ext:
+ tags.append(get_str_idx('vndk-fwk-ext'))
+ if lib in vndk_lib.vndk_vnd_ext:
+ tags.append(get_str_idx('vndk-vnd-ext'))
+ if lib in vndk_lib.extra_vendor_lib:
+ tags.append(get_str_idx('extra-vendor-lib'))
+ return tags
+
+ mods = []
+ for lib in libs:
+ mods.append([get_str_idx(lib.path),
+ 32 if lib.elf.is_32bit else 64,
+ collect_tags(lib),
+ collect_deps(lib),
+ collect_path_sorted_lib_idxs(lib.users)])
+
+ # Generate output files.
+ makedirs(args.output, exist_ok=True)
+ script_dir = os.path.dirname(os.path.abspath(__file__))
+ for name in ('index.html', 'insight.css', 'insight.js'):
+ shutil.copyfile(os.path.join(script_dir, 'assets', name),
+ os.path.join(args.output, name))
+
+ with open(os.path.join(args.output, 'insight-data.js'), 'w') as f:
+ f.write('''(function () {
+ var strs = ''' + json.dumps(strs) + ''';
+ var mods = ''' + json.dumps(mods) + ''';
+ insight.init(document, strs, mods);
+})();''')
+
+ return 0
+
+
class VNDKCapCommand(ELFGraphCommand):
def __init__(self):
super(VNDKCapCommand, self).__init__(
@@ -2092,6 +2239,7 @@
register_subcmd(VNDKCapCommand())
register_subcmd(DepsCommand())
register_subcmd(DepsClosureCommand())
+ register_subcmd(DepsInsightCommand())
register_subcmd(SpLibCommand())
register_subcmd(VNDKStableCommand())