| #!/usr/bin/env python | |
| # | |
| # Copyright (C) 2018 The Android Open Source Project | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| # | |
| import glob | |
| import os.path | |
| import re | |
| import sys | |
| PREBUILTS_VNDK_DIR = "prebuilts/vndk" | |
| VENDOR_DIRECTORIES = ('/vendor', '/odm') | |
| def find_latest_vndk_snapshot_version(): | |
| """Returns latest vndk snapshot version in current source tree. | |
| It will skip the test if the snapshot directories are not found. | |
| Returns: | |
| latest_version: string | |
| """ | |
| vndk_dir_list = glob.glob(PREBUILTS_VNDK_DIR + "/v*") | |
| if not vndk_dir_list: | |
| """Exit without error because we may have source trees that do not include | |
| VNDK snapshot directories in it. | |
| """ | |
| sys.exit(0) | |
| vndk_ver_list = [re.match(r".*/v(\d+)", vndk_dir).group(1) | |
| for vndk_dir in vndk_dir_list] | |
| latest_version = max(vndk_ver_list) | |
| if latest_version == '27': | |
| """Exit without error because VNDK v27 is not using ld.config.txt template | |
| """ | |
| sys.exit(0) | |
| return latest_version | |
| def get_vendor_configuration(ld_config_file): | |
| """Reads the ld.config.txt file to parse the namespace configurations. | |
| It finds the configurations that include vendor directories. | |
| Args: | |
| ld_config_file: string, path (relative to build top) of the ld.config.txt | |
| file. | |
| Returns: | |
| configs: dict{string:[string]}, dictionary of namespace configurations. | |
| it has 'section + property' names as keys and the directory list | |
| as values. | |
| """ | |
| try: | |
| conf_file = open(ld_config_file) | |
| except IOError: | |
| print("error: could not read %s" % ld_config_file) | |
| sys.exit(1) | |
| configs = dict() | |
| current_section = None | |
| with conf_file: | |
| for line in conf_file: | |
| # ignore comments | |
| found = line.find('#') | |
| if found != -1: | |
| line = line[:found] | |
| line = line.strip() | |
| if not line: | |
| continue | |
| if line[0] == '[' and line[-1] == ']': | |
| # new section started | |
| current_section = line[1:-1] | |
| continue | |
| if current_section == None: | |
| continue | |
| found = line.find('+=') | |
| opr_len = 2 | |
| if found == -1: | |
| found = line.find('=') | |
| opr_len = 1 | |
| if found == -1: | |
| continue | |
| namespace = line[:found].strip() | |
| if not namespace.endswith(".paths"): | |
| # check ".paths" only | |
| continue | |
| namespace = '[' + current_section + ']' + namespace | |
| values = line[found + opr_len:].strip() | |
| directories = values.split(':') | |
| for directory in directories: | |
| if any(vendor_dir in directory for vendor_dir in VENDOR_DIRECTORIES): | |
| if namespace in configs: | |
| configs[namespace].append(directory) | |
| else: | |
| configs[namespace] = [directory] | |
| return configs | |
| def get_snapshot_config(version): | |
| """Finds the ld.config.{version}.txt file from the VNDK snapshot directory. | |
| In the vndk prebuilt directory (prebuilts/vndk/v{version}), it searches | |
| {arch}/configs/ld.config.{version}.txt file, where {arch} is one of ('arm64', | |
| 'arm', 'x86_64', 'x86'). | |
| Args: | |
| version: string, the VNDK snapshot version to search. | |
| Returns: | |
| ld_config_file: string, relative path to ld.config.{version}.txt | |
| """ | |
| arch_list = ('arm64', 'arm', 'x86_64', 'x86') | |
| for arch in arch_list: | |
| ld_config_file = (PREBUILTS_VNDK_DIR | |
| + "/v{0}/{1}/configs/ld.config.{0}.txt".format(version, arch)) | |
| if os.path.isfile(ld_config_file): | |
| return ld_config_file | |
| print("error: cannot find ld.config.{0}.txt file in snapshot v{0}" | |
| .format(version)) | |
| sys.exit(1) | |
| def check_backward_compatibility(ld_config, vndk_snapshot_version): | |
| """Checks backward compatibility for current ld.config.txt file with the | |
| old ld.config.txt file. If any of the vendor directories in the old namespace | |
| configurations are missing, the test will fail. It is allowed to have new | |
| vendor directories in current ld.config.txt file. | |
| Args: | |
| ld_config: string, relative path to current ld.config.txt file. | |
| vndk_snapshot_version: string, the VNDK snapshot version that has an old | |
| ld.config.txt file to compare. | |
| Returns: | |
| result: bool, True if the current configuration is backward compatible. | |
| """ | |
| current_config = get_vendor_configuration(ld_config) | |
| old_config = get_vendor_configuration( | |
| get_snapshot_config(vndk_snapshot_version)) | |
| for namespace in old_config: | |
| if namespace not in current_config: | |
| print("error: cannot find %s which was provided in ld.config.%s.txt" | |
| % (namespace, vndk_snapshot_version)) | |
| return False | |
| for path in old_config[namespace]: | |
| if not path in current_config[namespace]: | |
| print("error: %s for %s in ld.config.%s.txt are missing in %s" | |
| % (path, namespace, vndk_snapshot_version, ld_config)) | |
| return False | |
| return True | |
| def main(): | |
| if len(sys.argv) != 2: | |
| print ("Usage: %s target_ld_config_txt_file_name" % sys.argv[0]) | |
| sys.exit(1) | |
| latest_vndk_snapshot_version = find_latest_vndk_snapshot_version() | |
| if not check_backward_compatibility(sys.argv[1], | |
| latest_vndk_snapshot_version): | |
| print("error: %s has backward incompatible changes to old " | |
| "vendor partition." % sys.argv[1]) | |
| sys.exit(1) | |
| # Current ld.config.txt file is backward compatible | |
| sys.exit(0) | |
| if __name__ == '__main__': | |
| main() |