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