1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4# 5# Copyright (c) 2025 Northeastern University 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18 19from collections import defaultdict 20from dataclasses import dataclass 21from typing import List, Dict, Any, DefaultDict, Optional, Union 22 23from ohos.sbom.data.ninja_json import NinjaJson 24from ohos.sbom.data.target import Target 25from templates.metadata.gen_module_info import create_module_info_parser, gen_module_info_data 26 27 28@dataclass 29class InstallModule: 30 module_info_file: str 31 32 33@dataclass 34class InstallMatchResult: 35 source_target: Target 36 matched_targets: List[Target] 37 38 39class OutputIndex: 40 41 def __init__(self, targets: List[Target]) -> None: 42 self._idx: DefaultDict[str, List[Target]] = defaultdict(list) 43 for t in targets: 44 for out in t.outputs: 45 self._idx[out].append(t) 46 47 def lookup(self, output: str) -> List[Target]: 48 return self._idx.get(output, []) 49 50 51class InstallModuleAnalyzer: 52 def __init__(self, nj: NinjaJson) -> None: 53 self._nj = nj 54 self._index = OutputIndex(self._nj.all_targets()) 55 self._matched_install_module = None 56 self._install_enable = None 57 self._install_dest = None 58 59 def get_matched_install_module(self) -> Dict[str, InstallMatchResult]: 60 if self._matched_install_module is None: 61 self._match_all() 62 return self._matched_install_module 63 64 def get_enabled_modules(self, install_enable: bool = True): 65 if self._matched_install_module is None: 66 self._analysis_all_install_enable() 67 return [key for key, value in self._install_enable.items() if value == install_enable] 68 69 def get_install_with_dest(self): 70 install_src_target = self.get_enabled_modules(install_enable=True) 71 return {target_name: self._install_dest[target_name] for target_name in install_src_target} 72 73 def get_install_enable_target(self): 74 enabled_modules = set(self.get_enabled_modules(install_enable=True)) 75 return self._nj.filter_targets(lambda t: t.target_name in enabled_modules) 76 77 def generate_module_info(self, target: Union[Target, str]) -> Optional[Dict[str, Any]]: 78 if isinstance(target, str): 79 target = next((t for t in self._nj.all_targets() if t.target_name == target), None) 80 if not target: 81 print(f"[Error] not found analyze target: {target}") 82 return None 83 create_module_info_parser() 84 args = target.args 85 86 args = create_module_info_parser().parse_args(args) 87 module_info_data = gen_module_info_data(args) 88 return module_info_data 89 90 def _match_all(self): 91 results: Dict[str, InstallMatchResult] = {} 92 build_dir = self._nj.build_setting.build_dir 93 94 targets = self._nj.filter_targets(lambda t: not t.testonly) 95 for src in targets: 96 raw_modules: List[Dict[str, str]] = src.metadata.get("install_modules", []) 97 if not raw_modules: 98 continue 99 100 modules = [InstallModule(m.get("module_info_file", "")) for m in raw_modules] 101 expected_outputs = { 102 str(build_dir + m.module_info_file) for m in modules 103 } 104 105 matched: List[Target] = [] 106 for out in expected_outputs: 107 matched.extend(self._index.lookup(out)) 108 109 dedup = {t.target_name: t for t in matched} 110 results[src.target_name] = InstallMatchResult( 111 source_target=src, 112 matched_targets=list(dedup.values()) 113 ) 114 115 if len(dedup) != len(modules): 116 print(f"[Warning] {src.target_name} install_modules not match all outputs") 117 self._matched_install_module = results 118 119 def _analysis_all_install_enable(self): 120 install_results: Dict[str, bool] = {} 121 dest_results: Dict[str, list[str]] = {} 122 matched_modules = self.get_matched_install_module() 123 124 def process_target(src_target_name: str, target: Target) -> None: 125 module_info = self.generate_module_info(target) 126 install_enable = False 127 dest = [] 128 if module_info: 129 install_enable = module_info.get("install_enable", False) 130 dest = module_info.get("dest", []) 131 install_results[src_target_name] = install_enable 132 dest_results[src_target_name] = dest 133 134 for src_target_name, match_result in matched_modules.items(): 135 for target in match_result.matched_targets: 136 process_target(src_target_name, target) 137 self._install_enable = install_results 138 self._install_dest = dest_results 139