1#!/usr/bin/env python3
2# coding: utf-8
3# Copyright (c) 2023 Huawei Device Co., Ltd.
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#     http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16import os
17import subprocess
18
19
20def run_cmd(cmd: str):
21    res = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE,
22                           stderr=subprocess.PIPE)
23    sout, serr = res.communicate(timeout=60)
24
25    return res.pid, res.returncode, sout, serr
26
27
28def get_needed_lib(file_path: str) -> list:
29    cmd = " ".join(["readelf", "-d", file_path])
30    res = run_cmd(cmd)
31    if res[1] != 0:
32        print("error run readelf -d {}".format(file_path))
33        print(" ".join(["pid ", str(res[0]), " ret ", str(res[1]), "\n",
34                        res[2].decode(), res[3].decode()]))
35        return []
36    needed_lib_name = []
37    lib_info = res[2].decode().split()
38    for i, val in enumerate(lib_info):
39        # lib_info : ... (NEEDED) Shared library: [libc++.so] ...
40        if val == "(NEEDED)" and lib_info[i + 3].startswith("[") and lib_info[i + 3].endswith("]"):
41            needed_lib_name.append(lib_info[i + 3][1 : -1])
42    return needed_lib_name
43
44
45def judge_lib_available(lib_name: str, lib_chain: list, available_libs: list, lib_to_path: dict) -> bool:
46    if lib_name in available_libs:
47        return True
48    lib_path = lib_to_path.get(lib_name)
49    if lib_path is None:
50        return False
51    for next_lib in get_needed_lib(lib_path):
52        if next_lib not in lib_chain:
53            lib_chain.append(next_lib)
54            if not judge_lib_available(next_lib, lib_chain, available_libs, lib_to_path):
55                return False
56            lib_chain.remove(next_lib)
57    available_libs.add(lib_name)
58    return True
59
60
61def judge_updater_available(updater_root_path: str) -> bool:
62    lib_to_path = dict()
63    for path in [os.path.join(updater_root_path, "lib64"), os.path.join(updater_root_path, "lib")]:
64        for root, dirs, files in os.walk(path):
65            for file in files:
66                lib_to_path[file] = os.path.join(root, file)
67    lib_to_path["updater"] = os.path.join(updater_root_path, "bin", "updater")
68    lib_chain = ["updater"]
69    available_libs = set()
70    if not judge_lib_available("updater", lib_chain, available_libs, lib_to_path):
71        print("Reason:  not allow updater to depend dynamic library which not exist in updater.img. {}"\
72              .format("->".join(lib_chain)))
73        print("Solution:  add updater in install_images field when compiling {}".format(lib_chain[-1]))
74        return False
75    return True
76
77
78def judge_updater_img_available(updater_root_path: str) -> bool:
79    return judge_updater_available(updater_root_path)
80