• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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