15f9996aaSopenharmony_ci#!/usr/bin/env python3
25f9996aaSopenharmony_ci# -*- coding: utf-8 -*-
35f9996aaSopenharmony_ci# Copyright (c) 2023 Huawei Device Co., Ltd.
45f9996aaSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
55f9996aaSopenharmony_ci# you may not use this file except in compliance with the License.
65f9996aaSopenharmony_ci# You may obtain a copy of the License at
75f9996aaSopenharmony_ci#
85f9996aaSopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
95f9996aaSopenharmony_ci#
105f9996aaSopenharmony_ci# Unless required by applicable law or agreed to in writing, software
115f9996aaSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
125f9996aaSopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135f9996aaSopenharmony_ci# See the License for the specific language governing permissions and
145f9996aaSopenharmony_ci# limitations under the License.
155f9996aaSopenharmony_ci
165f9996aaSopenharmony_ci
175f9996aaSopenharmony_cifrom __future__ import print_function
185f9996aaSopenharmony_ci
195f9996aaSopenharmony_ciimport os
205f9996aaSopenharmony_ciimport os.path
215f9996aaSopenharmony_ciimport sys
225f9996aaSopenharmony_ciimport argparse
235f9996aaSopenharmony_ciimport glob
245f9996aaSopenharmony_ciimport json
255f9996aaSopenharmony_ciimport re
265f9996aaSopenharmony_ciimport shutil
275f9996aaSopenharmony_ciimport subprocess
285f9996aaSopenharmony_ci
295f9996aaSopenharmony_ci# Rust path
305f9996aaSopenharmony_ciRUST_PATH = '//third_party/rust/'
315f9996aaSopenharmony_ci
325f9996aaSopenharmony_ci# import content added to all generated BUILD.gn files.
335f9996aaSopenharmony_ciIMPORT_CONTENT = '//build/templates/rust/ohos.gni'
345f9996aaSopenharmony_ci
355f9996aaSopenharmony_ci# The name of the temporary output directory.
365f9996aaSopenharmony_ciTARGET_TEMP = 'target_temp'
375f9996aaSopenharmony_ci
385f9996aaSopenharmony_ci# Header added to all generated BUILD.gn files.
395f9996aaSopenharmony_ciBUILD_GN_HEADER = (
405f9996aaSopenharmony_ci    '# Copyright (c) 2023 Huawei Device Co., Ltd.\n' +
415f9996aaSopenharmony_ci    '# Licensed under the Apache License, Version 2.0 (the "License");\n' +
425f9996aaSopenharmony_ci    '# you may not use this file except in compliance with the License.\n' +
435f9996aaSopenharmony_ci    '# You may obtain a copy of the License at\n' +
445f9996aaSopenharmony_ci    '#\n' +
455f9996aaSopenharmony_ci    '#     http://www.apache.org/licenses/LICENSE-2.0\n' +
465f9996aaSopenharmony_ci    '#\n' +
475f9996aaSopenharmony_ci    '# Unless required by applicable law or agreed to in writing, software\n' +
485f9996aaSopenharmony_ci    '# distributed under the License is distributed on an "AS IS" BASIS,\n' +
495f9996aaSopenharmony_ci    '# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' +
505f9996aaSopenharmony_ci    '# See the License for the specific language governing permissions and\n' +
515f9996aaSopenharmony_ci    '# limitations under the License.\n')
525f9996aaSopenharmony_ci
535f9996aaSopenharmony_ci# Message to be displayed when this script is called without the --run flag.
545f9996aaSopenharmony_ciDRY_RUN_CONTENT = (
555f9996aaSopenharmony_ci    'Dry-run: This script uses ./' + TARGET_TEMP + ' for output directory,\n' +
565f9996aaSopenharmony_ci    'runs cargo clean, runs cargo build -v, saves output to ./cargo.out,\n' +
575f9996aaSopenharmony_ci    'and writes to BUILD.gn in the current and subdirectories.\n\n' +
585f9996aaSopenharmony_ci    'To do do all of the above, use the --run flag.\n' +
595f9996aaSopenharmony_ci    'See --help for other flags, and more usage notes in this script.\n')
605f9996aaSopenharmony_ci
615f9996aaSopenharmony_ci# Rust package name with suffix -d1.d2.d3(+.*)?.
625f9996aaSopenharmony_ciVERSION_SUFFIX_RE = re.compile(r'^(.*)-[0-9]+\.[0-9]+\.[0-9]+(?:\+.*)?$')
635f9996aaSopenharmony_ci
645f9996aaSopenharmony_ci# Crate types corresponding to a library
655f9996aaSopenharmony_ciLIBRARY_CRATE_TYPES = ['staticlib', 'cdylib', 'lib', 'rlib', 'dylib', 'proc-macro']
665f9996aaSopenharmony_ci
675f9996aaSopenharmony_ci
685f9996aaSopenharmony_cidef escape_quotes(s: str):
695f9996aaSopenharmony_ci    # replace '"' with '\\"'
705f9996aaSopenharmony_ci    return s.replace('"', '\\"')
715f9996aaSopenharmony_ci
725f9996aaSopenharmony_ci
735f9996aaSopenharmony_cidef file_base_name(path: str):
745f9996aaSopenharmony_ci    return os.path.splitext(os.path.basename(path))[0]
755f9996aaSopenharmony_ci
765f9996aaSopenharmony_ci
775f9996aaSopenharmony_cidef pkg_to_crate_name(s: str):
785f9996aaSopenharmony_ci    return s.replace('-', '_').replace('.', '_')
795f9996aaSopenharmony_ci
805f9996aaSopenharmony_ci
815f9996aaSopenharmony_cidef get_base_name(path: str):
825f9996aaSopenharmony_ci    return pkg_to_crate_name(file_base_name(path))
835f9996aaSopenharmony_ci
845f9996aaSopenharmony_ci
855f9996aaSopenharmony_cidef get_crate_name(crate):
865f9996aaSopenharmony_ci    # to sort crates in a list
875f9996aaSopenharmony_ci    return crate.crate_name
885f9996aaSopenharmony_ci
895f9996aaSopenharmony_ci
905f9996aaSopenharmony_cidef get_designated_pkg_info(lines: list, designated: str):
915f9996aaSopenharmony_ci    package = re.compile(r'^ *\[package\]')
925f9996aaSopenharmony_ci    designated_re = re.compile('^ *' + designated + ' *= * "([^"]*)')
935f9996aaSopenharmony_ci    is_package = False
945f9996aaSopenharmony_ci    for line in lines:
955f9996aaSopenharmony_ci        if is_package:
965f9996aaSopenharmony_ci            if designated_re.match(line):
975f9996aaSopenharmony_ci                line = eval(repr(line).replace(f'\\"', ''))
985f9996aaSopenharmony_ci                return designated_re.match(line).group(1)
995f9996aaSopenharmony_ci        else:
1005f9996aaSopenharmony_ci            is_package = package.match(line) is not None
1015f9996aaSopenharmony_ci    return ''
1025f9996aaSopenharmony_ci
1035f9996aaSopenharmony_ci
1045f9996aaSopenharmony_cidef is_build_script(name: str):
1055f9996aaSopenharmony_ci    # Judge whether it is build script.
1065f9996aaSopenharmony_ci    return name.startswith('build_script_')
1075f9996aaSopenharmony_ci
1085f9996aaSopenharmony_ci
1095f9996aaSopenharmony_cidef is_dependent_path(path: str):
1105f9996aaSopenharmony_ci    # Absolute('/') or dependent('.../') paths are not main files of this crate.
1115f9996aaSopenharmony_ci    return path.startswith('/') or path.startswith('.../')
1125f9996aaSopenharmony_ci
1135f9996aaSopenharmony_ci
1145f9996aaSopenharmony_cidef unquote(s):
1155f9996aaSopenharmony_ci    # remove quotes around str
1165f9996aaSopenharmony_ci    if s and len(s) > 1 and s[0] == '"' and s[-1] == '"':
1175f9996aaSopenharmony_ci        return s[1:-1]
1185f9996aaSopenharmony_ci    return s
1195f9996aaSopenharmony_ci
1205f9996aaSopenharmony_ci
1215f9996aaSopenharmony_cidef remove_version_suffix(s):
1225f9996aaSopenharmony_ci    # remove -d1.d2.d3 suffix
1235f9996aaSopenharmony_ci    if VERSION_SUFFIX_RE.match(s):
1245f9996aaSopenharmony_ci        return VERSION_SUFFIX_RE.match(s).group(1)
1255f9996aaSopenharmony_ci    return s
1265f9996aaSopenharmony_ci
1275f9996aaSopenharmony_ci
1285f9996aaSopenharmony_cidef short_out_name(pkg: str, s: str):
1295f9996aaSopenharmony_ci    # replace /.../pkg-*/out/* with .../out/*
1305f9996aaSopenharmony_ci    return re.sub('^/.*/' + pkg + '-[0-9a-f]*/out/', '.../out/', s)
1315f9996aaSopenharmony_ci
1325f9996aaSopenharmony_ci
1335f9996aaSopenharmony_ciclass Crate(object):
1345f9996aaSopenharmony_ci    """Information of a Rust crate to collect/emit for an BUILD.gn module."""
1355f9996aaSopenharmony_ci
1365f9996aaSopenharmony_ci    def __init__(self, runner, outfile_name: str):
1375f9996aaSopenharmony_ci        # Remembered global runner
1385f9996aaSopenharmony_ci        self.runner = runner
1395f9996aaSopenharmony_ci        self.debug = runner.args.debug
1405f9996aaSopenharmony_ci        self.cargo_dir = ''              # directory of my Cargo.toml
1415f9996aaSopenharmony_ci        self.outfile = None              # open file handle of outfile_name during dump*
1425f9996aaSopenharmony_ci        self.outfile_name = outfile_name # path to BUILD.gn
1435f9996aaSopenharmony_ci        # GN module properties derived from rustc parameters.
1445f9996aaSopenharmony_ci        self.module_type = ''            # lib,crate_name,test etc.
1455f9996aaSopenharmony_ci        self.root_pkg_name = ''          # parent package name of a sub/test packge
1465f9996aaSopenharmony_ci        # Save parsed status
1475f9996aaSopenharmony_ci        self.error_infos = ''            # all errors found during parsing
1485f9996aaSopenharmony_ci        self.line = ''                   # original rustc command line parameters
1495f9996aaSopenharmony_ci        self.line_num = 1                # runner told input source line number
1505f9996aaSopenharmony_ci        # Parameters collected from rustc command line.
1515f9996aaSopenharmony_ci        self.cap_lints = ''
1525f9996aaSopenharmony_ci        self.crate_name = ''
1535f9996aaSopenharmony_ci        self.edition = '2015'            # cargo default is 2015, you can specify the edition as 2018 or 2021
1545f9996aaSopenharmony_ci        self.emit_list = ''              # --emit=dep-info,metadata,link
1555f9996aaSopenharmony_ci        self.main_src = ''
1565f9996aaSopenharmony_ci        self.target = ''
1575f9996aaSopenharmony_ci        self.cfgs = list()
1585f9996aaSopenharmony_ci        self.core_deps = list()          # first part of self.deps elements
1595f9996aaSopenharmony_ci        self.crate_types = list()
1605f9996aaSopenharmony_ci        self.deps = list()
1615f9996aaSopenharmony_ci        self.features = list()
1625f9996aaSopenharmony_ci        self.ignore_options = list()
1635f9996aaSopenharmony_ci        self.srcs = list()               # main_src or merged multiple source files
1645f9996aaSopenharmony_ci        self.shared_libs = list()        # -l dylib=wayland-client, -l z
1655f9996aaSopenharmony_ci        self.static_libs = list()        # -l static=host_cpuid
1665f9996aaSopenharmony_ci        # Parameters collected from Cargo.toml.
1675f9996aaSopenharmony_ci        self.cargo_pkg_version = ''      # value extracted from Cargo.toml version field
1685f9996aaSopenharmony_ci        self.cargo_pkg_authors = ''      # value extracted from Cargo.toml authors field
1695f9996aaSopenharmony_ci        self.cargo_pkg_name = ''         # value extracted from Cargo.toml name field
1705f9996aaSopenharmony_ci        self.cargo_pkg_description = ''  # value extracted from Cargo.toml description field
1715f9996aaSopenharmony_ci        # Parameters related to build.rs.
1725f9996aaSopenharmony_ci        self.build_root = ''
1735f9996aaSopenharmony_ci        self.checked_out_files = False   # to check only once
1745f9996aaSopenharmony_ci        self.build_script_outputs = []   # output files generated by build.rs
1755f9996aaSopenharmony_ci
1765f9996aaSopenharmony_ci    def write(self, s: str):
1775f9996aaSopenharmony_ci        # convenient way to output one line at a time with EOL.
1785f9996aaSopenharmony_ci        self.outfile.write(s + '\n')
1795f9996aaSopenharmony_ci
1805f9996aaSopenharmony_ci    def parse_rustc(self, line_num: int, line: str):
1815f9996aaSopenharmony_ci        """Find important rustc arguments to convert to BUILD.gn properties."""
1825f9996aaSopenharmony_ci        self.line_num = line_num
1835f9996aaSopenharmony_ci        self.line = line
1845f9996aaSopenharmony_ci        args = line.split()  # Loop through every argument of rustc.
1855f9996aaSopenharmony_ci        self.parse_args(args)
1865f9996aaSopenharmony_ci        if not self.crate_name:
1875f9996aaSopenharmony_ci            self.error_infos += 'ERROR: missing --crate-name\n'
1885f9996aaSopenharmony_ci        if not self.crate_types:
1895f9996aaSopenharmony_ci            if 'test' in self.cfgs:
1905f9996aaSopenharmony_ci                self.crate_types.append('test')
1915f9996aaSopenharmony_ci            else:
1925f9996aaSopenharmony_ci                self.error_infos += 'ERROR: missing --crate-type or --test\n'
1935f9996aaSopenharmony_ci        elif len(self.crate_types) > 1:
1945f9996aaSopenharmony_ci            if 'lib' in self.crate_types and 'rlib' in self.crate_types:
1955f9996aaSopenharmony_ci                self.error_infos += 'ERROR: cannot generate both lib and rlib crate types\n'
1965f9996aaSopenharmony_ci            if 'test' in self.crate_types:
1975f9996aaSopenharmony_ci                self.error_infos += 'ERROR: cannot handle both --crate-type and --test\n'
1985f9996aaSopenharmony_ci        if not self.main_src:
1995f9996aaSopenharmony_ci            self.error_infos += 'ERROR: missing main source file\n'
2005f9996aaSopenharmony_ci        else:
2015f9996aaSopenharmony_ci            self.srcs.append(self.main_src)
2025f9996aaSopenharmony_ci        if self.cargo_dir:
2035f9996aaSopenharmony_ci            self.get_root_pkg_name()
2045f9996aaSopenharmony_ci        if not self.root_pkg_name:
2055f9996aaSopenharmony_ci            self.root_pkg_name = self.crate_name
2065f9996aaSopenharmony_ci
2075f9996aaSopenharmony_ci        # Process crate with build.rs
2085f9996aaSopenharmony_ci        if not self.skip_crate():
2095f9996aaSopenharmony_ci            if not self.runner.args.no_pkg_info:
2105f9996aaSopenharmony_ci                self.find_pkg_info()
2115f9996aaSopenharmony_ci            self.find_build_root()
2125f9996aaSopenharmony_ci            if self.runner.args.copy_out:
2135f9996aaSopenharmony_ci                self.copy_out_files()
2145f9996aaSopenharmony_ci            elif self.find_out_files() and self.has_used_out_dir():
2155f9996aaSopenharmony_ci                self.copy_out_files()
2165f9996aaSopenharmony_ci
2175f9996aaSopenharmony_ci        self.cfgs = sorted(set(self.cfgs))
2185f9996aaSopenharmony_ci        self.core_deps = sorted(set(self.core_deps))
2195f9996aaSopenharmony_ci        self.crate_types = sorted(set(self.crate_types))
2205f9996aaSopenharmony_ci        self.deps = sorted(set(self.deps))
2215f9996aaSopenharmony_ci        self.features = sorted(set(self.features))
2225f9996aaSopenharmony_ci        self.ignore_options = sorted(set(self.ignore_options))
2235f9996aaSopenharmony_ci        self.static_libs = sorted(set(self.static_libs))
2245f9996aaSopenharmony_ci        self.shared_libs = sorted(set(self.shared_libs))
2255f9996aaSopenharmony_ci        self.decide_module_type()
2265f9996aaSopenharmony_ci        return self
2275f9996aaSopenharmony_ci
2285f9996aaSopenharmony_ci    def parse_args(self, args):
2295f9996aaSopenharmony_ci        num = 0
2305f9996aaSopenharmony_ci        while num < len(args):
2315f9996aaSopenharmony_ci            arg = args[num]
2325f9996aaSopenharmony_ci            if arg == '--crate-name':
2335f9996aaSopenharmony_ci                num += 1
2345f9996aaSopenharmony_ci                self.crate_name = args[num]
2355f9996aaSopenharmony_ci            elif arg == '--crate-type':
2365f9996aaSopenharmony_ci                num += 1
2375f9996aaSopenharmony_ci                self.crate_types.append(args[num])
2385f9996aaSopenharmony_ci            elif arg == '--cfg':
2395f9996aaSopenharmony_ci                num += 1
2405f9996aaSopenharmony_ci                self.deal_cfg(args[num])
2415f9996aaSopenharmony_ci            elif arg == '-C':
2425f9996aaSopenharmony_ci                num += 1
2435f9996aaSopenharmony_ci                self.add_ignore_options_flag(args[num])  # codegen options
2445f9996aaSopenharmony_ci            elif arg.startswith('-C'):
2455f9996aaSopenharmony_ci                self.add_ignore_options_flag(arg[2:])
2465f9996aaSopenharmony_ci            elif arg == '--cap-lints':
2475f9996aaSopenharmony_ci                num += 1
2485f9996aaSopenharmony_ci                self.cap_lints = args[num]
2495f9996aaSopenharmony_ci            elif arg.startswith('--edition='):
2505f9996aaSopenharmony_ci                self.edition = arg.replace('--edition=', '')
2515f9996aaSopenharmony_ci            elif arg.startswith('--emit='):
2525f9996aaSopenharmony_ci                self.emit_list = arg.replace('--emit=', '')
2535f9996aaSopenharmony_ci            elif arg == '--extern':
2545f9996aaSopenharmony_ci                num += 1
2555f9996aaSopenharmony_ci                self.deal_extern(args[num])
2565f9996aaSopenharmony_ci            elif (arg.startswith('--error-format=') or arg.startswith('--json=') or
2575f9996aaSopenharmony_ci                  arg.startswith('\'-Aclippy')):
2585f9996aaSopenharmony_ci                _ = arg  # ignored
2595f9996aaSopenharmony_ci            elif arg == '-L':
2605f9996aaSopenharmony_ci                num += 1
2615f9996aaSopenharmony_ci                self.set_root_pkg_name(args[num])
2625f9996aaSopenharmony_ci            elif arg == '-l':
2635f9996aaSopenharmony_ci                num += 1
2645f9996aaSopenharmony_ci                self.deal_static_and_dylib(args[num])
2655f9996aaSopenharmony_ci            elif arg == '--out-dir' or arg == '--color':  # ignored
2665f9996aaSopenharmony_ci                num += 1
2675f9996aaSopenharmony_ci            elif arg == '--target':
2685f9996aaSopenharmony_ci                num += 1
2695f9996aaSopenharmony_ci                self.target = args[num]
2705f9996aaSopenharmony_ci            elif arg == '--test':
2715f9996aaSopenharmony_ci                self.crate_types.append('test')
2725f9996aaSopenharmony_ci            elif not arg.startswith('-'):
2735f9996aaSopenharmony_ci                self.set_main_src(args[num])
2745f9996aaSopenharmony_ci            else:
2755f9996aaSopenharmony_ci                self.error_infos += 'ERROR: unknown ' + arg + '\n'
2765f9996aaSopenharmony_ci            num += 1
2775f9996aaSopenharmony_ci
2785f9996aaSopenharmony_ci    def deal_cfg(self, arg: str):
2795f9996aaSopenharmony_ci        if arg.startswith('\'feature='):
2805f9996aaSopenharmony_ci            feature = unquote(arg.replace('\'feature=', '')[:-1])
2815f9996aaSopenharmony_ci            # 'runtime' feature removed because it conflicts with static
2825f9996aaSopenharmony_ci            if feature == 'runtime':
2835f9996aaSopenharmony_ci                feature = 'static'
2845f9996aaSopenharmony_ci            self.features.append(feature)
2855f9996aaSopenharmony_ci        else:
2865f9996aaSopenharmony_ci            self.cfgs.append(arg)
2875f9996aaSopenharmony_ci
2885f9996aaSopenharmony_ci    def add_ignore_options_flag(self, flag: str):
2895f9996aaSopenharmony_ci        """Ignore options not used in GN."""
2905f9996aaSopenharmony_ci        # 'codegen-units' is set in GN global config or by default
2915f9996aaSopenharmony_ci        # 'embed-bitcode' is ignored; we might control LTO with other .gn flag
2925f9996aaSopenharmony_ci        # 'prefer-dynamic' does not work with common flag -C lto
2935f9996aaSopenharmony_ci        if not (flag.startswith('codegen-units=') or flag.startswith('debuginfo=') or
2945f9996aaSopenharmony_ci                flag.startswith('embed-bitcode=') or flag.startswith('extra-filename=') or
2955f9996aaSopenharmony_ci                flag.startswith('incremental=') or flag.startswith('metadata=') or
2965f9996aaSopenharmony_ci                flag == 'prefer-dynamic'):
2975f9996aaSopenharmony_ci            self.ignore_options.append(flag)
2985f9996aaSopenharmony_ci
2995f9996aaSopenharmony_ci    def deal_extern(self, arg: str):
3005f9996aaSopenharmony_ci        deps = re.sub('=/[^ ]*/deps/', ' = ', arg)
3015f9996aaSopenharmony_ci        self.deps.append(deps)
3025f9996aaSopenharmony_ci        self.core_deps.append(re.sub(' = .*', '', deps))
3035f9996aaSopenharmony_ci
3045f9996aaSopenharmony_ci    def set_root_pkg_name(self, arg: str):
3055f9996aaSopenharmony_ci        if arg.startswith('dependency=') and arg.endswith('/deps'):
3065f9996aaSopenharmony_ci            if '/' + TARGET_TEMP + '/' in arg:
3075f9996aaSopenharmony_ci                self.root_pkg_name = re.sub('^.*/', '',
3085f9996aaSopenharmony_ci                                            re.sub('/' + TARGET_TEMP + '/.*/deps$', '', arg))
3095f9996aaSopenharmony_ci            else:
3105f9996aaSopenharmony_ci                self.root_pkg_name = re.sub('^.*/', '',
3115f9996aaSopenharmony_ci                                            re.sub('/[^/]+/[^/]+/deps$', '', arg))
3125f9996aaSopenharmony_ci            self.root_pkg_name = remove_version_suffix(self.root_pkg_name)
3135f9996aaSopenharmony_ci
3145f9996aaSopenharmony_ci    def deal_static_and_dylib(self, arg: str):
3155f9996aaSopenharmony_ci        if arg.startswith('static='):
3165f9996aaSopenharmony_ci            self.static_libs.append(re.sub('static=', '', arg))
3175f9996aaSopenharmony_ci        elif arg.startswith('dylib='):
3185f9996aaSopenharmony_ci            self.shared_libs.append(re.sub('dylib=', '', arg))
3195f9996aaSopenharmony_ci        else:
3205f9996aaSopenharmony_ci            self.shared_libs.append(arg)
3215f9996aaSopenharmony_ci
3225f9996aaSopenharmony_ci    def set_main_src(self, arg: str):
3235f9996aaSopenharmony_ci        self.main_src = re.sub(r'^/[^ ]*/registry/src/', '.../', arg)
3245f9996aaSopenharmony_ci        self.main_src = re.sub(r'^\.\.\./github.com-[0-9a-f]*/', '.../', self.main_src)
3255f9996aaSopenharmony_ci        self.find_cargo_dir()
3265f9996aaSopenharmony_ci        if self.cargo_dir:
3275f9996aaSopenharmony_ci            if self.runner.args.no_subdir:
3285f9996aaSopenharmony_ci                # all .gn content to /dev/null
3295f9996aaSopenharmony_ci                self.outfile_name = '/dev/null'
3305f9996aaSopenharmony_ci            elif not self.runner.args.one_file:
3315f9996aaSopenharmony_ci                # Use Cargo.toml to write BUILD.gn in the subdirectory.
3325f9996aaSopenharmony_ci                self.outfile_name = os.path.join(self.cargo_dir, 'BUILD.gn')
3335f9996aaSopenharmony_ci                self.main_src = self.main_src[len(self.cargo_dir) + 1:]
3345f9996aaSopenharmony_ci
3355f9996aaSopenharmony_ci    def find_cargo_dir(self):
3365f9996aaSopenharmony_ci        """Deepest directory with Cargo.toml and contains the main_src."""
3375f9996aaSopenharmony_ci        if not is_dependent_path(self.main_src):
3385f9996aaSopenharmony_ci            dir_name = os.path.dirname(self.main_src)
3395f9996aaSopenharmony_ci            while dir_name:
3405f9996aaSopenharmony_ci                if dir_name.endswith('.'):
3415f9996aaSopenharmony_ci                    dir_name = os.path.dirname(dir_name)
3425f9996aaSopenharmony_ci                    continue
3435f9996aaSopenharmony_ci                if os.path.exists(os.path.join(dir_name, 'Cargo.toml')):
3445f9996aaSopenharmony_ci                    self.cargo_dir = dir_name
3455f9996aaSopenharmony_ci                    return
3465f9996aaSopenharmony_ci                dir_name = os.path.dirname(dir_name)
3475f9996aaSopenharmony_ci
3485f9996aaSopenharmony_ci    def skip_crate(self):
3495f9996aaSopenharmony_ci        """Return crate_name or a message if this crate should be skipped."""
3505f9996aaSopenharmony_ci        # Some Rust packages include extra unwanted crates.
3515f9996aaSopenharmony_ci        # This set contains all such excluded crate names.
3525f9996aaSopenharmony_ci        excluded_crates = set(['protobuf_bin_gen_rust_do_not_use'])
3535f9996aaSopenharmony_ci        if (is_build_script(self.crate_name) or
3545f9996aaSopenharmony_ci                self.crate_name in excluded_crates):
3555f9996aaSopenharmony_ci            return self.crate_name
3565f9996aaSopenharmony_ci        if is_dependent_path(self.main_src):
3575f9996aaSopenharmony_ci            return 'dependent crate'
3585f9996aaSopenharmony_ci        return ''
3595f9996aaSopenharmony_ci
3605f9996aaSopenharmony_ci    def get_root_pkg_name(self):
3615f9996aaSopenharmony_ci        """Read name of [package] in ./Cargo.toml."""
3625f9996aaSopenharmony_ci        cargo_toml_path = './Cargo.toml'
3635f9996aaSopenharmony_ci        if self.cargo_dir:
3645f9996aaSopenharmony_ci            cargo_toml_path = os.path.join(
3655f9996aaSopenharmony_ci                os.path.join('.', self.cargo_dir), 'Cargo.toml')
3665f9996aaSopenharmony_ci        if not os.path.exists(cargo_toml_path):
3675f9996aaSopenharmony_ci            return
3685f9996aaSopenharmony_ci        with open(cargo_toml_path, 'r') as infile:
3695f9996aaSopenharmony_ci            self.root_pkg_name = get_designated_pkg_info(infile, 'name')
3705f9996aaSopenharmony_ci        return
3715f9996aaSopenharmony_ci
3725f9996aaSopenharmony_ci    def find_pkg_info(self):
3735f9996aaSopenharmony_ci        """Read package info of [package] in ./Cargo.toml."""
3745f9996aaSopenharmony_ci        cargo_toml_path = './Cargo.toml'
3755f9996aaSopenharmony_ci        if self.cargo_dir:
3765f9996aaSopenharmony_ci            cargo_toml_path = os.path.join(
3775f9996aaSopenharmony_ci                os.path.join('.', self.cargo_dir), 'Cargo.toml')
3785f9996aaSopenharmony_ci        if not os.path.exists(cargo_toml_path):
3795f9996aaSopenharmony_ci            return
3805f9996aaSopenharmony_ci        with open(cargo_toml_path, 'r') as infile:
3815f9996aaSopenharmony_ci            if self.root_pkg_name:
3825f9996aaSopenharmony_ci                self.cargo_pkg_name = self.root_pkg_name
3835f9996aaSopenharmony_ci            else:
3845f9996aaSopenharmony_ci                self.cargo_pkg_name = get_designated_pkg_info(infile, 'name')
3855f9996aaSopenharmony_ci            infile.seek(0)
3865f9996aaSopenharmony_ci            self.cargo_pkg_version = get_designated_pkg_info(infile, 'version')
3875f9996aaSopenharmony_ci            infile.seek(0)
3885f9996aaSopenharmony_ci            pkg_description = get_designated_pkg_info(infile, 'description')
3895f9996aaSopenharmony_ci            pkg_description = pkg_description.replace('\n', '').replace(r'\n', '').strip()
3905f9996aaSopenharmony_ci            self.cargo_pkg_description = pkg_description
3915f9996aaSopenharmony_ci            infile.seek(0)
3925f9996aaSopenharmony_ci            authors_re = re.compile(' *authors *= * \[(.*?)\]', re.S)
3935f9996aaSopenharmony_ci            authors_section = authors_re.search(infile.read())
3945f9996aaSopenharmony_ci            if authors_section:
3955f9996aaSopenharmony_ci                authors = authors_section.group(1)
3965f9996aaSopenharmony_ci                authors = authors.replace('\n', '').replace('  ', ' ').replace('"', '').strip()
3975f9996aaSopenharmony_ci                if authors.endswith(','):
3985f9996aaSopenharmony_ci                    authors = authors[:-1]
3995f9996aaSopenharmony_ci                self.cargo_pkg_authors = authors
4005f9996aaSopenharmony_ci
4015f9996aaSopenharmony_ci    def find_build_root(self):
4025f9996aaSopenharmony_ci        """Read build of [package] in ./Cargo.toml."""
4035f9996aaSopenharmony_ci        cargo_toml_path = './Cargo.toml'
4045f9996aaSopenharmony_ci        if self.cargo_dir:
4055f9996aaSopenharmony_ci            cargo_toml_path = os.path.join(
4065f9996aaSopenharmony_ci                os.path.join('.', self.cargo_dir), 'Cargo.toml')
4075f9996aaSopenharmony_ci        if not os.path.exists(cargo_toml_path):
4085f9996aaSopenharmony_ci            return
4095f9996aaSopenharmony_ci        with open(cargo_toml_path, 'r') as infile:
4105f9996aaSopenharmony_ci            self.build_root = get_designated_pkg_info(infile, 'build')
4115f9996aaSopenharmony_ci        if not self.build_root:
4125f9996aaSopenharmony_ci            build_rs_path = './build.rs'
4135f9996aaSopenharmony_ci            if self.cargo_dir:
4145f9996aaSopenharmony_ci                build_rs_path = os.path.join(os.path.join('.', self.cargo_dir), 'build.rs')
4155f9996aaSopenharmony_ci            if os.path.exists(build_rs_path):
4165f9996aaSopenharmony_ci                self.build_root = 'build.rs'
4175f9996aaSopenharmony_ci
4185f9996aaSopenharmony_ci    def find_out_files(self):
4195f9996aaSopenharmony_ci        # normal_output_list has build.rs output for normal crates
4205f9996aaSopenharmony_ci        normal_output_list = glob.glob(
4215f9996aaSopenharmony_ci            TARGET_TEMP + '/*/*/build/' + self.root_pkg_name + '-*/out/*')
4225f9996aaSopenharmony_ci        # other_output_list has build.rs output for proc-macro crates
4235f9996aaSopenharmony_ci        other_output_list = glob.glob(
4245f9996aaSopenharmony_ci            TARGET_TEMP + '/*/build/' + self.root_pkg_name + '-*/out/*')
4255f9996aaSopenharmony_ci        return normal_output_list + other_output_list
4265f9996aaSopenharmony_ci
4275f9996aaSopenharmony_ci    def has_used_out_dir(self):
4285f9996aaSopenharmony_ci        '''Returns true if env!('OUT_DIR') is found.'''
4295f9996aaSopenharmony_ci        cmd = ['grep', '-rl', '--exclude', 'build.rs', '--include', '*.rs', "env!('OUT_DIR')", '*']
4305f9996aaSopenharmony_ci        if self.cargo_dir:
4315f9996aaSopenharmony_ci            cmd = ['grep', '-rl', '--exclude', os.path.join(self.cargo_dir, 'build.rs'), '--include', \
4325f9996aaSopenharmony_ci            '*.rs', "env!('OUT_DIR')", '*']
4335f9996aaSopenharmony_ci        return subprocess.call(cmd, shell=False) == 0
4345f9996aaSopenharmony_ci
4355f9996aaSopenharmony_ci    def copy_out_files(self):
4365f9996aaSopenharmony_ci        """Copy build.rs output files to ./out and set up build_script_outputs."""
4375f9996aaSopenharmony_ci        if self.checked_out_files:
4385f9996aaSopenharmony_ci            return
4395f9996aaSopenharmony_ci        self.checked_out_files = True
4405f9996aaSopenharmony_ci        cargo_out_files = self.find_out_files()
4415f9996aaSopenharmony_ci        out_files = set()
4425f9996aaSopenharmony_ci        out_path = 'out'
4435f9996aaSopenharmony_ci        if self.cargo_dir:
4445f9996aaSopenharmony_ci            out_path = os.path.join(self.cargo_dir, out_path)
4455f9996aaSopenharmony_ci        if cargo_out_files:
4465f9996aaSopenharmony_ci            os.makedirs(out_path, exist_ok=True)
4475f9996aaSopenharmony_ci        for path in cargo_out_files:
4485f9996aaSopenharmony_ci            file_name = path.split('/')[-1]
4495f9996aaSopenharmony_ci            out_files.add(file_name)
4505f9996aaSopenharmony_ci        self.build_script_outputs = sorted(out_files)
4515f9996aaSopenharmony_ci
4525f9996aaSopenharmony_ci    def decide_module_type(self):
4535f9996aaSopenharmony_ci        # Use the first crate type for the default/first module.
4545f9996aaSopenharmony_ci        crate_type = self.crate_types[0] if self.crate_types else ''
4555f9996aaSopenharmony_ci        self.decide_one_module_type(crate_type)
4565f9996aaSopenharmony_ci
4575f9996aaSopenharmony_ci    def decide_one_module_type(self, crate_type: str):
4585f9996aaSopenharmony_ci        """Decide which GN module type to use."""
4595f9996aaSopenharmony_ci        if crate_type == 'bin':
4605f9996aaSopenharmony_ci            self.module_type = self.crate_name
4615f9996aaSopenharmony_ci        elif crate_type in LIBRARY_CRATE_TYPES:
4625f9996aaSopenharmony_ci            self.module_type = 'lib'
4635f9996aaSopenharmony_ci        elif crate_type == 'test':
4645f9996aaSopenharmony_ci            self.module_type = 'test'
4655f9996aaSopenharmony_ci        else:
4665f9996aaSopenharmony_ci            self.module_type = ''
4675f9996aaSopenharmony_ci
4685f9996aaSopenharmony_ci    def merge_crate(self, other, outfile_name: str):
4695f9996aaSopenharmony_ci        """Try to merge crate into self."""
4705f9996aaSopenharmony_ci        # Cargo build --tests could recompile a library for tests.
4715f9996aaSopenharmony_ci        # We need to merge such duplicated calls to rustc, with the
4725f9996aaSopenharmony_ci        # algorithm in is_should_merge.
4735f9996aaSopenharmony_ci        should_merge = self.is_should_merge(other)
4745f9996aaSopenharmony_ci        should_merge_test = False
4755f9996aaSopenharmony_ci        if not should_merge:
4765f9996aaSopenharmony_ci            should_merge_test = self.merge_test(other)
4775f9996aaSopenharmony_ci        if should_merge or should_merge_test:
4785f9996aaSopenharmony_ci            self.runner.init_gn_file(outfile_name)
4795f9996aaSopenharmony_ci            # to write debug info
4805f9996aaSopenharmony_ci            with open(outfile_name, 'a') as outfile:
4815f9996aaSopenharmony_ci                self.outfile = outfile
4825f9996aaSopenharmony_ci                other.outfile = outfile
4835f9996aaSopenharmony_ci                self.execute_merge(other, should_merge_test)
4845f9996aaSopenharmony_ci            return True
4855f9996aaSopenharmony_ci        return False
4865f9996aaSopenharmony_ci
4875f9996aaSopenharmony_ci    def is_should_merge(self, other):
4885f9996aaSopenharmony_ci        return (self.crate_name == other.crate_name and
4895f9996aaSopenharmony_ci                self.crate_types == other.crate_types and
4905f9996aaSopenharmony_ci                self.main_src == other.main_src and
4915f9996aaSopenharmony_ci                self.root_pkg_name == other.root_pkg_name and
4925f9996aaSopenharmony_ci                not self.skip_crate() and self.is_same_flags(other))
4935f9996aaSopenharmony_ci
4945f9996aaSopenharmony_ci    def merge_test(self, other):
4955f9996aaSopenharmony_ci        """Returns true if self and other are tests of same root_pkg_name."""
4965f9996aaSopenharmony_ci        # Before merger, each test has its own crate_name. A merged test uses
4975f9996aaSopenharmony_ci        # its source file base name as output file name, so a test is mergeable
4985f9996aaSopenharmony_ci        # only if its base name equals to its crate name.
4995f9996aaSopenharmony_ci        return (self.crate_types == other.crate_types and self.crate_types == ['test'] and
5005f9996aaSopenharmony_ci                self.root_pkg_name == other.root_pkg_name and not self.skip_crate() and
5015f9996aaSopenharmony_ci                other.crate_name == get_base_name(other.main_src) and
5025f9996aaSopenharmony_ci                (len(self.srcs) > 1 or (self.crate_name == get_base_name(self.main_src))) and
5035f9996aaSopenharmony_ci                self.is_same_flags(other))
5045f9996aaSopenharmony_ci
5055f9996aaSopenharmony_ci    def is_same_flags(self, other):
5065f9996aaSopenharmony_ci        return (not self.error_infos and not other.error_infos and
5075f9996aaSopenharmony_ci                self.cap_lints == other.cap_lints and self.cfgs == other.cfgs and
5085f9996aaSopenharmony_ci                self.core_deps == other.core_deps and self.edition == other.edition and
5095f9996aaSopenharmony_ci                self.emit_list == other.emit_list and self.features == other.features and
5105f9996aaSopenharmony_ci                self.ignore_options == other.ignore_options and
5115f9996aaSopenharmony_ci                self.static_libs == other.static_libs and
5125f9996aaSopenharmony_ci                self.shared_libs == other.shared_libs)
5135f9996aaSopenharmony_ci
5145f9996aaSopenharmony_ci    def execute_merge(self, other, should_merge_test):
5155f9996aaSopenharmony_ci        """Merge attributes of other to self."""
5165f9996aaSopenharmony_ci        if self.debug:
5175f9996aaSopenharmony_ci            self.write('\n// Before merge definition(self):')
5185f9996aaSopenharmony_ci            self.dump_debug_info()
5195f9996aaSopenharmony_ci            self.write('\n// Before merge definition(other):')
5205f9996aaSopenharmony_ci            other.dump_debug_info()
5215f9996aaSopenharmony_ci        if not self.target:
5225f9996aaSopenharmony_ci            # okay to keep only the first target triple
5235f9996aaSopenharmony_ci            self.target = other.target
5245f9996aaSopenharmony_ci        self.decide_module_type()
5255f9996aaSopenharmony_ci        if should_merge_test:
5265f9996aaSopenharmony_ci            if (self.runner.should_ignore_test(self.main_src) and
5275f9996aaSopenharmony_ci                not self.runner.should_ignore_test(other.main_src)):
5285f9996aaSopenharmony_ci                self.main_src = other.main_src
5295f9996aaSopenharmony_ci            self.srcs.append(other.main_src)
5305f9996aaSopenharmony_ci            self.crate_name = pkg_to_crate_name(self.root_pkg_name)
5315f9996aaSopenharmony_ci        if self.debug:
5325f9996aaSopenharmony_ci            self.write('\n// After merge definition:')
5335f9996aaSopenharmony_ci            self.dump_debug_info()
5345f9996aaSopenharmony_ci
5355f9996aaSopenharmony_ci    def dump(self):
5365f9996aaSopenharmony_ci        """Dump all error/debug/module code to the output .gn file."""
5375f9996aaSopenharmony_ci        self.runner.init_gn_file(self.outfile_name)
5385f9996aaSopenharmony_ci        with open(self.outfile_name, 'a') as outfile:
5395f9996aaSopenharmony_ci            self.outfile = outfile
5405f9996aaSopenharmony_ci            if self.error_infos:
5415f9996aaSopenharmony_ci                self.dump_line()
5425f9996aaSopenharmony_ci                self.write(self.error_infos)
5435f9996aaSopenharmony_ci            elif self.skip_crate():
5445f9996aaSopenharmony_ci                self.dump_skip_crate(self.skip_crate())
5455f9996aaSopenharmony_ci            else:
5465f9996aaSopenharmony_ci                if self.debug:
5475f9996aaSopenharmony_ci                    self.dump_debug_info()
5485f9996aaSopenharmony_ci                self.dump_gn_module()
5495f9996aaSopenharmony_ci
5505f9996aaSopenharmony_ci    def dump_debug_info(self):
5515f9996aaSopenharmony_ci        """Dump parsed data, when cargo2gn is called with --debug."""
5525f9996aaSopenharmony_ci
5535f9996aaSopenharmony_ci        def dump(name, value):
5545f9996aaSopenharmony_ci            self.write('//%12s = %s' % (name, value))
5555f9996aaSopenharmony_ci
5565f9996aaSopenharmony_ci        def dump_list(fmt, values):
5575f9996aaSopenharmony_ci            for v in values:
5585f9996aaSopenharmony_ci                self.write(fmt % v)
5595f9996aaSopenharmony_ci
5605f9996aaSopenharmony_ci        def opt_dump(name, value):
5615f9996aaSopenharmony_ci            if value:
5625f9996aaSopenharmony_ci                dump(name, value)
5635f9996aaSopenharmony_ci
5645f9996aaSopenharmony_ci        self.dump_line()
5655f9996aaSopenharmony_ci        dump('crate_name', self.crate_name)
5665f9996aaSopenharmony_ci        dump('crate_types', self.crate_types)
5675f9996aaSopenharmony_ci        opt_dump('edition', self.edition)
5685f9996aaSopenharmony_ci        opt_dump('emit_list', self.emit_list)
5695f9996aaSopenharmony_ci        dump('main_src', self.main_src)
5705f9996aaSopenharmony_ci        dump('module_type', self.module_type)
5715f9996aaSopenharmony_ci        opt_dump('target', self.target)
5725f9996aaSopenharmony_ci        opt_dump('cap_lints', self.cap_lints)
5735f9996aaSopenharmony_ci        dump_list('// cfg = %s', self.cfgs)
5745f9996aaSopenharmony_ci        dump_list('// cfg = \'feature "%s"\'', self.features)
5755f9996aaSopenharmony_ci        dump_list('// codegen = %s', self.ignore_options)
5765f9996aaSopenharmony_ci        dump_list('// deps = %s', self.deps)
5775f9996aaSopenharmony_ci        dump_list('// -l (dylib) = %s', self.shared_libs)
5785f9996aaSopenharmony_ci        dump_list('// -l static = %s', self.static_libs)
5795f9996aaSopenharmony_ci
5805f9996aaSopenharmony_ci    def dump_line(self):
5815f9996aaSopenharmony_ci        self.write('\n// Line ' + str(self.line_num) + ' ' + self.line)
5825f9996aaSopenharmony_ci
5835f9996aaSopenharmony_ci    def dump_skip_crate(self, kind: str):
5845f9996aaSopenharmony_ci        if self.debug:
5855f9996aaSopenharmony_ci            self.write('\n// IGNORED: ' + kind + ' ' + self.main_src)
5865f9996aaSopenharmony_ci        return self
5875f9996aaSopenharmony_ci
5885f9996aaSopenharmony_ci    def dump_gn_module(self):
5895f9996aaSopenharmony_ci        """Dump one or more GN module definition, depending on crate_types."""
5905f9996aaSopenharmony_ci        if len(self.crate_types) == 1:
5915f9996aaSopenharmony_ci            self.dump_single_type_gn_module()
5925f9996aaSopenharmony_ci            return
5935f9996aaSopenharmony_ci        if 'test' in self.crate_types:
5945f9996aaSopenharmony_ci            self.write('\nERROR: multiple crate types cannot include test type')
5955f9996aaSopenharmony_ci            return
5965f9996aaSopenharmony_ci        # Dump one GN module per crate_type.
5975f9996aaSopenharmony_ci        for crate_type in self.crate_types:
5985f9996aaSopenharmony_ci            self.decide_one_module_type(crate_type)
5995f9996aaSopenharmony_ci            self.dump_one_gn_module(crate_type)
6005f9996aaSopenharmony_ci
6015f9996aaSopenharmony_ci    def dump_single_type_gn_module(self):
6025f9996aaSopenharmony_ci        """Dump one simple GN module, which has only one crate_type."""
6035f9996aaSopenharmony_ci        crate_type = self.crate_types[0]
6045f9996aaSopenharmony_ci        if crate_type != 'test':
6055f9996aaSopenharmony_ci            self.dump_one_gn_module(crate_type)
6065f9996aaSopenharmony_ci            return
6075f9996aaSopenharmony_ci        # Dump one test module per source file.
6085f9996aaSopenharmony_ci        self.srcs = [
6095f9996aaSopenharmony_ci            src for src in self.srcs if not self.runner.should_ignore_test(src)]
6105f9996aaSopenharmony_ci        if len(self.srcs) > 1:
6115f9996aaSopenharmony_ci            self.srcs = sorted(set(self.srcs))
6125f9996aaSopenharmony_ci        saved_srcs = self.srcs
6135f9996aaSopenharmony_ci        for src in saved_srcs:
6145f9996aaSopenharmony_ci            self.srcs = [src]
6155f9996aaSopenharmony_ci            saved_main_src = self.main_src
6165f9996aaSopenharmony_ci            self.main_src = src
6175f9996aaSopenharmony_ci            self.decide_one_module_type(crate_type)
6185f9996aaSopenharmony_ci            self.dump_one_gn_module(crate_type)
6195f9996aaSopenharmony_ci            self.main_src = saved_main_src
6205f9996aaSopenharmony_ci        self.srcs = saved_srcs
6215f9996aaSopenharmony_ci
6225f9996aaSopenharmony_ci    def dump_one_gn_module(self, crate_type: str):
6235f9996aaSopenharmony_ci        """Dump one GN module definition."""
6245f9996aaSopenharmony_ci        if not self.module_type:
6255f9996aaSopenharmony_ci            self.write('\nERROR: unknown crate_type ' + crate_type)
6265f9996aaSopenharmony_ci            return
6275f9996aaSopenharmony_ci        self.write('\nohos_cargo_crate("' + self.module_type + '") {')
6285f9996aaSopenharmony_ci        self.dump_gn_first_properties(crate_type)
6295f9996aaSopenharmony_ci        self.dump_gn_core_properties()
6305f9996aaSopenharmony_ci        self.write('}')
6315f9996aaSopenharmony_ci
6325f9996aaSopenharmony_ci    def dump_gn_first_properties(self, crate_type: str):
6335f9996aaSopenharmony_ci        if crate_type != 'bin':
6345f9996aaSopenharmony_ci            self.write('    crate_name = "' + self.crate_name + '"')
6355f9996aaSopenharmony_ci        if crate_type:
6365f9996aaSopenharmony_ci            if crate_type == 'lib':
6375f9996aaSopenharmony_ci                crate_type = 'rlib'
6385f9996aaSopenharmony_ci            self.write('    crate_type = "' + crate_type + '"')
6395f9996aaSopenharmony_ci        if self.main_src:
6405f9996aaSopenharmony_ci            self.write('    crate_root = "' + self.main_src + '"')
6415f9996aaSopenharmony_ci        if self.crate_name.startswith('lib'):
6425f9996aaSopenharmony_ci            self.write('    output_name = "lib' + self.crate_name + '"')
6435f9996aaSopenharmony_ci        self.write('')
6445f9996aaSopenharmony_ci
6455f9996aaSopenharmony_ci    def dump_gn_core_properties(self):
6465f9996aaSopenharmony_ci        self.dump_sources_list()
6475f9996aaSopenharmony_ci        if self.edition:
6485f9996aaSopenharmony_ci            self.write('    edition = "' + self.edition + '"')
6495f9996aaSopenharmony_ci        if not self.runner.args.no_pkg_info:
6505f9996aaSopenharmony_ci            if self.cargo_pkg_version:
6515f9996aaSopenharmony_ci                self.write('    cargo_pkg_version = "' +
6525f9996aaSopenharmony_ci                           self.cargo_pkg_version + '"')
6535f9996aaSopenharmony_ci            if self.cargo_pkg_authors:
6545f9996aaSopenharmony_ci                self.write('    cargo_pkg_authors = "' +
6555f9996aaSopenharmony_ci                           self.cargo_pkg_authors + '"')
6565f9996aaSopenharmony_ci            if self.cargo_pkg_name:
6575f9996aaSopenharmony_ci                self.write('    cargo_pkg_name = "' +
6585f9996aaSopenharmony_ci                           self.cargo_pkg_name + '"')
6595f9996aaSopenharmony_ci            if self.cargo_pkg_description:
6605f9996aaSopenharmony_ci                self.write('    cargo_pkg_description = "' +
6615f9996aaSopenharmony_ci                           self.cargo_pkg_description + '"')
6625f9996aaSopenharmony_ci        if self.deps:
6635f9996aaSopenharmony_ci            self.dump_gn_deps()
6645f9996aaSopenharmony_ci        if self.build_root and self.root_pkg_name in self.runner.build_deps:
6655f9996aaSopenharmony_ci            self.dump_gn_build_deps()
6665f9996aaSopenharmony_ci        self.dump_gn_property_list('features', '"%s"', self.features)
6675f9996aaSopenharmony_ci        if self.build_root:
6685f9996aaSopenharmony_ci            self.write('    build_root = "' + self.build_root + '"')
6695f9996aaSopenharmony_ci            build_sources = list()
6705f9996aaSopenharmony_ci            build_sources.append(self.build_root)
6715f9996aaSopenharmony_ci            self.dump_gn_property_list('build_sources', '"%s"', build_sources)
6725f9996aaSopenharmony_ci            if self.build_script_outputs:
6735f9996aaSopenharmony_ci                self.dump_gn_property_list(
6745f9996aaSopenharmony_ci                    'build_script_outputs', '"%s"', self.build_script_outputs)
6755f9996aaSopenharmony_ci
6765f9996aaSopenharmony_ci    def dump_sources_list(self):
6775f9996aaSopenharmony_ci        """Dump the srcs list, for defaults or regular modules."""
6785f9996aaSopenharmony_ci        if len(self.srcs) > 1:
6795f9996aaSopenharmony_ci            srcs = sorted(set(self.srcs))  # make a copy and dedup
6805f9996aaSopenharmony_ci            for num in range(len(self.srcs)):
6815f9996aaSopenharmony_ci                srcs[num] = srcs[num]
6825f9996aaSopenharmony_ci        else:
6835f9996aaSopenharmony_ci            srcs = [self.main_src]
6845f9996aaSopenharmony_ci        self.dump_gn_property_list('sources', '"%s"', srcs)
6855f9996aaSopenharmony_ci
6865f9996aaSopenharmony_ci    def dump_gn_deps(self):
6875f9996aaSopenharmony_ci        """Dump the deps."""
6885f9996aaSopenharmony_ci        rust_deps = list()
6895f9996aaSopenharmony_ci        deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
6905f9996aaSopenharmony_ci        for lib in self.deps:
6915f9996aaSopenharmony_ci            libname_groups = deps_libname.match(lib)
6925f9996aaSopenharmony_ci            if libname_groups is not None:
6935f9996aaSopenharmony_ci                lib_name = libname_groups.group(1)
6945f9996aaSopenharmony_ci            else:
6955f9996aaSopenharmony_ci                lib_name = re.sub(' .*$', '', lib)
6965f9996aaSopenharmony_ci            if lib_name in self.runner.args.dependency_blocklist:
6975f9996aaSopenharmony_ci                continue
6985f9996aaSopenharmony_ci            if lib.endswith('.rlib') or lib.endswith('.rmeta') or lib.endswith('.so'):
6995f9996aaSopenharmony_ci                # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
7005f9996aaSopenharmony_ci                rust_lib = self.get_rust_lib(lib_name)
7015f9996aaSopenharmony_ci                if rust_lib:
7025f9996aaSopenharmony_ci                    rust_lib += ':lib'
7035f9996aaSopenharmony_ci                    rust_deps.append(rust_lib)
7045f9996aaSopenharmony_ci            elif lib != 'proc_macro':
7055f9996aaSopenharmony_ci                # --extern proc_macro is special and ignored
7065f9996aaSopenharmony_ci                rust_deps.append('// unknown type of lib: '.join(lib))
7075f9996aaSopenharmony_ci        if rust_deps:
7085f9996aaSopenharmony_ci            self.dump_gn_property_list('deps', '"%s"', rust_deps)
7095f9996aaSopenharmony_ci
7105f9996aaSopenharmony_ci    def dump_gn_build_deps(self):
7115f9996aaSopenharmony_ci        """Dump the build deps."""
7125f9996aaSopenharmony_ci        rust_build_deps = list()
7135f9996aaSopenharmony_ci        build_deps_libname = re.compile('^.* = lib(.*)-[0-9a-f]*.(rlib|so|rmeta)$')
7145f9996aaSopenharmony_ci        build_deps = self.runner.build_deps.get(self.root_pkg_name)
7155f9996aaSopenharmony_ci        if not build_deps:
7165f9996aaSopenharmony_ci            return
7175f9996aaSopenharmony_ci        for lib in build_deps:
7185f9996aaSopenharmony_ci            libname_groups = build_deps_libname.match(lib)
7195f9996aaSopenharmony_ci            if libname_groups is not None:
7205f9996aaSopenharmony_ci                lib_name = libname_groups.group(1)
7215f9996aaSopenharmony_ci            else:
7225f9996aaSopenharmony_ci                lib_name = re.sub(' .*$', '', lib)
7235f9996aaSopenharmony_ci            if lib_name in self.runner.args.dependency_blocklist:
7245f9996aaSopenharmony_ci                continue
7255f9996aaSopenharmony_ci            if lib.endswith('.rlib') or lib.endswith('.rmeta') or lib.endswith('.so'):
7265f9996aaSopenharmony_ci                # On MacOS .rmeta is used when Linux uses .rlib or .rmeta.
7275f9996aaSopenharmony_ci                rust_lib = self.get_rust_lib(lib_name)
7285f9996aaSopenharmony_ci                if rust_lib:
7295f9996aaSopenharmony_ci                    rust_build_deps.append(rust_lib + ':lib')
7305f9996aaSopenharmony_ci            elif lib != 'proc_macro':
7315f9996aaSopenharmony_ci                # --extern proc_macro is special and ignored
7325f9996aaSopenharmony_ci                rust_build_deps.append('// unknown type of lib: '.join(lib))
7335f9996aaSopenharmony_ci        if rust_build_deps:
7345f9996aaSopenharmony_ci            self.dump_gn_property_list('build_deps', '"%s"', rust_build_deps)
7355f9996aaSopenharmony_ci
7365f9996aaSopenharmony_ci    def dump_gn_property_list(self, name: str, fmt, values):
7375f9996aaSopenharmony_ci        if not values:
7385f9996aaSopenharmony_ci            return
7395f9996aaSopenharmony_ci        if len(values) > 1:
7405f9996aaSopenharmony_ci            self.write('    ' + name + ' = [')
7415f9996aaSopenharmony_ci            self.dump_gn_property_list_items(fmt, values)
7425f9996aaSopenharmony_ci            self.write('    ]')
7435f9996aaSopenharmony_ci        else:
7445f9996aaSopenharmony_ci            self.write('    ' + name + ' = [' +
7455f9996aaSopenharmony_ci                       (fmt % escape_quotes(values[0])) + ']')
7465f9996aaSopenharmony_ci
7475f9996aaSopenharmony_ci    def dump_gn_property_list_items(self, fmt, values):
7485f9996aaSopenharmony_ci        for v in values:
7495f9996aaSopenharmony_ci            # fmt has quotes, so we need escape_quotes(v)
7505f9996aaSopenharmony_ci            self.write('        ' + (fmt % escape_quotes(v)) + ',')
7515f9996aaSopenharmony_ci
7525f9996aaSopenharmony_ci    def get_rust_lib(self, lib_name: str):
7535f9996aaSopenharmony_ci        rust_lib = ''
7545f9996aaSopenharmony_ci        if lib_name:
7555f9996aaSopenharmony_ci            crate_name = pkg_to_crate_name(lib_name)
7565f9996aaSopenharmony_ci            deps_libname = self.runner.deps_libname_map.get(crate_name)
7575f9996aaSopenharmony_ci            if deps_libname:
7585f9996aaSopenharmony_ci                rust_lib = RUST_PATH + deps_libname
7595f9996aaSopenharmony_ci        return rust_lib
7605f9996aaSopenharmony_ci
7615f9996aaSopenharmony_ci
7625f9996aaSopenharmony_ciclass Runner(object):
7635f9996aaSopenharmony_ci    """Main class to parse cargo -v output"""
7645f9996aaSopenharmony_ci
7655f9996aaSopenharmony_ci    def __init__(self, args):
7665f9996aaSopenharmony_ci        self.gn_files = set()    # Remember all output BUILD.gn files.
7675f9996aaSopenharmony_ci        self.root_pkg_name = ''  # name of package in ./Cargo.toml
7685f9996aaSopenharmony_ci        self.args = args
7695f9996aaSopenharmony_ci        self.dry_run = not args.run
7705f9996aaSopenharmony_ci        self.skip_cargo = args.skipcargo
7715f9996aaSopenharmony_ci        self.cargo_path = ['./cargo']  # path to cargo
7725f9996aaSopenharmony_ci        self.crates = list()         # all crates
7735f9996aaSopenharmony_ci        self.error_infos = ''        # all error infos
7745f9996aaSopenharmony_ci        self.test_error_infos = ''   # all test error infos
7755f9996aaSopenharmony_ci        self.warning_files = set()   # all warning files
7765f9996aaSopenharmony_ci        self.set_cargo_path()
7775f9996aaSopenharmony_ci        # Default operation is cargo clean, followed by build or user given operation.
7785f9996aaSopenharmony_ci        if args.cargo:
7795f9996aaSopenharmony_ci            self.cargo = ['clean'] + args.cargo
7805f9996aaSopenharmony_ci        else:
7815f9996aaSopenharmony_ci            # Use the same target for both host and default device builds.
7825f9996aaSopenharmony_ci            self.cargo = ['clean', 'build --target x86_64-unknown-linux-gnu']
7835f9996aaSopenharmony_ci        self.empty_tests = set()
7845f9996aaSopenharmony_ci        self.empty_unittests = False
7855f9996aaSopenharmony_ci        self.build_deps = {}
7865f9996aaSopenharmony_ci        self.deps_libname_map = {}
7875f9996aaSopenharmony_ci
7885f9996aaSopenharmony_ci    def set_cargo_path(self):
7895f9996aaSopenharmony_ci        """Find cargo in the --cargo_bin and set cargo path"""
7905f9996aaSopenharmony_ci        if self.args.cargo_bin:
7915f9996aaSopenharmony_ci            self.cargo_path = [os.path.join(self.args.cargo_bin, 'cargo')]
7925f9996aaSopenharmony_ci            if os.path.isfile(self.cargo_path):
7935f9996aaSopenharmony_ci                print('INFO: using cargo in ' + self.args.cargo_bin)
7945f9996aaSopenharmony_ci                return
7955f9996aaSopenharmony_ci            else:
7965f9996aaSopenharmony_ci                sys.exit('ERROR: cannot find cargo in ' + self.args.cargo_bin)
7975f9996aaSopenharmony_ci        else:
7985f9996aaSopenharmony_ci            sys.exit('ERROR: the prebuilt cargo is not available; please use the --cargo_bin flag.')
7995f9996aaSopenharmony_ci        return
8005f9996aaSopenharmony_ci
8015f9996aaSopenharmony_ci    def run_cargo(self):
8025f9996aaSopenharmony_ci        """Run cargo -v and save its output to ./cargo.out."""
8035f9996aaSopenharmony_ci        if self.skip_cargo:
8045f9996aaSopenharmony_ci            return self
8055f9996aaSopenharmony_ci        cargo_toml = './Cargo.toml'
8065f9996aaSopenharmony_ci        cargo_out = './cargo.out'
8075f9996aaSopenharmony_ci        if not os.access(cargo_toml, os.R_OK):
8085f9996aaSopenharmony_ci            print('ERROR: Cannot find ', cargo_toml)
8095f9996aaSopenharmony_ci            return self
8105f9996aaSopenharmony_ci        cargo_lock = './Cargo.lock'
8115f9996aaSopenharmony_ci        cargo_lock_save = './cargo.lock.save'
8125f9996aaSopenharmony_ci        have_cargo_lock = os.path.exists(cargo_lock)
8135f9996aaSopenharmony_ci        if not self.dry_run:
8145f9996aaSopenharmony_ci            if os.path.exists(cargo_out):
8155f9996aaSopenharmony_ci                os.remove(cargo_out)
8165f9996aaSopenharmony_ci            if not self.args.use_cargo_lock and have_cargo_lock:
8175f9996aaSopenharmony_ci                os.rename(cargo_lock, cargo_lock_save)
8185f9996aaSopenharmony_ci        # set up search PATH for cargo to find the correct rustc
8195f9996aaSopenharmony_ci        save_path = os.environ['PATH']
8205f9996aaSopenharmony_ci        os.environ['PATH'] = os.path.dirname(self.cargo_path) + ':' + save_path
8215f9996aaSopenharmony_ci        # Add [workspace] to Cargo.toml if it is non-existent.
8225f9996aaSopenharmony_ci        is_add_workspace = False
8235f9996aaSopenharmony_ci        if self.args.add_workspace:
8245f9996aaSopenharmony_ci            with open(cargo_toml, 'r') as in_file:
8255f9996aaSopenharmony_ci                cargo_toml_lines = in_file.readlines()
8265f9996aaSopenharmony_ci            if '[workspace]\n' in cargo_toml_lines:
8275f9996aaSopenharmony_ci                print('WARNING: found [workspace] in Cargo.toml')
8285f9996aaSopenharmony_ci            else:
8295f9996aaSopenharmony_ci                with open(cargo_toml, 'w') as out_file:
8305f9996aaSopenharmony_ci                    out_file.write('[workspace]\n')
8315f9996aaSopenharmony_ci                    is_add_workspace = True
8325f9996aaSopenharmony_ci        self.deal_cargo_cmd(cargo_out)
8335f9996aaSopenharmony_ci        # restore original Cargo.toml
8345f9996aaSopenharmony_ci        if is_add_workspace:
8355f9996aaSopenharmony_ci            with open(cargo_toml, 'w') as out_file:
8365f9996aaSopenharmony_ci                out_file.writelines(cargo_toml_lines)
8375f9996aaSopenharmony_ci        if not self.dry_run:
8385f9996aaSopenharmony_ci            if not have_cargo_lock:  # restore to no Cargo.lock state
8395f9996aaSopenharmony_ci                if os.path.exists(cargo_lock):
8405f9996aaSopenharmony_ci                    os.remove(cargo_lock)
8415f9996aaSopenharmony_ci            elif not self.args.use_cargo_lock:  # restore saved Cargo.lock
8425f9996aaSopenharmony_ci                os.rename(cargo_lock_save, cargo_lock)
8435f9996aaSopenharmony_ci        os.environ['PATH'] = save_path
8445f9996aaSopenharmony_ci        return self
8455f9996aaSopenharmony_ci
8465f9996aaSopenharmony_ci    def deal_cargo_cmd(self, cargo_out: str):
8475f9996aaSopenharmony_ci        cargo_cmd_v_flag = ['-vv'] if self.args.vv else ['-v']
8485f9996aaSopenharmony_ci        cargo_cmd_target_dir = ['--target-dir', TARGET_TEMP]
8495f9996aaSopenharmony_ci        cargo_cmd_redir = ['>>', cargo_out, '2>&1']
8505f9996aaSopenharmony_ci        for cargo in self.cargo:
8515f9996aaSopenharmony_ci            cargo_cmd = [self.cargo_path] + cargo_cmd_v_flag
8525f9996aaSopenharmony_ci            features = []
8535f9996aaSopenharmony_ci            if cargo != 'clean':
8545f9996aaSopenharmony_ci                if self.args.features is not None:
8555f9996aaSopenharmony_ci                    features.append(' --no-default-features')
8565f9996aaSopenharmony_ci                if self.args.features:
8575f9996aaSopenharmony_ci                    features += [' --features ', self.args.features]
8585f9996aaSopenharmony_ci            cargo_cmd += [cargo] + features + cargo_cmd_target_dir + cargo_cmd_redir
8595f9996aaSopenharmony_ci            if self.args.rustflags and cargo != 'clean':
8605f9996aaSopenharmony_ci                cargo_cmd = ['RUSTFLAGS="' + self.args.rustflags] + cargo_cmd
8615f9996aaSopenharmony_ci            self.run_cargo_cmd(cargo_cmd, cargo_out)
8625f9996aaSopenharmony_ci
8635f9996aaSopenharmony_ci    def run_cargo_cmd(self, cargo_cmd: str, cargo_out: str):
8645f9996aaSopenharmony_ci        if self.dry_run:
8655f9996aaSopenharmony_ci            print('Dry-run skip:', cargo_cmd)
8665f9996aaSopenharmony_ci        else:
8675f9996aaSopenharmony_ci            with open(cargo_out, 'a') as file:
8685f9996aaSopenharmony_ci                file.write('### Running: ' + ''.join(cargo_cmd) + '\n')
8695f9996aaSopenharmony_ci            ret = subprocess.run(cargo_cmd, shell=False)
8705f9996aaSopenharmony_ci            if ret.returncode != 0:
8715f9996aaSopenharmony_ci                print('ERROR: There was an error while running cargo.' +
8725f9996aaSopenharmony_ci                      ' See the cargo.out file for details.')
8735f9996aaSopenharmony_ci
8745f9996aaSopenharmony_ci    def generate_gn(self):
8755f9996aaSopenharmony_ci        """Parse cargo.out and generate BUILD.gn files."""
8765f9996aaSopenharmony_ci        cargo_out = 'cargo.out'  # The file name used to save cargo build -v output.
8775f9996aaSopenharmony_ci        errors_line = 'Errors in ' + cargo_out + ':'
8785f9996aaSopenharmony_ci        if self.dry_run:
8795f9996aaSopenharmony_ci            print('Dry-run skip: read', cargo_out, 'write BUILD.gn')
8805f9996aaSopenharmony_ci        elif os.path.exists(cargo_out):
8815f9996aaSopenharmony_ci            self.find_root_pkg()
8825f9996aaSopenharmony_ci            with open(cargo_out, 'r') as cargo_out:
8835f9996aaSopenharmony_ci                self.parse(cargo_out, 'BUILD.gn')
8845f9996aaSopenharmony_ci                self.crates.sort(key=get_crate_name)
8855f9996aaSopenharmony_ci                for crate in self.crates:
8865f9996aaSopenharmony_ci                    crate.dump()
8875f9996aaSopenharmony_ci                if self.error_infos:
8885f9996aaSopenharmony_ci                    self.append_to_gn('\n' + errors_line + '\n' + self.error_infos)
8895f9996aaSopenharmony_ci                if self.test_error_infos:
8905f9996aaSopenharmony_ci                    self.append_to_gn('\n// Errors when listing tests:\n' +
8915f9996aaSopenharmony_ci                                      self.test_error_infos)
8925f9996aaSopenharmony_ci        return self
8935f9996aaSopenharmony_ci
8945f9996aaSopenharmony_ci    def find_root_pkg(self):
8955f9996aaSopenharmony_ci        """Read name of [package] in ./Cargo.toml."""
8965f9996aaSopenharmony_ci        if os.path.exists('./Cargo.toml'):
8975f9996aaSopenharmony_ci            return
8985f9996aaSopenharmony_ci        with open('./Cargo.toml', 'r') as infile:
8995f9996aaSopenharmony_ci            get_designated_pkg_info(infile, 'name')
9005f9996aaSopenharmony_ci
9015f9996aaSopenharmony_ci    def parse(self, infile: str, outfile_name: str):
9025f9996aaSopenharmony_ci        """Parse rustc, test, and warning messages in infile, return a list of Crates."""
9035f9996aaSopenharmony_ci        # cargo test --list output of the start of running a binary.
9045f9996aaSopenharmony_ci        cargo_test_list_start_re = re.compile('^\s*Running (.*) \(.*\)$')
9055f9996aaSopenharmony_ci        # cargo test --list output of the end of running a binary.
9065f9996aaSopenharmony_ci        cargo_test_list_end_re = re.compile('^(\d+) tests, (\d+) benchmarks$')
9075f9996aaSopenharmony_ci        compiling_pat = re.compile('^ +Compiling (.*)$')
9085f9996aaSopenharmony_ci        current_test_name = None
9095f9996aaSopenharmony_ci        for line in infile:
9105f9996aaSopenharmony_ci            # We read the file in two passes, where the first simply checks for empty tests.
9115f9996aaSopenharmony_ci            # Otherwise we would add and merge tests before seeing they're empty.
9125f9996aaSopenharmony_ci            if cargo_test_list_start_re.match(line):
9135f9996aaSopenharmony_ci                current_test_name = cargo_test_list_start_re.match(line).group(1)
9145f9996aaSopenharmony_ci            elif current_test_name and cargo_test_list_end_re.match(line):
9155f9996aaSopenharmony_ci                match = cargo_test_list_end_re.match(line)
9165f9996aaSopenharmony_ci                if int(match.group(1)) + int(match.group(2)) == 0:
9175f9996aaSopenharmony_ci                    self.add_empty_test(current_test_name)
9185f9996aaSopenharmony_ci                current_test_name = None
9195f9996aaSopenharmony_ci            #Get Compiling information
9205f9996aaSopenharmony_ci            if compiling_pat.match(line):
9215f9996aaSopenharmony_ci                self.add_deps_libname_map(compiling_pat.match(line).group(1))
9225f9996aaSopenharmony_ci        infile.seek(0)
9235f9996aaSopenharmony_ci        self.parse_cargo_out(infile, outfile_name)
9245f9996aaSopenharmony_ci
9255f9996aaSopenharmony_ci    def add_empty_test(self, name: str):
9265f9996aaSopenharmony_ci        if name == 'unittests':
9275f9996aaSopenharmony_ci            self.empty_unittests = True
9285f9996aaSopenharmony_ci        else:
9295f9996aaSopenharmony_ci            self.empty_tests.add(name)
9305f9996aaSopenharmony_ci
9315f9996aaSopenharmony_ci    def add_deps_libname_map(self, line: str):
9325f9996aaSopenharmony_ci        line_list = line.split()
9335f9996aaSopenharmony_ci        if len(line_list) > 1:
9345f9996aaSopenharmony_ci            self.deps_libname_map[pkg_to_crate_name(line_list[0])] = line_list[0]
9355f9996aaSopenharmony_ci
9365f9996aaSopenharmony_ci    def parse_cargo_out(self, infile: str, outfile_name: str):
9375f9996aaSopenharmony_ci        # Cargo -v output of a call to rustc.
9385f9996aaSopenharmony_ci        rustc_re = re.compile('^ +Running `rustc (.*)`$')
9395f9996aaSopenharmony_ci        # Cargo -vv output of a call to rustc could be split into multiple lines.
9405f9996aaSopenharmony_ci        # Assume that the first line will contain some CARGO_* env definition.
9415f9996aaSopenharmony_ci        rustc_vv_re = re.compile('^ +Running `.*CARGO_.*=.*$')
9425f9996aaSopenharmony_ci        # Rustc output of file location path pattern for a warning message.
9435f9996aaSopenharmony_ci        warning_output_file_re = re.compile('^ *--> ([^:]*):[0-9]+')
9445f9996aaSopenharmony_ci        cargo_to_gn_running_re = re.compile('^### Running: .*$')
9455f9996aaSopenharmony_ci        line_num = 0
9465f9996aaSopenharmony_ci        previous_warning = False  # true if the previous line was warning
9475f9996aaSopenharmony_ci        rustc_line = ''           # previous line matching rustc_vv_re
9485f9996aaSopenharmony_ci        in_tests = False
9495f9996aaSopenharmony_ci        for line in infile:
9505f9996aaSopenharmony_ci            line_num += 1
9515f9996aaSopenharmony_ci            if line.startswith('warning: '):
9525f9996aaSopenharmony_ci                previous_warning = True
9535f9996aaSopenharmony_ci                rustc_line = self.assert_empty_rustc_line(rustc_line)
9545f9996aaSopenharmony_ci                continue
9555f9996aaSopenharmony_ci            new_rustc_line = ''
9565f9996aaSopenharmony_ci            if rustc_re.match(line):
9575f9996aaSopenharmony_ci                args_line = rustc_re.match(line).group(1)
9585f9996aaSopenharmony_ci                self.add_crate(Crate(self, outfile_name).parse_rustc(line_num, args_line))
9595f9996aaSopenharmony_ci                self.assert_empty_rustc_line(rustc_line)
9605f9996aaSopenharmony_ci            elif rustc_line or rustc_vv_re.match(line):
9615f9996aaSopenharmony_ci                new_rustc_line = self.deal_rustc_command(
9625f9996aaSopenharmony_ci                    line_num, rustc_line, line, outfile_name)
9635f9996aaSopenharmony_ci            elif previous_warning and warning_output_file_re.match(line):
9645f9996aaSopenharmony_ci                file_path = warning_output_file_re.match(line).group(1)
9655f9996aaSopenharmony_ci                if file_path[0] != '/':  # ignore absolute path
9665f9996aaSopenharmony_ci                    self.warning_files.add(file_path)
9675f9996aaSopenharmony_ci                self.assert_empty_rustc_line(rustc_line)
9685f9996aaSopenharmony_ci            elif line.startswith('error: ') or line.startswith('error[E'):
9695f9996aaSopenharmony_ci                if not self.args.ignore_cargo_errors:
9705f9996aaSopenharmony_ci                    self.add_error_infos(in_tests, line)
9715f9996aaSopenharmony_ci            elif cargo_to_gn_running_re.match(line):
9725f9996aaSopenharmony_ci                in_tests = "cargo test" in line and "--list" in line
9735f9996aaSopenharmony_ci            previous_warning = False
9745f9996aaSopenharmony_ci            rustc_line = new_rustc_line
9755f9996aaSopenharmony_ci
9765f9996aaSopenharmony_ci    def assert_empty_rustc_line(self, line: str):
9775f9996aaSopenharmony_ci        # report error if line is not empty
9785f9996aaSopenharmony_ci        if line:
9795f9996aaSopenharmony_ci            self.append_to_gn('ERROR -vv line: ' + line)
9805f9996aaSopenharmony_ci        return ''
9815f9996aaSopenharmony_ci
9825f9996aaSopenharmony_ci    def append_to_gn(self, line: str):
9835f9996aaSopenharmony_ci        self.init_gn_file('BUILD.gn')
9845f9996aaSopenharmony_ci        with open('BUILD.gn', 'a') as outfile:
9855f9996aaSopenharmony_ci            outfile.write(line)
9865f9996aaSopenharmony_ci        print(line)
9875f9996aaSopenharmony_ci
9885f9996aaSopenharmony_ci    def init_gn_file(self, name: str):
9895f9996aaSopenharmony_ci        # name could be BUILD.gn or sub_dir_path/BUILD.gn
9905f9996aaSopenharmony_ci        if name in self.gn_files:
9915f9996aaSopenharmony_ci            return
9925f9996aaSopenharmony_ci        self.gn_files.add(name)
9935f9996aaSopenharmony_ci        if os.path.exists(name):
9945f9996aaSopenharmony_ci            os.remove(name)
9955f9996aaSopenharmony_ci        with open(name, 'w') as outfile:
9965f9996aaSopenharmony_ci            outfile.write(BUILD_GN_HEADER)
9975f9996aaSopenharmony_ci            outfile.write('\n')
9985f9996aaSopenharmony_ci            outfile.write('import("%s")\n' % IMPORT_CONTENT)
9995f9996aaSopenharmony_ci
10005f9996aaSopenharmony_ci    def add_error_infos(self, in_tests: str, line: str):
10015f9996aaSopenharmony_ci        if in_tests:
10025f9996aaSopenharmony_ci            self.test_error_infos += '// '.join(line)
10035f9996aaSopenharmony_ci        else:
10045f9996aaSopenharmony_ci            self.error_infos += line
10055f9996aaSopenharmony_ci
10065f9996aaSopenharmony_ci    def deal_rustc_command(self, line_num: str, rustc_line: str, line: str, outfile_name: str):
10075f9996aaSopenharmony_ci        """Process a rustc command line from cargo -vv output."""
10085f9996aaSopenharmony_ci        # cargo build -vv output can have multiple lines for a rustc command due to '\n' in strings
10095f9996aaSopenharmony_ci        # for environment variables. strip removes leading spaces and '\n' at the end
10105f9996aaSopenharmony_ci        new_rustc_line = (rustc_line.strip() + line) if rustc_line else line
10115f9996aaSopenharmony_ci        # The combined -vv output rustc command line pattern.
10125f9996aaSopenharmony_ci        rustc_vv_cmd_args = re.compile('^ *Running `.*CARGO_.*=.* rustc (.*)`$')
10135f9996aaSopenharmony_ci        if not line.endswith('`\n') or (new_rustc_line.count('`') % 2) != 0:
10145f9996aaSopenharmony_ci            return new_rustc_line
10155f9996aaSopenharmony_ci        if rustc_vv_cmd_args.match(new_rustc_line):
10165f9996aaSopenharmony_ci            args = rustc_vv_cmd_args.match(new_rustc_line).group(1)
10175f9996aaSopenharmony_ci            self.add_crate(Crate(self, outfile_name).parse_rustc(line_num, args))
10185f9996aaSopenharmony_ci        else:
10195f9996aaSopenharmony_ci            self.assert_empty_rustc_line(new_rustc_line)
10205f9996aaSopenharmony_ci        return ''
10215f9996aaSopenharmony_ci
10225f9996aaSopenharmony_ci    def add_crate(self, new_crate):
10235f9996aaSopenharmony_ci        """Merge crate with someone in crates, or append to it. Return crates."""
10245f9996aaSopenharmony_ci        if (is_build_script(new_crate.crate_name) and
10255f9996aaSopenharmony_ci            not is_dependent_path(new_crate.main_src) and
10265f9996aaSopenharmony_ci            new_crate.root_pkg_name and len(new_crate.deps) > 0):
10275f9996aaSopenharmony_ci            self.build_deps[new_crate.root_pkg_name] = new_crate.deps
10285f9996aaSopenharmony_ci        if new_crate.skip_crate():
10295f9996aaSopenharmony_ci            # include debug info of all crates
10305f9996aaSopenharmony_ci            if self.args.debug:
10315f9996aaSopenharmony_ci                self.crates.append(new_crate)
10325f9996aaSopenharmony_ci        else:
10335f9996aaSopenharmony_ci            for crate in self.crates:
10345f9996aaSopenharmony_ci                if crate.merge_crate(new_crate, 'BUILD.gn'):
10355f9996aaSopenharmony_ci                    return
10365f9996aaSopenharmony_ci            # If not merged, decide module type and name now.
10375f9996aaSopenharmony_ci            new_crate.decide_module_type()
10385f9996aaSopenharmony_ci            self.crates.append(new_crate)
10395f9996aaSopenharmony_ci
10405f9996aaSopenharmony_ci    def should_ignore_test(self, src):
10415f9996aaSopenharmony_ci        # cargo test outputs the source file for integration tests but "unittests" for unit tests.
10425f9996aaSopenharmony_ci        # To figure out to which crate this corresponds, we check if the current source file is
10435f9996aaSopenharmony_ci        # the main source of a non-test crate, e.g., a library or a binary.
10445f9996aaSopenharmony_ci        return (src in self.empty_tests or src in self.args.test_blocklist or
10455f9996aaSopenharmony_ci                (self.empty_unittests and
10465f9996aaSopenharmony_ci                 src in [c.main_src for c in self.crates if c.crate_types != ['test']]))
10475f9996aaSopenharmony_ci
10485f9996aaSopenharmony_ci
10495f9996aaSopenharmony_cidef get_arg_parser():
10505f9996aaSopenharmony_ci    """Parse main arguments."""
10515f9996aaSopenharmony_ci    argparser = argparse.ArgumentParser('cargo2gn')
10525f9996aaSopenharmony_ci    argparser.add_argument('--add-workspace', action='store_true', default=False,
10535f9996aaSopenharmony_ci        help=('append [workspace] to Cargo.toml before calling cargo, to treat' +
10545f9996aaSopenharmony_ci              ' current directory as root of package source; otherwise the relative' +
10555f9996aaSopenharmony_ci              ' source file path in generated .gn file will be from the parent directory.'))
10565f9996aaSopenharmony_ci    argparser.add_argument('--cargo', action='append', metavar='args_string',
10575f9996aaSopenharmony_ci        help=('extra cargo build -v args in a string, ' +
10585f9996aaSopenharmony_ci              'each --cargo flag calls cargo build -v once'))
10595f9996aaSopenharmony_ci    argparser.add_argument('--cargo-bin', type=str,
10605f9996aaSopenharmony_ci        help='use cargo in the cargo_bin directory instead of the prebuilt one')
10615f9996aaSopenharmony_ci    argparser.add_argument('--config', type=str,
10625f9996aaSopenharmony_ci        help=('Load command-line options from the given config file. ' +
10635f9996aaSopenharmony_ci              'Options in this file will override those passed on the command line.'))
10645f9996aaSopenharmony_ci    argparser.add_argument('--copy-out', action='store_true', default=False,
10655f9996aaSopenharmony_ci        help=('only for root directory, copy build.rs output to ./out/* and ' +
10665f9996aaSopenharmony_ci              'add a genrule to copy ./out/*.'))
10675f9996aaSopenharmony_ci    argparser.add_argument('--debug', action='store_true', default=False,
10685f9996aaSopenharmony_ci        help='dump debug info into BUILD.gn')
10695f9996aaSopenharmony_ci    argparser.add_argument('--dependency-blocklist', nargs='*', default=[],
10705f9996aaSopenharmony_ci        help='Do not emit the given dependencies (without lib prefixes).')
10715f9996aaSopenharmony_ci    argparser.add_argument('--features', type=str,
10725f9996aaSopenharmony_ci        help=('pass features to cargo build, ' +
10735f9996aaSopenharmony_ci              'empty string means no default features'))
10745f9996aaSopenharmony_ci    argparser.add_argument('--ignore-cargo-errors', action='store_true', default=False,
10755f9996aaSopenharmony_ci        help='do not append cargo/rustc error messages to BUILD.gn')
10765f9996aaSopenharmony_ci    argparser.add_argument('--no-pkg-info', action='store_true', default=False,
10775f9996aaSopenharmony_ci        help='Do not attempt to determine the package info automatically.')
10785f9996aaSopenharmony_ci    argparser.add_argument('--no-subdir', action='store_true', default=False,
10795f9996aaSopenharmony_ci        help='do not output anything for sub-directories')
10805f9996aaSopenharmony_ci    argparser.add_argument('--one-file', action='store_true', default=False,
10815f9996aaSopenharmony_ci        help=('output all into one BUILD.gn, default will generate one BUILD.gn ' +
10825f9996aaSopenharmony_ci              'per Cargo.toml in subdirectories'))
10835f9996aaSopenharmony_ci    argparser.add_argument('--run', action='store_true', default=False,
10845f9996aaSopenharmony_ci        help='run it, default is dry-run')
10855f9996aaSopenharmony_ci    argparser.add_argument('--rustflags', type=str, help='passing flags to rustc')
10865f9996aaSopenharmony_ci    argparser.add_argument('--skipcargo', action='store_true', default=False,
10875f9996aaSopenharmony_ci        help='skip cargo command, parse cargo.out, and generate BUILD.gn')
10885f9996aaSopenharmony_ci    argparser.add_argument('--test-blocklist', nargs='*', default=[],
10895f9996aaSopenharmony_ci        help=('Do not emit the given tests. ' +
10905f9996aaSopenharmony_ci              'Pass the path to the test file to exclude.'))
10915f9996aaSopenharmony_ci    argparser.add_argument('--use-cargo-lock', action='store_true', default=False,
10925f9996aaSopenharmony_ci        help=('run cargo build with existing Cargo.lock ' +
10935f9996aaSopenharmony_ci              '(used when some latest dependent crates failed)'))
10945f9996aaSopenharmony_ci    argparser.add_argument('--vv', action='store_true', default=False,
10955f9996aaSopenharmony_ci        help='run cargo with -vv instead of default -v')
10965f9996aaSopenharmony_ci    return argparser
10975f9996aaSopenharmony_ci
10985f9996aaSopenharmony_ci
10995f9996aaSopenharmony_cidef get_parse_args(argparser):
11005f9996aaSopenharmony_ci    """Parses command-line options."""
11015f9996aaSopenharmony_ci    args = argparser.parse_args()
11025f9996aaSopenharmony_ci    # Use the values specified in a config file if one was found.
11035f9996aaSopenharmony_ci    if args.config:
11045f9996aaSopenharmony_ci        with open(args.config, 'r') as file:
11055f9996aaSopenharmony_ci            config_data = json.load(file)
11065f9996aaSopenharmony_ci            args_dict = vars(args)
11075f9996aaSopenharmony_ci            for arg in config_data:
11085f9996aaSopenharmony_ci                args_dict[arg.replace('-', '_')] = config_data[arg]
11095f9996aaSopenharmony_ci    return args
11105f9996aaSopenharmony_ci
11115f9996aaSopenharmony_ci
11125f9996aaSopenharmony_cidef main():
11135f9996aaSopenharmony_ci    argparser = get_arg_parser()
11145f9996aaSopenharmony_ci    args = get_parse_args(argparser)
11155f9996aaSopenharmony_ci    if not args.run:  # default is dry-run
11165f9996aaSopenharmony_ci        print(DRY_RUN_CONTENT)
11175f9996aaSopenharmony_ci    Runner(args).run_cargo().generate_gn()
11185f9996aaSopenharmony_ci
11195f9996aaSopenharmony_ci
11205f9996aaSopenharmony_ciif __name__ == '__main__':
11215f9996aaSopenharmony_ci    main()
1122