vndk-def: Hide system-to-vendor deps instead
This commit replaces remove_dep() with hide_dep() so that the dependencies can be kept in deps-insight command. Before this commit, system-to-vendor dependencies will be removed and won't be present in the output of deps-insight command. Test: ./tests/test_vndk.py Test: ./tests/test_command_deps_insight.py Change-Id: I377a81f98a726219e241e79ae6274e9b124de035
This commit is contained in:
@@ -39,3 +39,19 @@ if sys.version_info >= (3, 0):
|
||||
from io import StringIO
|
||||
else:
|
||||
from StringIO import StringIO
|
||||
|
||||
|
||||
try:
|
||||
from unittest.mock import patch
|
||||
except ImportError:
|
||||
import contextlib
|
||||
@contextlib.contextmanager
|
||||
def patch(target, mock):
|
||||
obj, attr = target.rsplit('.')
|
||||
obj = __import__(obj)
|
||||
original_value = getattr(obj, attr)
|
||||
setattr(obj, attr, mock)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
setattr(obj, attr, original_value)
|
||||
|
||||
88
vndk/tools/definition-tool/tests/test_command_deps_insight.py
Executable file
88
vndk/tools/definition-tool/tests/test_command_deps_insight.py
Executable file
@@ -0,0 +1,88 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from compat import StringIO, patch
|
||||
from vndk_definition_tool import (
|
||||
DepsInsightCommand, ModuleInfo, PT_SYSTEM, PT_VENDOR)
|
||||
from utils import GraphBuilder
|
||||
|
||||
|
||||
class DepsInsightCommandTest(unittest.TestCase):
|
||||
_PATH_FIELD = 0
|
||||
_ELF_CLASS_FIELD = 1
|
||||
_TAGS_FIELD = 2
|
||||
_DEPS_FIELD = 3
|
||||
_USERS_FIELD = 4
|
||||
_SOURCE_DIRS_FIELD = 5
|
||||
|
||||
|
||||
@classmethod
|
||||
def _get_module(cls, strs, mods, path):
|
||||
for mod in mods:
|
||||
if strs[mod[cls._PATH_FIELD]] == path:
|
||||
return mod
|
||||
return None
|
||||
|
||||
|
||||
@classmethod
|
||||
def _get_module_deps(cls, strs, mods, path):
|
||||
mod = cls._get_module(strs, mods, path)
|
||||
result = set()
|
||||
for deps in mod[cls._DEPS_FIELD]:
|
||||
result.update(strs[mods[x][cls._PATH_FIELD]] for x in deps)
|
||||
return result
|
||||
|
||||
|
||||
@classmethod
|
||||
def _get_module_users(cls, strs, mods, path):
|
||||
mod = cls._get_module(strs, mods, path)
|
||||
users = mod[cls._USERS_FIELD]
|
||||
return set(strs[mods[x][cls._PATH_FIELD]] for x in users)
|
||||
|
||||
|
||||
def test_serialize_data_with_all_deps(self):
|
||||
"""compute_degenerated_vndk() should not remove bad dependencies from
|
||||
the output of deps-insight. This test checks the existance of bad
|
||||
dependencies."""
|
||||
|
||||
gb = GraphBuilder()
|
||||
libfwk = gb.add_lib32(PT_SYSTEM, 'libfwk')
|
||||
libvndk = gb.add_lib32(PT_SYSTEM, 'libvndk',
|
||||
dt_needed=['libvnd_bad.so'], extra_dir='vndk')
|
||||
libvndk_sp = gb.add_lib32(PT_SYSTEM, 'libutils',
|
||||
dt_needed=['libvnd_bad.so'],
|
||||
extra_dir='vndk-sp')
|
||||
libvnd = gb.add_lib32(PT_VENDOR, 'libvnd',
|
||||
dt_needed=['libvndk.so', 'libutils.so'])
|
||||
libvnd_bad = gb.add_lib32(PT_VENDOR, 'libvnd_bad', extra_dir='vndk-sp')
|
||||
gb.resolve()
|
||||
|
||||
with patch('sys.stderr', StringIO()):
|
||||
vndk_sets = gb.graph.compute_degenerated_vndk(set(), None)
|
||||
|
||||
self.assertNotIn(libvnd_bad, libvndk.deps_good)
|
||||
self.assertNotIn(libvnd_bad, libvndk_sp.deps_good)
|
||||
|
||||
strs, mods = DepsInsightCommand.serialize_data(
|
||||
list(gb.graph.all_libs()), vndk_sets, ModuleInfo())
|
||||
|
||||
deps = self._get_module_deps(strs, mods, libvndk.path)
|
||||
self.assertIn(libvnd_bad.path, deps)
|
||||
|
||||
deps = self._get_module_deps(strs, mods, libvndk_sp.path)
|
||||
self.assertIn(libvnd_bad.path, deps)
|
||||
|
||||
users = self._get_module_users(strs, mods, libvnd_bad.path)
|
||||
self.assertIn(libvndk.path, users)
|
||||
self.assertIn(libvndk_sp.path, users)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -19,35 +19,37 @@ class ELFLinkDataTest(unittest.TestCase):
|
||||
self.w = ELFLinkData(PT_SYSTEM, '/system/lib/libw.so', None, 0)
|
||||
self.v = ELFLinkData(PT_VENDOR, '/vendor/lib/libv.so', None, 0)
|
||||
|
||||
self.x.add_dep(self.y, ELFLinkData.NEEDED)
|
||||
self.x.add_dep(self.z, ELFLinkData.DLOPEN)
|
||||
self.x.add_needed_dep(self.y)
|
||||
self.x.add_dlopen_dep(self.z)
|
||||
|
||||
self.z.add_dep(self.w, ELFLinkData.NEEDED)
|
||||
self.z.add_dep(self.w, ELFLinkData.DLOPEN)
|
||||
self.z.add_needed_dep(self.w)
|
||||
self.z.add_dlopen_dep(self.w)
|
||||
|
||||
def test_add_dep_and_accessors(self):
|
||||
self.assertIn(self.y, self.x.dt_deps)
|
||||
self.assertIn(self.x, self.y.dt_users)
|
||||
self.assertNotIn(self.y, self.x.dl_deps)
|
||||
self.assertNotIn(self.x, self.y.dl_users)
|
||||
self.assertIn(self.y, self.x.deps_needed_all)
|
||||
self.assertIn(self.x, self.y.users_needed_all)
|
||||
self.assertNotIn(self.y, self.x.deps_dlopen_all)
|
||||
self.assertNotIn(self.x, self.y.users_dlopen_all)
|
||||
|
||||
self.assertIn(self.z, self.x.dl_deps)
|
||||
self.assertIn(self.x, self.z.dl_users)
|
||||
self.assertNotIn(self.z, self.x.dt_deps)
|
||||
self.assertNotIn(self.x, self.z.dt_users)
|
||||
self.assertIn(self.z, self.x.deps_dlopen_all)
|
||||
self.assertIn(self.x, self.z.users_dlopen_all)
|
||||
self.assertNotIn(self.z, self.x.deps_needed_all)
|
||||
self.assertNotIn(self.x, self.z.users_needed_all)
|
||||
|
||||
def test_remove_dep(self):
|
||||
self.assertIn(self.y, self.x.dt_deps)
|
||||
self.assertIn(self.x, self.y.dt_users)
|
||||
self.assertIn(self.y, self.x.deps_needed_all)
|
||||
self.assertIn(self.x, self.y.users_needed_all)
|
||||
|
||||
with self.assertRaises(KeyError):
|
||||
self.x.remove_dep(self.y, ELFLinkData.DLOPEN)
|
||||
self.assertIn(self.y, self.x.dt_deps)
|
||||
self.assertIn(self.x, self.y.dt_users)
|
||||
self.x.hide_dlopen_dep(self.y)
|
||||
self.assertIn(self.y, self.x.deps_needed_all)
|
||||
self.assertIn(self.x, self.y.users_needed_all)
|
||||
|
||||
self.x.remove_dep(self.y, ELFLinkData.NEEDED)
|
||||
self.assertNotIn(self.y, self.x.dt_deps)
|
||||
self.assertNotIn(self.x, self.y.dt_users)
|
||||
self.x.hide_needed_dep(self.y)
|
||||
self.assertIn(self.y, self.x.deps_needed_hidden)
|
||||
self.assertIn(self.x, self.y.users_needed_hidden)
|
||||
self.assertNotIn(self.y, self.x.deps_needed)
|
||||
self.assertNotIn(self.x, self.y.users_needed)
|
||||
|
||||
def test_num_deps(self):
|
||||
self.assertEqual(2, self.x.num_deps)
|
||||
|
||||
@@ -1,65 +1,14 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
import unittest
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from compat import StringIO
|
||||
from vndk_definition_tool import (ELF, ELFLinker, GenericRefs, PT_SYSTEM,
|
||||
PT_VENDOR)
|
||||
|
||||
|
||||
class GraphBuilder(object):
|
||||
_PARTITION_NAMES = {
|
||||
PT_SYSTEM: 'system',
|
||||
PT_VENDOR: 'vendor',
|
||||
}
|
||||
|
||||
_LIB_DIRS = {
|
||||
ELF.ELFCLASS32: 'lib',
|
||||
ELF.ELFCLASS64: 'lib64',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.graph = ELFLinker()
|
||||
|
||||
def add_lib(self, partition, klass, name, dt_needed=[],
|
||||
exported_symbols=set(), imported_symbols=set(),
|
||||
extra_dir=None):
|
||||
"""Create and add a shared library to ELFLinker."""
|
||||
|
||||
lib_dir = os.path.join('/', self._PARTITION_NAMES[partition],
|
||||
self._LIB_DIRS[klass])
|
||||
if extra_dir:
|
||||
lib_dir = os.path.join(lib_dir, extra_dir)
|
||||
|
||||
path = os.path.join(lib_dir, name + '.so')
|
||||
|
||||
elf = ELF(klass, ELF.ELFDATA2LSB, dt_needed=dt_needed,
|
||||
exported_symbols=exported_symbols,
|
||||
imported_symbols=imported_symbols)
|
||||
|
||||
node = self.graph.add_lib(partition, path, elf)
|
||||
setattr(self, name + '_' + elf.elf_class_name, node)
|
||||
return node
|
||||
|
||||
def add_multilib(self, partition, name, dt_needed=[],
|
||||
exported_symbols=set(), imported_symbols=set(),
|
||||
extra_dir=None):
|
||||
"""Add 32-bit / 64-bit shared libraries to ELFLinker."""
|
||||
return (
|
||||
self.add_lib(partition, ELF.ELFCLASS32, name, dt_needed,
|
||||
exported_symbols, imported_symbols, extra_dir),
|
||||
self.add_lib(partition, ELF.ELFCLASS64, name, dt_needed,
|
||||
exported_symbols, imported_symbols, extra_dir)
|
||||
)
|
||||
|
||||
def resolve(self):
|
||||
self.graph.resolve_deps()
|
||||
from utils import GraphBuilder
|
||||
from vndk_definition_tool import (ELF, GenericRefs, PT_SYSTEM, PT_VENDOR)
|
||||
|
||||
|
||||
class ELFLinkerTest(unittest.TestCase):
|
||||
@@ -147,18 +96,18 @@ class ELFLinkerTest(unittest.TestCase):
|
||||
# Check the dependencies of libc.so.
|
||||
node = gb.graph.get_lib('/system/lib/libc.so')
|
||||
self.assertEqual(['/system/lib/libdl.so', '/system/lib/libm.so'],
|
||||
self._get_paths_from_nodes(node.deps))
|
||||
self._get_paths_from_nodes(node.deps_all))
|
||||
|
||||
# Check the dependencies of libRS.so.
|
||||
node = gb.graph.get_lib('/system/lib64/libRS.so')
|
||||
self.assertEqual(['/system/lib64/libdl.so'],
|
||||
self._get_paths_from_nodes(node.deps))
|
||||
self._get_paths_from_nodes(node.deps_all))
|
||||
|
||||
# Check the dependencies of libEGL.so.
|
||||
node = gb.graph.get_lib('/vendor/lib64/libEGL.so')
|
||||
self.assertEqual(['/system/lib64/libc.so', '/system/lib64/libcutils.so',
|
||||
'/system/lib64/libdl.so'],
|
||||
self._get_paths_from_nodes(node.deps))
|
||||
self._get_paths_from_nodes(node.deps_all))
|
||||
|
||||
def test_linked_symbols(self):
|
||||
gb = self._create_normal_graph()
|
||||
@@ -217,21 +166,21 @@ class ELFLinkerTest(unittest.TestCase):
|
||||
# Check the users of libc.so.
|
||||
node = graph.get_lib('/system/lib/libc.so')
|
||||
self.assertEqual(['/system/lib/libcutils.so', '/vendor/lib/libEGL.so'],
|
||||
self._get_paths_from_nodes(node.users))
|
||||
self._get_paths_from_nodes(node.users_all))
|
||||
|
||||
# Check the users of libdl.so.
|
||||
node = graph.get_lib('/system/lib/libdl.so')
|
||||
self.assertEqual(['/system/lib/libRS.so', '/system/lib/libc.so',
|
||||
'/system/lib/libcutils.so', '/vendor/lib/libEGL.so'],
|
||||
self._get_paths_from_nodes(node.users))
|
||||
self._get_paths_from_nodes(node.users_all))
|
||||
|
||||
# Check the users of libRS.so.
|
||||
node = graph.get_lib('/system/lib64/libRS.so')
|
||||
self.assertEqual([], self._get_paths_from_nodes(node.users))
|
||||
self.assertEqual([], self._get_paths_from_nodes(node.users_all))
|
||||
|
||||
# Check the users of libEGL.so.
|
||||
node = graph.get_lib('/vendor/lib64/libEGL.so')
|
||||
self.assertEqual([], self._get_paths_from_nodes(node.users))
|
||||
self.assertEqual([], self._get_paths_from_nodes(node.users_all))
|
||||
|
||||
def test_compute_predefined_sp_hal(self):
|
||||
gb = GraphBuilder()
|
||||
|
||||
119
vndk/tools/definition-tool/tests/test_vndk.py
Executable file
119
vndk/tools/definition-tool/tests/test_vndk.py
Executable file
@@ -0,0 +1,119 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from compat import StringIO, patch
|
||||
from vndk_definition_tool import (ELF, ELFLinker, PT_SYSTEM, PT_VENDOR)
|
||||
from utils import GraphBuilder
|
||||
|
||||
|
||||
class ELFLinkerVNDKTest(unittest.TestCase):
|
||||
def test_normalize_partition_tags_bad_vendor_deps(self):
|
||||
"""Check whether normalize_partition_tags() hides the dependencies from
|
||||
the system partition to the vendor partition if the dependencies are
|
||||
not SP-HAL libraries."""
|
||||
|
||||
gb = GraphBuilder()
|
||||
libfwk = gb.add_lib32(PT_SYSTEM, 'libfwk', dt_needed=['libvnd.so'])
|
||||
libvnd = gb.add_lib32(PT_VENDOR, 'libvnd')
|
||||
gb.resolve()
|
||||
|
||||
self.assertIn(libvnd, libfwk.deps_needed)
|
||||
self.assertIn(libfwk, libvnd.users_needed)
|
||||
|
||||
stderr = StringIO()
|
||||
with patch('sys.stderr', stderr):
|
||||
gb.graph.normalize_partition_tags(set(), None)
|
||||
|
||||
self.assertRegex(
|
||||
stderr.getvalue(),
|
||||
'error: .*: system exe/lib must not depend on vendor lib .*. '
|
||||
'Assume such dependency does not exist.')
|
||||
|
||||
self.assertNotIn(libvnd, libfwk.deps_needed)
|
||||
self.assertNotIn(libfwk, libvnd.users_needed)
|
||||
|
||||
self.assertIn(libvnd, libfwk.deps_needed_hidden)
|
||||
self.assertIn(libfwk, libvnd.users_needed_hidden)
|
||||
|
||||
self.assertIn(libvnd, libfwk.deps_all)
|
||||
self.assertIn(libvnd, libfwk.deps_needed_all)
|
||||
self.assertNotIn(libvnd, libfwk.deps_good)
|
||||
|
||||
self.assertIn(libfwk, libvnd.users_all)
|
||||
self.assertIn(libfwk, libvnd.users_needed_all)
|
||||
self.assertNotIn(libfwk, libvnd.users_good)
|
||||
|
||||
|
||||
def test_normalize_partition_tags_sp_hal(self):
|
||||
"""Check whether normalize_partition_tags() keep dependencies to SP-HAL
|
||||
libraries as-is."""
|
||||
|
||||
gb = GraphBuilder()
|
||||
libfwk = gb.add_lib32(PT_SYSTEM, 'libfwk', dt_needed=['libsphal.so'])
|
||||
libsphal = gb.add_lib32(PT_VENDOR, 'libsphal')
|
||||
gb.resolve()
|
||||
|
||||
self.assertIn(libsphal, libfwk.deps_needed)
|
||||
self.assertIn(libfwk, libsphal.users_needed)
|
||||
|
||||
gb.graph.normalize_partition_tags({libsphal}, None)
|
||||
|
||||
# SP-HALs should be kept as-is.
|
||||
self.assertIn(libsphal, libfwk.deps_needed)
|
||||
self.assertIn(libfwk, libsphal.users_needed)
|
||||
|
||||
|
||||
def test_vndk(self):
|
||||
"""Check the computation of vndk without generic references."""
|
||||
|
||||
gb = GraphBuilder()
|
||||
libfwk = gb.add_lib32(PT_SYSTEM, 'libfwk')
|
||||
libvndk = gb.add_lib32(PT_SYSTEM, 'libvndk', extra_dir='vndk')
|
||||
libvndk_sp = gb.add_lib32(PT_SYSTEM, 'libutils', extra_dir='vndk-sp')
|
||||
libvnd = gb.add_lib32(PT_VENDOR, 'libvnd',
|
||||
dt_needed=['libvndk.so', 'libutils.so'])
|
||||
gb.resolve()
|
||||
|
||||
self.assertIn(libvndk, libvnd.deps_all)
|
||||
self.assertIn(libvndk_sp, libvnd.deps_all)
|
||||
|
||||
vndk_sets = gb.graph.compute_degenerated_vndk(None)
|
||||
|
||||
self.assertIn(libvndk, vndk_sets.vndk)
|
||||
self.assertIn(libvndk_sp, vndk_sets.vndk_sp)
|
||||
|
||||
|
||||
def test_vndk_bad_vendor_deps(self):
|
||||
"""Check the computation of vndk without generic references."""
|
||||
|
||||
gb = GraphBuilder()
|
||||
libfwk = gb.add_lib32(PT_SYSTEM, 'libfwk')
|
||||
libvndk = gb.add_lib32(PT_SYSTEM, 'libvndk',
|
||||
dt_needed=['libvnd_bad.so'], extra_dir='vndk')
|
||||
libvndk_sp = gb.add_lib32(PT_SYSTEM, 'libutils',
|
||||
dt_needed=['libvnd_bad.so'],
|
||||
extra_dir='vndk-sp')
|
||||
libvnd = gb.add_lib32(PT_VENDOR, 'libvnd',
|
||||
dt_needed=['libvndk.so', 'libutils.so'])
|
||||
libvnd_bad = gb.add_lib32(PT_VENDOR, 'libvnd_bad', extra_dir='vndk-sp')
|
||||
gb.resolve()
|
||||
|
||||
self.assertIn(libvnd_bad, libvndk.deps_all)
|
||||
self.assertIn(libvnd_bad, libvndk_sp.deps_all)
|
||||
|
||||
with patch('sys.stderr', StringIO()):
|
||||
vndk_sets = gb.graph.compute_degenerated_vndk(None)
|
||||
|
||||
self.assertNotIn(libvnd_bad, vndk_sets.vndk)
|
||||
self.assertNotIn(libvnd_bad, vndk_sets.vndk_sp)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
70
vndk/tools/definition-tool/tests/utils.py
Normal file
70
vndk/tools/definition-tool/tests/utils.py
Normal file
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from compat import StringIO
|
||||
from vndk_definition_tool import (ELF, ELFLinker, PT_SYSTEM, PT_VENDOR)
|
||||
|
||||
|
||||
class GraphBuilder(object):
|
||||
_PARTITION_NAMES = {
|
||||
PT_SYSTEM: 'system',
|
||||
PT_VENDOR: 'vendor',
|
||||
}
|
||||
|
||||
_LIB_DIRS = {
|
||||
ELF.ELFCLASS32: 'lib',
|
||||
ELF.ELFCLASS64: 'lib64',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self.graph = ELFLinker()
|
||||
|
||||
def add_lib(self, partition, klass, name, dt_needed=[],
|
||||
exported_symbols=set(), imported_symbols=set(),
|
||||
extra_dir=None):
|
||||
"""Create and add a shared library to ELFLinker."""
|
||||
|
||||
lib_dir = os.path.join('/', self._PARTITION_NAMES[partition],
|
||||
self._LIB_DIRS[klass])
|
||||
if extra_dir:
|
||||
lib_dir = os.path.join(lib_dir, extra_dir)
|
||||
|
||||
path = os.path.join(lib_dir, name + '.so')
|
||||
|
||||
elf = ELF(klass, ELF.ELFDATA2LSB, dt_needed=dt_needed,
|
||||
exported_symbols=exported_symbols,
|
||||
imported_symbols=imported_symbols)
|
||||
|
||||
lib = self.graph.add_lib(partition, path, elf)
|
||||
setattr(self, name + '_' + elf.elf_class_name, lib)
|
||||
return lib
|
||||
|
||||
def add_lib32(self, partition, name, dt_needed=[],
|
||||
exported_symbols=set(), imported_symbols=set(),
|
||||
extra_dir=None):
|
||||
return self.add_lib(partition, ELF.ELFCLASS32, name, dt_needed,
|
||||
exported_symbols, imported_symbols, extra_dir)
|
||||
|
||||
def add_lib64(self, partition, name, dt_needed=[],
|
||||
exported_symbols=set(), imported_symbols=set(),
|
||||
extra_dir=None):
|
||||
return self.add_lib(partition, ELF.ELFCLASS64, name, dt_needed,
|
||||
exported_symbols, imported_symbols, extra_dir)
|
||||
|
||||
def add_multilib(self, partition, name, dt_needed=[],
|
||||
exported_symbols=set(), imported_symbols=set(),
|
||||
extra_dir=None):
|
||||
"""Add 32-bit / 64-bit shared libraries to ELFLinker."""
|
||||
return (
|
||||
self.add_lib(partition, ELF.ELFCLASS32, name, dt_needed,
|
||||
exported_symbols, imported_symbols, extra_dir),
|
||||
self.add_lib(partition, ELF.ELFCLASS64, name, dt_needed,
|
||||
exported_symbols, imported_symbols, extra_dir)
|
||||
)
|
||||
|
||||
def resolve(self):
|
||||
self.graph.resolve_deps()
|
||||
@@ -893,15 +893,18 @@ class ELFResolver(object):
|
||||
|
||||
|
||||
class ELFLinkData(object):
|
||||
NEEDED = 0 # Dependencies recorded in DT_NEEDED entries.
|
||||
DLOPEN = 1 # Dependencies introduced by dlopen().
|
||||
|
||||
def __init__(self, partition, path, elf, tag_bit):
|
||||
self.partition = partition
|
||||
self.path = path
|
||||
self.elf = elf
|
||||
self._deps = (set(), set())
|
||||
self._users = (set(), set())
|
||||
self.deps_needed = set()
|
||||
self.deps_needed_hidden = set()
|
||||
self.deps_dlopen = set()
|
||||
self.deps_dlopen_hidden = set()
|
||||
self.users_needed = set()
|
||||
self.users_needed_hidden = set()
|
||||
self.users_dlopen = set()
|
||||
self.users_dlopen_hidden = set()
|
||||
self.imported_ext_symbols = collections.defaultdict(set)
|
||||
self._tag_bit = tag_bit
|
||||
self.unresolved_symbols = set()
|
||||
@@ -936,67 +939,85 @@ class ELFLinkData(object):
|
||||
def is_fwk_only_rs(self):
|
||||
return TaggedDict.is_fwk_only_rs(self._tag_bit)
|
||||
|
||||
def add_dep(self, dst, ty):
|
||||
self._deps[ty].add(dst)
|
||||
dst._users[ty].add(self)
|
||||
def add_needed_dep(self, dst):
|
||||
assert dst not in self.deps_needed_hidden
|
||||
assert self not in dst.users_needed_hidden
|
||||
self.deps_needed.add(dst)
|
||||
dst.users_needed.add(self)
|
||||
|
||||
def remove_dep(self, dst, ty):
|
||||
self._deps[ty].remove(dst)
|
||||
dst._users[ty].remove(self)
|
||||
def add_dlopen_dep(self, dst):
|
||||
assert dst not in self.deps_dlopen_hidden
|
||||
assert self not in dst.users_dlopen_hidden
|
||||
self.deps_dlopen.add(dst)
|
||||
dst.users_dlopen.add(self)
|
||||
|
||||
def hide_needed_dep(self, dst):
|
||||
self.deps_needed.remove(dst)
|
||||
dst.users_needed.remove(self)
|
||||
self.deps_needed_hidden.add(dst)
|
||||
dst.users_needed_hidden.add(self)
|
||||
|
||||
def hide_dlopen_dep(self, dst):
|
||||
self.deps_dlopen.remove(dst)
|
||||
dst.users_dlopen.remove(self)
|
||||
self.deps_dlopen_hidden.add(dst)
|
||||
dst.users_dlopen_hidden.add(self)
|
||||
|
||||
@property
|
||||
def num_deps(self):
|
||||
"""Get the number of dependencies. If a library is linked by both
|
||||
NEEDED and DLOPEN relationship, then it will be counted twice."""
|
||||
return sum(len(deps) for deps in self._deps)
|
||||
return (len(self.deps_needed) + len(self.deps_needed_hidden) +
|
||||
len(self.deps_dlopen) + len(self.deps_dlopen_hidden))
|
||||
|
||||
@property
|
||||
def deps(self):
|
||||
return itertools.chain.from_iterable(self._deps)
|
||||
def deps_all(self):
|
||||
return itertools.chain(self.deps_needed, self.deps_needed_hidden,
|
||||
self.deps_dlopen, self.deps_dlopen_hidden)
|
||||
|
||||
@property
|
||||
def deps_with_type(self):
|
||||
dt_deps = zip(self._deps[self.NEEDED], itertools.repeat(self.NEEDED))
|
||||
dl_deps = zip(self._deps[self.DLOPEN], itertools.repeat(self.DLOPEN))
|
||||
return itertools.chain(dt_deps, dl_deps)
|
||||
def deps_good(self):
|
||||
return itertools.chain(self.deps_needed, self.deps_dlopen)
|
||||
|
||||
@property
|
||||
def dt_deps(self):
|
||||
return self._deps[self.NEEDED]
|
||||
def deps_needed_all(self):
|
||||
return itertools.chain(self.deps_needed, self.deps_needed_hidden)
|
||||
|
||||
@property
|
||||
def dl_deps(self):
|
||||
return self._deps[self.DLOPEN]
|
||||
def deps_dlopen_all(self):
|
||||
return itertools.chain(self.deps_dlopen, self.deps_dlopen_hidden)
|
||||
|
||||
@property
|
||||
def num_users(self):
|
||||
"""Get the number of users. If a library is linked by both NEEDED and
|
||||
DLOPEN relationship, then it will be counted twice."""
|
||||
return sum(len(users) for users in self._users)
|
||||
return (len(self.users_needed) + len(self.users_needed_hidden) +
|
||||
len(self.users_dlopen) + len(self.users_dlopen_hidden))
|
||||
|
||||
@property
|
||||
def users(self):
|
||||
return itertools.chain.from_iterable(self._users)
|
||||
def users_all(self):
|
||||
return itertools.chain(self.users_needed, self.users_needed_hidden,
|
||||
self.users_dlopen, self.users_dlopen_hidden)
|
||||
|
||||
@property
|
||||
def users_with_type(self):
|
||||
dt_users = zip(self._users[self.NEEDED], itertools.repeat(self.NEEDED))
|
||||
dl_users = zip(self._users[self.DLOPEN], itertools.repeat(self.DLOPEN))
|
||||
return itertools.chain(dt_users, dl_users)
|
||||
def users_good(self):
|
||||
return itertools.chain(self.users_needed, self.users_dlopen)
|
||||
|
||||
@property
|
||||
def dt_users(self):
|
||||
return self._users[self.NEEDED]
|
||||
def users_needed_all(self):
|
||||
return itertools.chain(self.users_needed, self.users_needed_hidden)
|
||||
|
||||
@property
|
||||
def dl_users(self):
|
||||
return self._users[self.DLOPEN]
|
||||
def users_dlopen_all(self):
|
||||
return itertools.chain(self.users_dlopen, self.users_dlopen_hidden)
|
||||
|
||||
def has_dep(self, dst):
|
||||
return any(dst in deps for deps in self._deps)
|
||||
return (dst in self.deps_needed or dst in self.deps_needed_hidden or
|
||||
dst in self.deps_dlopen or dst in self.deps_dlopen_hidden)
|
||||
|
||||
def has_user(self, dst):
|
||||
return any(dst in users for users in self._users)
|
||||
return (dst in self.users_needed or dst in self.users_needed_hidden or
|
||||
dst in self.users_dlopen or dst in self.users_dlopen_hidden)
|
||||
|
||||
def is_system_lib(self):
|
||||
return self.partition == PT_SYSTEM
|
||||
@@ -1091,12 +1112,12 @@ class ELFLinker(object):
|
||||
lib.partition = new_partition
|
||||
self._add_lib_to_lookup_dict(lib)
|
||||
|
||||
def add_dep(self, src_path, dst_path, ty):
|
||||
def add_dlopen_dep(self, src_path, dst_path):
|
||||
for elf_class in (ELF.ELFCLASS32, ELF.ELFCLASS64):
|
||||
src = self.get_lib_in_elf_class(elf_class, src_path)
|
||||
dst = self.get_lib_in_elf_class(elf_class, dst_path)
|
||||
if src and dst:
|
||||
src.add_dep(dst, ty)
|
||||
src.add_dlopen_dep(dst)
|
||||
return
|
||||
print('error: cannot add dependency from {} to {}.'
|
||||
.format(src_path, dst_path), file=sys.stderr)
|
||||
@@ -1176,8 +1197,7 @@ class ELFLinker(object):
|
||||
for line in f:
|
||||
match = patt.match(line)
|
||||
if match:
|
||||
self.add_dep(match.group(1), match.group(2),
|
||||
ELFLinkData.DLOPEN)
|
||||
self.add_dlopen_dep(match.group(1), match.group(2))
|
||||
|
||||
def _find_exported_symbol(self, symbol, libs):
|
||||
"""Find the shared library with the exported symbol."""
|
||||
@@ -1211,7 +1231,7 @@ class ELFLinker(object):
|
||||
.format(lib.path, dt_needed, candidates), file=sys.stderr)
|
||||
lib.unresolved_dt_needed.append(dt_needed)
|
||||
continue
|
||||
lib.add_dep(dep, ELFLinkData.NEEDED)
|
||||
lib.add_needed_dep(dep)
|
||||
imported_libs.append(dep)
|
||||
return imported_libs
|
||||
|
||||
@@ -1368,16 +1388,18 @@ class ELFLinker(object):
|
||||
"""Find all SP-NDK libraries."""
|
||||
return set(lib for lib in self.all_libs() if lib.is_sp_ndk)
|
||||
|
||||
def compute_sp_lib(self, generic_refs):
|
||||
def compute_sp_lib(self, generic_refs, ignore_hidden_deps=False):
|
||||
def is_ndk(lib):
|
||||
return lib.is_ndk
|
||||
|
||||
sp_ndk = self.compute_sp_ndk()
|
||||
sp_ndk_closure = self.compute_deps_closure(sp_ndk, is_ndk)
|
||||
sp_ndk_closure = self.compute_deps_closure(
|
||||
sp_ndk, is_ndk, ignore_hidden_deps)
|
||||
sp_ndk_indirect = sp_ndk_closure - sp_ndk
|
||||
|
||||
sp_hal = self.compute_predefined_sp_hal()
|
||||
sp_hal_closure = self.compute_deps_closure(sp_hal, is_ndk)
|
||||
sp_hal_closure = self.compute_deps_closure(
|
||||
sp_hal, is_ndk, ignore_hidden_deps)
|
||||
|
||||
def is_aosp_lib(lib):
|
||||
return (not generic_refs or \
|
||||
@@ -1414,10 +1436,10 @@ class ELFLinker(object):
|
||||
return result
|
||||
|
||||
def _deps_po_sorted(self, lib_set):
|
||||
return self._po_sorted(lib_set, lambda x: x.deps)
|
||||
return self._po_sorted(lib_set, lambda x: x.deps_all)
|
||||
|
||||
def _users_po_sorted(self, lib_set):
|
||||
return self._po_sorted(lib_set, lambda x: x.users)
|
||||
return self._po_sorted(lib_set, lambda x: x.users_all)
|
||||
|
||||
def normalize_partition_tags(self, sp_hals, generic_refs):
|
||||
system_libs = set(self.lib_pt[PT_SYSTEM].values())
|
||||
@@ -1427,31 +1449,31 @@ class ELFLinker(object):
|
||||
return lib.is_system_lib() or lib in sp_hals
|
||||
|
||||
for lib in system_libs_po:
|
||||
if all(is_system_lib_or_sp_hal(dep) for dep in lib.deps):
|
||||
if all(is_system_lib_or_sp_hal(dep) for dep in lib.deps_all):
|
||||
# Good system lib. Do nothing.
|
||||
continue
|
||||
if not generic_refs or generic_refs.refs.get(lib.path):
|
||||
# If lib is in AOSP generic reference, then we assume that the
|
||||
# non-SP-HAL dependencies are errors. Emit errors and remove
|
||||
# the dependencies.
|
||||
for dep in list(lib.dt_deps):
|
||||
for dep in list(lib.deps_needed_all):
|
||||
if not is_system_lib_or_sp_hal(dep):
|
||||
print('error: {}: system exe/lib must not depend on '
|
||||
'vendor lib {}. Assume such dependency does '
|
||||
'not exist.'.format(lib.path, dep.path),
|
||||
file=sys.stderr)
|
||||
lib.remove_dep(dep, ELFLinkData.NEEDED)
|
||||
for dep in list(lib.dl_deps):
|
||||
lib.hide_needed_dep(dep)
|
||||
for dep in list(lib.deps_dlopen_all):
|
||||
if not is_system_lib_or_sp_hal(dep):
|
||||
print('error: {}: system exe/lib must not dlopen() '
|
||||
'vendor lib {}. Assume such dependency does '
|
||||
'not exist.'.format(lib.path, dep.path),
|
||||
file=sys.stderr)
|
||||
lib.remove_dep(dep, ELFLinkData.DLOPEN)
|
||||
lib.hide_dlopen_dep(dep)
|
||||
else:
|
||||
# If lib is not in AOSP generic reference, then we assume that
|
||||
# lib must be moved to vendor partition.
|
||||
for dep in lib.deps:
|
||||
for dep in lib.deps_all:
|
||||
if not is_system_lib_or_sp_hal(dep):
|
||||
print('warning: {}: system exe/lib must not depend on '
|
||||
'vendor lib {}. Assuming {} should be placed in '
|
||||
@@ -1517,7 +1539,7 @@ class ELFLinker(object):
|
||||
return True
|
||||
return is_aosp_lib(lib)
|
||||
|
||||
sp_hal_dep = self.compute_deps_closure(sp_hal, is_not_sp_hal_dep)
|
||||
sp_hal_dep = self.compute_deps_closure(sp_hal, is_not_sp_hal_dep, True)
|
||||
sp_hal_dep -= sp_hal
|
||||
|
||||
# Find VNDK-SP libs.
|
||||
@@ -1529,7 +1551,7 @@ class ELFLinker(object):
|
||||
self._parse_action_on_ineligible_lib(action_ineligible_vndk_sp)
|
||||
vndk_sp = set()
|
||||
for lib in itertools.chain(sp_hal, sp_hal_dep):
|
||||
for dep in lib.deps:
|
||||
for dep in lib.deps_all:
|
||||
if is_not_vndk_sp(dep):
|
||||
continue
|
||||
if dep in predefined_vndk_sp:
|
||||
@@ -1548,7 +1570,7 @@ class ELFLinker(object):
|
||||
lib in fwk_only_rs
|
||||
|
||||
vndk_sp_indirect = self.compute_deps_closure(
|
||||
vndk_sp, is_not_vndk_sp_indirect)
|
||||
vndk_sp, is_not_vndk_sp_indirect, True)
|
||||
vndk_sp_indirect -= vndk_sp
|
||||
|
||||
# Find unused predefined VNDK-SP libs.
|
||||
@@ -1561,7 +1583,7 @@ class ELFLinker(object):
|
||||
def is_not_vndk_sp_indirect_unused(lib):
|
||||
return is_not_vndk_sp_indirect(lib) or lib in vndk_sp_indirect
|
||||
vndk_sp_unused_deps = self.compute_deps_closure(
|
||||
vndk_sp_unused, is_not_vndk_sp_indirect_unused)
|
||||
vndk_sp_unused, is_not_vndk_sp_indirect_unused, True)
|
||||
vndk_sp_unused_deps -= vndk_sp_unused
|
||||
|
||||
vndk_sp_indirect_unused = set(lib for lib in predefined_vndk_sp_indirect
|
||||
@@ -1600,7 +1622,7 @@ class ELFLinker(object):
|
||||
|
||||
# Add the dependencies to vndk_sp_indirect if they are not vndk_sp.
|
||||
closure = self.compute_deps_closure(
|
||||
{lib}, lambda lib: lib not in vndk_sp_indirect_unused)
|
||||
{lib}, lambda lib: lib not in vndk_sp_indirect_unused, True)
|
||||
closure.remove(lib)
|
||||
vndk_sp_indirect_unused.difference_update(closure)
|
||||
vndk_sp_indirect.update(closure)
|
||||
@@ -1626,7 +1648,7 @@ class ELFLinker(object):
|
||||
result = set()
|
||||
for lib in libs:
|
||||
exts = set(lib.imported_ext_symbols.keys())
|
||||
for dep in lib.deps:
|
||||
for dep in lib.deps_all:
|
||||
if not is_vndk_sp_public(dep):
|
||||
continue
|
||||
if dep in vndk_sp_ext or dep in vndk_sp_indirect_ext:
|
||||
@@ -1672,7 +1694,7 @@ class ELFLinker(object):
|
||||
def collect_vndk(vendor_libs):
|
||||
next_vendor_libs = set()
|
||||
for lib in vendor_libs:
|
||||
for dep in lib.deps:
|
||||
for dep in lib.deps_all:
|
||||
if is_vndk_sp_unused(dep):
|
||||
relabel_vndk_sp_as_used(dep)
|
||||
continue
|
||||
@@ -1700,7 +1722,7 @@ class ELFLinker(object):
|
||||
while candidates:
|
||||
candidates = collect_vndk(candidates)
|
||||
|
||||
vndk_indirect = self.compute_deps_closure(vndk, is_not_vndk)
|
||||
vndk_indirect = self.compute_deps_closure(vndk, is_not_vndk, True)
|
||||
vndk_indirect -= vndk
|
||||
|
||||
def is_vndk(lib):
|
||||
@@ -1733,7 +1755,7 @@ class ELFLinker(object):
|
||||
return lib.is_ll_ndk or is_vndk_sp(lib) or is_vndk(lib)
|
||||
|
||||
ll_ndk_indirect = self.compute_deps_closure(
|
||||
ll_ndk, is_not_ll_ndk_indirect)
|
||||
ll_ndk, is_not_ll_ndk_indirect, True)
|
||||
ll_ndk_indirect -= ll_ndk
|
||||
|
||||
def is_not_sp_ndk_indirect(lib):
|
||||
@@ -1741,7 +1763,7 @@ class ELFLinker(object):
|
||||
is_vndk_sp(lib) or is_vndk(lib)
|
||||
|
||||
sp_ndk_indirect = self.compute_deps_closure(
|
||||
sp_ndk, is_not_sp_ndk_indirect)
|
||||
sp_ndk, is_not_sp_ndk_indirect, True)
|
||||
sp_ndk_indirect -= sp_ndk
|
||||
|
||||
# Return the VNDK classifications.
|
||||
@@ -1782,12 +1804,18 @@ class ELFLinker(object):
|
||||
return closure
|
||||
|
||||
@classmethod
|
||||
def compute_deps_closure(cls, root_set, is_excluded):
|
||||
return cls._compute_closure(root_set, is_excluded, lambda x: x.deps)
|
||||
def compute_deps_closure(cls, root_set, is_excluded,
|
||||
ignore_hidden_deps=False):
|
||||
get_successors = (lambda x: x.deps_good) if ignore_hidden_deps else \
|
||||
(lambda x: x.deps_all)
|
||||
return cls._compute_closure(root_set, is_excluded, get_successors)
|
||||
|
||||
@classmethod
|
||||
def compute_users_closure(cls, root_set, is_excluded):
|
||||
return cls._compute_closure(root_set, is_excluded, lambda x: x.users)
|
||||
def compute_users_closure(cls, root_set, is_excluded,
|
||||
ignore_hidden_users=False):
|
||||
get_successors = (lambda x: x.users_good) if ignore_hidden_users else \
|
||||
(lambda x: x.users_all)
|
||||
return cls._compute_closure(root_set, is_excluded, get_successors)
|
||||
|
||||
@staticmethod
|
||||
def _create_internal(scan_elf_files, system_dirs, system_dirs_as_vendor,
|
||||
@@ -2145,7 +2173,7 @@ class VNDKCommand(VNDKCommandBase):
|
||||
for lib in lib_set.values():
|
||||
if not lib.num_users:
|
||||
continue
|
||||
if all((user.partition != partition for user in lib.users)):
|
||||
if all((user.partition != partition for user in lib.users_all)):
|
||||
print(error_msg.format(lib.path), file=sys.stderr)
|
||||
|
||||
def _warn_incorrect_partition(self, graph):
|
||||
@@ -2325,21 +2353,11 @@ class DepsInsightCommand(VNDKCommandBase):
|
||||
parser.add_argument(
|
||||
'--output', '-o', help='output directory')
|
||||
|
||||
def main(self, args):
|
||||
generic_refs, graph, tagged_paths = self.create_from_args(args)
|
||||
|
||||
module_info = ModuleInfo.load_from_path_or_default(args.module_info)
|
||||
|
||||
# Compute vndk heuristics.
|
||||
vndk_lib = graph.compute_degenerated_vndk(
|
||||
generic_refs, tagged_paths, args.action_ineligible_vndk_sp,
|
||||
args.action_ineligible_vndk)
|
||||
|
||||
# Serialize data.
|
||||
@staticmethod
|
||||
def serialize_data(libs, vndk_lib, module_info):
|
||||
strs = []
|
||||
strs_dict = dict()
|
||||
|
||||
libs = list(graph.all_libs())
|
||||
libs.sort(key=lambda lib: lib.path)
|
||||
libs_dict = {lib: i for i, lib in enumerate(libs)}
|
||||
|
||||
@@ -2356,7 +2374,7 @@ class DepsInsightCommand(VNDKCommandBase):
|
||||
return [libs_dict[lib] for lib in sorted(libs)]
|
||||
|
||||
def collect_deps(lib):
|
||||
queue = list(lib.deps)
|
||||
queue = list(lib.deps_all)
|
||||
visited = set(queue)
|
||||
visited.add(lib)
|
||||
deps = []
|
||||
@@ -2366,7 +2384,7 @@ class DepsInsightCommand(VNDKCommandBase):
|
||||
# Collect dependencies for next queue.
|
||||
next_queue = []
|
||||
for lib in queue:
|
||||
for dep in lib.deps:
|
||||
for dep in lib.deps_all:
|
||||
if dep not in visited:
|
||||
next_queue.append(dep)
|
||||
visited.add(dep)
|
||||
@@ -2395,9 +2413,25 @@ class DepsInsightCommand(VNDKCommandBase):
|
||||
32 if lib.elf.is_32bit else 64,
|
||||
collect_tags(lib),
|
||||
collect_deps(lib),
|
||||
collect_path_sorted_lib_idxs(lib.users),
|
||||
collect_path_sorted_lib_idxs(lib.users_all),
|
||||
collect_source_dir_paths(lib)])
|
||||
|
||||
return (strs, mods)
|
||||
|
||||
def main(self, args):
|
||||
generic_refs, graph, tagged_paths = self.create_from_args(args)
|
||||
|
||||
module_info = ModuleInfo.load_from_path_or_default(args.module_info)
|
||||
|
||||
# Compute vndk heuristics.
|
||||
vndk_lib = graph.compute_degenerated_vndk(
|
||||
generic_refs, tagged_paths, args.action_ineligible_vndk_sp,
|
||||
args.action_ineligible_vndk)
|
||||
|
||||
# Serialize data.
|
||||
strs, mods = self.serialize_data(list(graph.all_libs), vndk_lib,
|
||||
module_info)
|
||||
|
||||
# Generate output files.
|
||||
makedirs(args.output, exist_ok=True)
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
@@ -2454,11 +2488,11 @@ class DepsCommand(ELFGraphCommand):
|
||||
|
||||
data = []
|
||||
if args.revert:
|
||||
for assoc_lib in sorted(lib.users):
|
||||
for assoc_lib in sorted(lib.users_all):
|
||||
data.append((assoc_lib.path,
|
||||
collect_symbols(assoc_lib, lib)))
|
||||
else:
|
||||
for assoc_lib in sorted(lib.deps):
|
||||
for assoc_lib in sorted(lib.deps_all):
|
||||
data.append((assoc_lib.path,
|
||||
collect_symbols(lib, assoc_lib)))
|
||||
results.append((name, data))
|
||||
@@ -2639,7 +2673,7 @@ class CheckDepCommand(CheckDepCommandBase):
|
||||
file=sys.stderr)
|
||||
|
||||
# Check whether vendor modules depend on ineligible libs.
|
||||
for dep in lib.deps:
|
||||
for dep in lib.deps_all:
|
||||
if dep not in vendor_libs and dep not in eligible_libs:
|
||||
num_errors += 1
|
||||
bad_deps.add(dep)
|
||||
@@ -2688,7 +2722,7 @@ class CheckEligibleListCommand(CheckDepCommandBase):
|
||||
delimiter = ''
|
||||
for lib in sorted(eligible_libs):
|
||||
bad_deps = []
|
||||
for dep in lib.deps:
|
||||
for dep in lib.deps_all:
|
||||
if dep not in eligible_libs and dep not in indirect_libs:
|
||||
print('error: eligible lib "{}" should not depend on '
|
||||
'non-eligible lib "{}".'.format(lib.path, dep.path),
|
||||
@@ -2702,7 +2736,7 @@ class CheckEligibleListCommand(CheckDepCommandBase):
|
||||
# Check the libbinder dependencies.
|
||||
for lib in sorted(eligible_libs):
|
||||
bad_deps = []
|
||||
for dep in lib.deps:
|
||||
for dep in lib.deps_all:
|
||||
if os.path.basename(dep.path) == 'libbinder.so':
|
||||
print('error: eligible lib "{}" should not depend on '
|
||||
'libbinder.so.'.format(lib.path), file=sys.stderr)
|
||||
@@ -2773,7 +2807,7 @@ class DepGraphCommand(ELFGraphCommand):
|
||||
'depends': [],
|
||||
'violates': [],
|
||||
}
|
||||
for dep in lib.deps:
|
||||
for dep in lib.deps_all:
|
||||
if self._check_if_allowed(tag,
|
||||
self._get_tag_from_lib(dep, tagged_paths)):
|
||||
lib_item['depends'].append(dep.path)
|
||||
|
||||
Reference in New Issue
Block a user