• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
19
20import sys
21import os
22import time
23import platform
24import threading
25from enum import Enum
26
27from containers.status import throw_exception
28from exceptions.ohos_exception import OHOSException
29from services.interface.build_file_generator_interface import BuildFileGeneratorInterface
30from resources.config import Config
31from containers.arg import Arg, ModuleType
32from util.system_util import SystemUtil
33from util.io_util import IoUtil
34from util.log_util import LogUtil
35
36
37class CMDTYPE(Enum):
38    GEN = 1
39    PATH = 2
40    DESC = 3
41    LS = 4
42    REFS = 5
43    FORMAT = 6
44    CLEAN = 7
45
46
47class Gn(BuildFileGeneratorInterface):
48
49    def __init__(self):
50        super().__init__()
51        self.config = Config()
52        self._regist_gn_path()
53
54    def run(self):
55        self.execute_gn_cmd(CMDTYPE.GEN)
56
57    @throw_exception
58    def execute_gn_cmd(self, cmd_type: int, **kwargs):
59        if cmd_type == CMDTYPE.GEN:
60            return self._execute_gn_gen_cmd()
61        elif cmd_type == CMDTYPE.PATH:
62            return self._execute_gn_path_cmd(**kwargs)
63        elif cmd_type == CMDTYPE.DESC:
64            return self._execute_gn_desc_cmd(**kwargs)
65        elif cmd_type == CMDTYPE.LS:
66            return self._execute_gn_ls_cmd(**kwargs)
67        elif cmd_type == CMDTYPE.REFS:
68            return self._execute_gn_refs_cmd(**kwargs)
69        elif cmd_type == CMDTYPE.FORMAT:
70            return self._execute_gn_format_cmd(**kwargs)
71        elif cmd_type == CMDTYPE.CLEAN:
72            return self._execute_gn_clean_cmd(**kwargs)
73        else:
74            raise OHOSException(
75                'You are tring to use an unsupported gn cmd type "{}"'.format(cmd_type), '3001')
76
77    '''Description: Get gn excutable path and regist it
78    @parameter: none
79    @return: Status
80    '''
81
82    @throw_exception
83    def _regist_gn_path(self):
84        if sys.platform == "linux" and platform.machine().lower() == "aarch64":
85            gn_path = os.path.join(self.config.root_path, 'prebuilts/build-tools/{}-aarch64/bin/gn'
86                .format(sys.platform))
87        else:
88            gn_path = os.path.join(self.config.root_path, 'prebuilts/build-tools/{}-x86/bin/gn'
89                .format(sys.platform))
90        if os.path.exists(gn_path):
91            self.exec = gn_path
92        else:
93            raise OHOSException(
94                'There is no gn executable file at {}'.format(gn_path), '0001')
95
96    '''Description: Execute 'gn gen' command using registed args
97    @parameter: kwargs TBD
98    @return: None
99    '''
100
101    @throw_exception
102    def _execute_gn_gen_cmd(self, **kwargs):
103        gn_gen_cmd = [self.exec, 'gen', '--json=gn_log.json',
104                      '--args={}'.format(' '.join(self._convert_args())),
105                      self.config.out_path] + self._convert_flags()
106        if self.config.os_level == 'mini' or self.config.os_level == 'small':
107            gn_gen_cmd.append(f'--script-executable={sys.executable}')
108        LogUtil.write_log(self.config.log_path, 'Excuting gn command: {} {} --args="{}" {}'.format(
109            self.exec, 'gen',
110            ' '.join(self._convert_args()).replace('"', "\\\""),
111            ' '.join(gn_gen_cmd[3:])),
112            'info')
113        if self.config.log_mode == 'silent':
114            def loading_animation(done_event):
115                frames = ["|", "/", "-", "\\"]
116                circle_times = 0
117                while not done_event.is_set():
118                    sys.stdout.write("\r" + "[OHOS INFO] GN parsing... " + frames[circle_times % len(frames)])
119                    sys.stdout.flush()
120                    time.sleep(0.1)
121                    circle_times += 1
122
123            def task(done_event):
124                SystemUtil.exec_command(gn_gen_cmd, self.config.log_path, log_mode=self.config.log_mode)
125                done_event.set()
126                sys.stdout.write("\n" + "[OHOS INFO] GN parsing Done\n")
127            done_event = threading.Event()
128            animation_thread = threading.Thread(target=loading_animation, args=(done_event,))
129            animation_thread.start()
130            task(done_event)
131            animation_thread.join()
132        else:
133            SystemUtil.exec_command(gn_gen_cmd, self.config.log_path)
134
135    '''Description: Execute 'gn path' command using registed args
136    @parameter: kwargs TBD
137    @return: None
138    '''
139
140    @throw_exception
141    def _execute_gn_path_cmd(self, **kwargs):
142        out_dir = kwargs.get("out_dir")
143        default_options = ['--all']
144        args_file = Arg.read_args_file(ModuleType.TOOL)['path']
145        if (os.path.exists(os.path.join(out_dir, "args.gn"))):
146            gn_path_cmd = [self.exec, 'path', out_dir]
147            for arg in kwargs.get('args_list'):
148                if arg.startswith('-'):
149                    self._check_options_validity(arg, args_file)
150                gn_path_cmd.append(arg)
151            gn_path_cmd.extend(default_options)
152            sort_index = gn_path_cmd.index
153            gn_path_cmd = list(set(gn_path_cmd))
154            gn_path_cmd.sort(key=sort_index)
155            SystemUtil.exec_command(gn_path_cmd)
156        else:
157            raise OHOSException(
158                '"{}" Not a build directory.'.format(out_dir), '3004')
159
160    '''Description: Execute 'gn desc' command using registed args
161    @parameter: kwargs TBD
162    @return: None
163    '''
164
165    @throw_exception
166    def _execute_gn_desc_cmd(self, **kwargs):
167        out_dir = kwargs.get("out_dir")
168        default_options = ['--tree', '--blame']
169        args_file = Arg.read_args_file(ModuleType.TOOL)['desc']
170        if (os.path.exists(os.path.join(out_dir, "args.gn"))):
171            gn_desc_cmd = [self.exec, 'desc', out_dir]
172            for arg in kwargs.get('args_list'):
173                if arg.startswith('-'):
174                    self._check_options_validity(arg, args_file)
175                gn_desc_cmd.append(arg)
176            gn_desc_cmd.extend(default_options)
177            sort_index = gn_desc_cmd.index
178            gn_desc_cmd = list(set(gn_desc_cmd))
179            gn_desc_cmd.sort(key=sort_index)
180            SystemUtil.exec_command(gn_desc_cmd)
181        else:
182            raise OHOSException(
183                '"{}" Not a build directory.'.format(out_dir), '3004')
184
185    '''Description: Execute 'gn ls' command using registed args
186    @parameter: kwargs TBD
187    @return: None
188    '''
189
190    @throw_exception
191    def _execute_gn_ls_cmd(self, **kwargs):
192        out_dir = kwargs.get("out_dir")
193        args_file = Arg.read_args_file(ModuleType.TOOL)['ls']
194        if (os.path.exists(os.path.join(out_dir, "args.gn"))):
195            gn_ls_cmd = [self.exec, 'ls', out_dir]
196            for arg in kwargs.get('args_list'):
197                if arg.startswith('-'):
198                    self._check_options_validity(arg, args_file)
199                gn_ls_cmd.append(arg)
200            SystemUtil.exec_command(gn_ls_cmd)
201        else:
202            raise OHOSException(
203                '"{}" Not a build directory.'.format(out_dir), '3004')
204
205    '''Description: Execute 'gn refs' command using registed args
206    @parameter: kwargs TBD
207    @return: None
208    '''
209
210    @throw_exception
211    def _execute_gn_refs_cmd(self, **kwargs):
212        out_dir = kwargs.get("out_dir")
213        args_file = Arg.read_args_file(ModuleType.TOOL)['refs']
214        if (os.path.exists(os.path.join(out_dir, "args.gn"))):
215            gn_refs_cmd = [self.exec, 'refs', out_dir]
216            for arg in kwargs.get('args_list'):
217                if arg.startswith('-'):
218                    self._check_options_validity(arg, args_file)
219                gn_refs_cmd.append(arg)
220            SystemUtil.exec_command(gn_refs_cmd)
221        else:
222            raise OHOSException(
223                '"{}" Not a build directory.'.format(out_dir), '3004')
224
225    '''Description: Execute 'gn format' command using registed args
226    @parameter: kwargs TBD
227    @return: None
228    '''
229
230    @throw_exception
231    def _execute_gn_format_cmd(self, **kwargs):
232        gn_format_cmd = [self.exec, 'format']
233        args_file = Arg.read_args_file(ModuleType.TOOL)['format']
234        for arg in kwargs.get("args_list"):
235            if (arg.endswith('.gn')):
236                if (os.path.exists(arg)):
237                    gn_format_cmd.append(arg)
238                else:
239                    raise OHOSException(
240                        "ERROR Couldn't read '{}'".format(arg), '3005')
241            else:
242                if arg.startswith('-'):
243                    self._check_options_validity(arg, args_file)
244                gn_format_cmd.append(arg)
245        SystemUtil.exec_command(gn_format_cmd)
246
247    '''Description: Execute 'gn clean' command using registed args
248    @parameter: kwargs TBD
249    @return: None
250    '''
251
252    @throw_exception
253    def _execute_gn_clean_cmd(self, **kwargs):
254        out_dir = kwargs.get("out_dir")
255        if (os.path.exists(os.path.join(out_dir, "args.gn"))):
256            gn_clean_cmd = [self.exec, 'clean', out_dir]
257            SystemUtil.exec_command(gn_clean_cmd)
258        else:
259            raise OHOSException('"{}" Not a build directory.'
260                                'Usage: "gn clean <out_dir>"'.format(out_dir), '3004')
261
262    '''Description: Convert all registed args into a list
263    @parameter: none
264    @return: list of all registed args
265    '''
266
267    def _convert_args(self) -> list:
268        args_list = []
269
270        for key, value in self.args_dict.items():
271            if isinstance(value, bool):
272                args_list.append('{}={}'.format(key, str(value).lower()))
273
274            elif isinstance(value, str):
275                args_list.append('{}="{}"'.format(key, value))
276
277            elif isinstance(value, int):
278                args_list.append('{}={}'.format(key, value))
279
280            elif isinstance(value, list):
281                args_list.append('{}="{}"'.format(key, "&&".join(value)))
282
283        return args_list
284
285    '''Description: Convert all registed flags into a list
286    @parameter: none
287    @return: list of all registed flags
288    '''
289
290    def _convert_flags(self) -> list:
291        flags_list = []
292
293        for key, value in self.flags_dict.items():
294            if key == 'gn_flags' and isinstance(value, list):
295                flags_list += value
296            elif value == '':
297                flags_list.append('{}'.format(key))
298            else:
299                flags_list.append('{}={}'.format(key, str(value)).lower())
300
301        return flags_list
302
303    '''Description: Option validity check
304    @parameter: "option": Option to be checked
305                "args_file": Option config file
306    @return: Inspection result(True|False)
307    '''
308
309    def _check_options_validity(self, option: str, args_file: dict):
310        support_sub_options = args_file.get(
311            "arg_attribute").get("support_sub_options")
312        option_name = option.lstrip('-')
313        option_value = ""
314        if '=' in option:
315            option_name, option_value = option.lstrip('-').split('=')
316        if option_name in support_sub_options:
317            sub_optional_list = support_sub_options.get(
318                option_name).get("arg_attribute").get("optional")
319            if sub_optional_list and option_value not in sub_optional_list:
320                if not len(option_value):
321                    raise OHOSException('ERROR argument "--{}": Invalid choice "{}". '
322                                        'choose from {}'.format(option_name, option_value, sub_optional_list), '3006')
323                else:
324                    raise OHOSException('ERROR argument "--{}": Invalid choice "{}". '
325                                        'choose from {}'.format(option_name, option_value, sub_optional_list), '3003')
326        else:
327            raise OHOSException('ERROR argument "{}": Invalid choice "{}". '
328                                'choose from {}'.format(args_file.get("arg_name"),
329                                                        option, list(support_sub_options.keys())), '3003')