1#!/usr/bin/env python3 2# 3# Copyright (C) 2022 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16"""Utility functions to produce module or module type dependency graphs using json-module-graph or queryview.""" 17 18import json 19import os 20import os.path 21import subprocess 22import xml.etree.ElementTree 23 24# This list of module types are omitted from the report and graph 25# for brevity and simplicity. Presence in this list doesn't mean 26# that they shouldn't be converted, but that they are not that useful 27# to be recorded in the graph or report currently. 28IGNORED_KINDS = set([ 29 "license_kind", 30 "license", 31 "cc_defaults", 32 "cc_prebuilt_object", 33 "cc_prebuilt_library_headers", 34 "cc_prebuilt_library_shared", 35 "cc_prebuilt_library_static", 36 "cc_prebuilt_library_static", 37 "cc_prebuilt_library", 38 "java_defaults", 39 "ndk_prebuilt_static_stl", 40 "ndk_library", 41]) 42 43SRC_ROOT_DIR = os.path.abspath(__file__ + "/../../../../..") 44 45 46def _build_with_soong(target): 47 subprocess.check_output( 48 [ 49 "build/soong/soong_ui.bash", 50 "--make-mode", 51 "--skip-soong-tests", 52 target, 53 ], 54 cwd=SRC_ROOT_DIR, 55 env={ 56 # Use aosp_arm as the canonical target product. 57 "TARGET_PRODUCT": "aosp_arm", 58 "TARGET_BUILD_VARIANT": "userdebug", 59 }, 60 ) 61 62 63def get_queryview_module_info(module): 64 """Returns the list of transitive dependencies of input module as built by queryview.""" 65 _build_with_soong("queryview") 66 67 result = subprocess.check_output( 68 [ 69 "tools/bazel", "query", "--config=ci", "--config=queryview", 70 "--output=xml", 71 'deps(attr("soong_module_name", "^{}$", //...))'.format(module) 72 ], 73 cwd=SRC_ROOT_DIR, 74 ) 75 return xml.etree.ElementTree.fromstring(result) 76 77 78def get_json_module_info(module): 79 """Returns the list of transitive dependencies of input module as provided by Soong's json module graph.""" 80 _build_with_soong("json-module-graph") 81 # Run query.sh on the module graph for the top level module 82 result = subprocess.check_output( 83 [ 84 "build/bazel/json_module_graph/query.sh", "fullTransitiveDeps", 85 "out/soong/module-graph.json", module 86 ], 87 cwd=SRC_ROOT_DIR, 88 ) 89 return json.loads(result) 90 91 92def get_bp2build_converted_modules(): 93 """ Returns the list of modules that bp2build can currently convert. """ 94 _build_with_soong("bp2build") 95 # Parse the list of converted module names from bp2build 96 with open( 97 os.path.join( 98 SRC_ROOT_DIR, 99 "out/soong/soong_injection/metrics/converted_modules.txt")) as f: 100 # Read line by line, excluding comments. 101 # Each line is a module name. 102 ret = [line.strip() for line in f.readlines() if not line.startswith("#")] 103 return set(ret) 104 105 106def get_json_module_type_info(module_type): 107 """Returns the combined transitive dependency closures of all modules of module_type.""" 108 _build_with_soong("json-module-graph") 109 # Run query.sh on the module graph for the top level module type 110 result = subprocess.check_output( 111 [ 112 "build/bazel/json_module_graph/query.sh", 113 "fullTransitiveModuleTypeDeps", "out/soong/module-graph.json", 114 module_type 115 ], 116 cwd=SRC_ROOT_DIR, 117 ) 118 return json.loads(result) 119 120 121def is_windows_variation(module): 122 """Returns True if input module's variant is Windows. 123 124 Args: 125 module: an entry parsed from Soong's json-module-graph 126 """ 127 dep_variations = module.get("Variations") 128 dep_variation_os = "" 129 if dep_variations != None: 130 dep_variation_os = dep_variations.get("os") 131 return dep_variation_os == "windows" 132 133 134def ignore_kind(kind): 135 return kind in IGNORED_KINDS or "defaults" in kind 136