1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3
4#
5# Copyright (c) 2023 Huawei Device Co., Ltd.
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
19import os
20import argparse
21import sys
22
23from enum import Enum
24from resources.global_var import CURRENT_ARGS_DIR
25from resources.global_var import CURRENT_BUILD_ARGS
26from resources.global_var import DEFAULT_BUILD_ARGS
27from resources.global_var import CURRENT_SET_ARGS
28from resources.global_var import DEFAULT_SET_ARGS
29from resources.global_var import CURRENT_CLEAN_ARGS
30from resources.global_var import DEFAULT_CLEAN_ARGS
31from resources.global_var import DEFAULT_ENV_ARGS
32from resources.global_var import CURRENT_ENV_ARGS
33from resources.global_var import DEFAULT_TOOL_ARGS
34from resources.global_var import CURRENT_TOOL_ARGS
35from resources.global_var import DEFAULT_INDEP_BUILD_ARGS
36from resources.global_var import CURRENT_INDEP_BUILD_ARGS
37from resources.global_var import DEFAULT_INSTALL_ARGS
38from resources.global_var import CURRENT_INSTALL_ARGS
39from resources.global_var import DEFAULT_PACKAGE_ARGS
40from resources.global_var import CURRENT_PACKAGE_ARGS
41from resources.global_var import DEFAULT_PUBLISH_ARGS
42from resources.global_var import CURRENT_PUBLISH_ARGS
43from resources.global_var import DEFAULT_UPDATE_ARGS
44from resources.global_var import CURRENT_UPDATE_ARGS
45from resources.global_var import DEFAULT_PUSH_ARGS
46from resources.global_var import CURRENT_PUSH_ARGS
47from resources.global_var import ARGS_DIR
48from exceptions.ohos_exception import OHOSException
49from util.log_util import LogUtil
50from util.io_util import IoUtil
51from util.type_check_util import TypeCheckUtil
52from resolver.args_factory import ArgsFactory
53from containers.status import throw_exception
54
55
56class ModuleType(Enum):
57
58    BUILD = 0
59    SET = 1
60    ENV = 2
61    CLEAN = 3
62    TOOL = 4
63    INDEP_BUILD = 5
64    INSTALL = 6
65    PACKAGE = 7
66    PUBLISH = 8
67    UPDATE = 9
68    PUSH = 10
69
70
71class ArgType():
72
73    NONE = 0
74    BOOL = 1
75    INT = 2
76    STR = 3
77    LIST = 4
78    DICT = 5
79    SUBPARSERS = 6
80
81    @staticmethod
82    def get_type(value: str):
83        if value == 'bool':
84            return ArgType.BOOL
85        elif value == "int":
86            return ArgType.INT
87        elif value == 'str':
88            return ArgType.STR
89        elif value == "list":
90            return ArgType.LIST
91        elif value == 'dict':
92            return ArgType.DICT
93        elif value == 'subparsers':
94            return ArgType.SUBPARSERS
95        else:
96            return ArgType.NONE
97
98
99class BuildPhase():
100
101    NONE = 0
102    PRE_BUILD = 1
103    PRE_LOAD = 2
104    LOAD = 3
105    PRE_TARGET_GENERATE = 4
106    TARGET_GENERATE = 5
107    POST_TARGET_GENERATE = 6
108    PRE_TARGET_COMPILATION = 7
109    TARGET_COMPILATION = 8
110    POST_TARGET_COMPILATION = 9
111    POST_BUILD = 10
112
113    @staticmethod
114    def get_type(value: str):
115        if value == 'prebuild':
116            return BuildPhase.PRE_BUILD
117        elif value == "preload":
118            return BuildPhase.PRE_LOAD
119        elif value == 'load':
120            return BuildPhase.LOAD
121        elif value == "preTargetGenerate":
122            return BuildPhase.PRE_TARGET_GENERATE
123        elif value == 'targetGenerate':
124            return BuildPhase.TARGET_GENERATE
125        elif value == 'postTargetGenerate':
126            return BuildPhase.POST_TARGET_GENERATE
127        elif value == 'preTargetCompilation':
128            return BuildPhase.PRE_TARGET_COMPILATION
129        elif value == 'targetCompilation':
130            return BuildPhase.TARGET_COMPILATION
131        elif value == 'postTargetCompilation':
132            return BuildPhase.POST_TARGET_COMPILATION
133        elif value == 'postbuild':
134            return BuildPhase.POST_BUILD
135        else:
136            return BuildPhase.NONE
137
138
139class CleanPhase():
140
141    REGULAR = 0
142    DEEP = 1
143    NONE = 2
144
145    @staticmethod
146    def get_type(value: str):
147        if value == 'regular':
148            return CleanPhase.REGULAR
149        elif value == 'deep':
150            return CleanPhase.DEEP
151        else:
152            return CleanPhase.NONE
153
154
155class Arg():
156
157    def __init__(self, name: str, helps: str, phase: str,
158                 attribute: dict, argtype: ArgType, value,
159                 resolve_function: str):
160        self._arg_name = name
161        self._arg_help = helps
162        self._arg_phase = phase
163        self._arg_attribute = attribute
164        self._arg_type = argtype
165        self._arg_value = value
166        self._resolve_function = resolve_function
167
168    @property
169    def arg_name(self):
170        return self._arg_name
171
172    @property
173    def arg_value(self):
174        return self._arg_value
175
176    @property
177    def arg_help(self):
178        return self._arg_help
179
180    @property
181    def arg_attribute(self):
182        return self._arg_attribute
183
184    @property
185    def arg_phase(self):
186        return self._arg_phase
187
188    @property
189    def arg_type(self):
190        return self._arg_type
191
192    @property
193    def resolve_function(self):
194        return self._resolve_function
195
196    @staticmethod
197    @throw_exception
198    def create_instance_by_dict(data: dict):
199        arg_name = str(data['arg_name']).replace("-", "_")[2:]
200        arg_help = str(data['arg_help'])
201        arg_phase = BuildPhase.get_type(str(data['arg_phase']))
202        arg_attibute = dict(data['arg_attribute'])
203        arg_type = ArgType.get_type(data['arg_type'])
204        arg_value = ''
205        if arg_type == ArgType.BOOL:
206            arg_value = data['argDefault']
207        elif arg_type == ArgType.INT:
208            arg_value = int(data['argDefault'])
209        elif arg_type == ArgType.STR:
210            arg_value = data['argDefault']
211        elif arg_type == ArgType.LIST:
212            arg_value = list(data['argDefault'])
213        elif arg_type == ArgType.DICT:
214            arg_value = dict(data['argDefault'])
215        elif arg_type == ArgType.SUBPARSERS:
216            arg_value = list(data['argDefault'])
217        else:
218            raise OHOSException('Unknown arg type "{}" for arg "{}"'.format(
219                arg_type, arg_name), "0003")
220        resolve_function = data['resolve_function']
221        return Arg(arg_name, arg_help, arg_phase, arg_attibute, arg_type, arg_value, resolve_function)
222
223    @staticmethod
224    def get_help(module_type: ModuleType) -> str:
225        parser = argparse.ArgumentParser()
226        all_args = Arg.read_args_file(module_type)
227
228        for arg in all_args.values():
229            arg = dict(arg)
230            ArgsFactory.genetic_add_option(parser, arg)
231
232        parser.usage = 'hb {} [option]'.format(module_type.name.lower())
233        parser.parse_known_args(sys.argv[2:])
234
235        return parser.format_help()
236
237    @staticmethod
238    def parse_all_args(module_type: ModuleType) -> dict:
239        args_dict = {}
240        parser = argparse.ArgumentParser()
241        all_args = Arg.read_args_file(module_type)
242
243        for arg in all_args.values():
244            arg = dict(arg)
245            ArgsFactory.genetic_add_option(parser, arg)
246            oh_arg = Arg.create_instance_by_dict(arg)
247            args_dict[oh_arg.arg_name] = oh_arg
248
249        parser.usage = 'hb {} [option]'.format(module_type.name.lower())
250        parser_args = parser.parse_known_args(sys.argv[2:])
251
252        for oh_arg in args_dict.values():
253            if isinstance(oh_arg, Arg):
254                assigned_value = parser_args[0].__dict__[oh_arg.arg_name]
255                if oh_arg.arg_type == ArgType.LIST:
256                    convert_assigned_value = TypeCheckUtil.tile_list(assigned_value)
257                    convert_assigned_value = list(set(convert_assigned_value))
258                elif oh_arg.arg_type == ArgType.SUBPARSERS:
259                    convert_assigned_value = TypeCheckUtil.tile_list(assigned_value)
260                    if len(convert_assigned_value):
261                        convert_assigned_value = list(set(convert_assigned_value))
262                        convert_assigned_value.extend(parser_args[1])
263                        convert_assigned_value.sort(key=sys.argv[2:].index)
264                elif oh_arg.arg_type == ArgType.BOOL:
265                    if str(assigned_value).lower() == 'false':
266                        convert_assigned_value = False
267                    elif str(assigned_value).lower() == 'true' or assigned_value is None:
268                        convert_assigned_value = True
269                else:
270                    convert_assigned_value = assigned_value
271
272                if oh_arg.arg_attribute.get('deprecated', None) and oh_arg.arg_value != convert_assigned_value:
273                    LogUtil.hb_warning(
274                        'compile option "{}" will be deprecated, \
275                            please consider use other options'.format(oh_arg.arg_name))
276                oh_arg.arg_value = convert_assigned_value
277                Arg.write_args_file(
278                    oh_arg.arg_name, oh_arg.arg_value, module_type)
279
280        return args_dict
281
282    @arg_value.setter
283    def arg_value(self, value):
284        self._arg_value = value
285
286    @resolve_function.setter
287    def resolve_function(self, value):
288        self._resolve_function = value
289
290    @staticmethod
291    @throw_exception
292    def write_args_file(key: str, value, module_type: ModuleType):
293        args_file_path = ''
294        if module_type == ModuleType.BUILD:
295            args_file_path = CURRENT_BUILD_ARGS
296        elif module_type == ModuleType.SET:
297            args_file_path = CURRENT_SET_ARGS
298        elif module_type == ModuleType.CLEAN:
299            args_file_path = CURRENT_CLEAN_ARGS
300        elif module_type == ModuleType.ENV:
301            args_file_path = CURRENT_ENV_ARGS
302        elif module_type == ModuleType.TOOL:
303            args_file_path = CURRENT_TOOL_ARGS
304        elif module_type == ModuleType.INDEP_BUILD:
305            args_file_path = CURRENT_INDEP_BUILD_ARGS
306        elif module_type == ModuleType.INSTALL:
307            args_file_path = CURRENT_INSTALL_ARGS
308        elif module_type == ModuleType.PACKAGE:
309            args_file_path = CURRENT_PACKAGE_ARGS
310        elif module_type == ModuleType.PUBLISH:
311            args_file_path = CURRENT_PUBLISH_ARGS
312        elif module_type == ModuleType.UPDATE:
313            args_file_path = CURRENT_UPDATE_ARGS
314        elif module_type == ModuleType.PUSH:
315            args_file_path = CURRENT_PUSH_ARGS
316        else:
317            raise OHOSException(
318                'You are trying to write args file, but there is no corresponding module "{}" args file'
319                .format(module_type), "0002")
320        args_file = Arg.read_args_file(module_type)
321        args_file[key]["argDefault"] = value
322        IoUtil.dump_json_file(args_file_path, args_file)
323
324    @staticmethod
325    @throw_exception
326    def read_args_file(module_type: ModuleType):
327        args_file_path = ''
328        default_file_path = ''
329        if module_type == ModuleType.BUILD:
330            args_file_path = CURRENT_BUILD_ARGS
331            default_file_path = DEFAULT_BUILD_ARGS
332        elif module_type == ModuleType.SET:
333            args_file_path = CURRENT_SET_ARGS
334            default_file_path = DEFAULT_SET_ARGS
335        elif module_type == ModuleType.CLEAN:
336            args_file_path = CURRENT_CLEAN_ARGS
337            default_file_path = DEFAULT_CLEAN_ARGS
338        elif module_type == ModuleType.ENV:
339            args_file_path = CURRENT_ENV_ARGS
340            default_file_path = DEFAULT_ENV_ARGS
341        elif module_type == ModuleType.TOOL:
342            args_file_path = CURRENT_TOOL_ARGS
343            default_file_path = DEFAULT_TOOL_ARGS
344        elif module_type == ModuleType.INDEP_BUILD:
345            args_file_path = CURRENT_INDEP_BUILD_ARGS
346            default_file_path = DEFAULT_INDEP_BUILD_ARGS
347        elif module_type == ModuleType.INSTALL:
348            args_file_path = CURRENT_INSTALL_ARGS
349            default_file_path = DEFAULT_INSTALL_ARGS
350        elif module_type == ModuleType.PACKAGE:
351            args_file_path = CURRENT_PACKAGE_ARGS
352            default_file_path = DEFAULT_PACKAGE_ARGS
353        elif module_type == ModuleType.PUBLISH:
354            args_file_path = CURRENT_PUBLISH_ARGS
355            default_file_path = DEFAULT_PUBLISH_ARGS
356        elif module_type == ModuleType.UPDATE:
357            args_file_path = CURRENT_UPDATE_ARGS
358            default_file_path = DEFAULT_UPDATE_ARGS
359        elif module_type == ModuleType.PUSH:
360            args_file_path = CURRENT_PUSH_ARGS
361            default_file_path = DEFAULT_PUSH_ARGS
362        else:
363            raise OHOSException(
364                'You are trying to read args file, but there is no corresponding module "{}" args file'
365                .format(module_type.name.lower()), "0018")
366        if not os.path.exists(CURRENT_ARGS_DIR):
367            os.makedirs(CURRENT_ARGS_DIR, exist_ok=True)
368        if not os.path.exists(args_file_path):
369            IoUtil.copy_file(src=default_file_path, dst=args_file_path)
370        return IoUtil.read_json_file(args_file_path)
371
372    @staticmethod
373    def clean_args_file():
374        if os.path.exists(CURRENT_ARGS_DIR):
375            for file in os.listdir(CURRENT_ARGS_DIR):
376                if file.endswith('.json') and os.path.exists(os.path.join(CURRENT_ARGS_DIR, file)):
377                    os.remove(os.path.join(CURRENT_ARGS_DIR, file))
378
379    @staticmethod
380    def clean_args_file_by_type(module_type: ModuleType):
381        args_file_path = ''
382        if module_type == ModuleType.INSTALL:
383            args_file_path = CURRENT_INSTALL_ARGS
384        elif module_type == ModuleType.PACKAGE:
385            args_file_path = CURRENT_PACKAGE_ARGS
386        elif module_type == ModuleType.UPDATE:
387            args_file_path = CURRENT_UPDATE_ARGS
388        elif module_type == ModuleType.ENV:
389            args_file_path = CURRENT_ENV_ARGS
390        elif module_type == ModuleType.INDEP_BUILD:
391            args_file_path = CURRENT_INDEP_BUILD_ARGS
392        elif module_type == ModuleType.PUSH:
393            args_file_path = CURRENT_PUSH_ARGS
394        if os.path.exists(args_file_path):
395            os.remove(args_file_path)
396