From 055d0b451a1704cd663a6bab9c2cbd98e4ee3791 Mon Sep 17 00:00:00 2001 From: lixinyu Date: Mon, 6 Nov 2023 15:46:31 +0800 Subject: [PATCH] runqemu: add runqemu function * add new function calld runqemu for running system in qemu Signed-off-by: lixinyu --- README.md | 35 +- src/oebuild/app/conf/plugins.yaml | 6 +- src/oebuild/app/plugins/bitbake/base_build.py | 6 +- src/oebuild/app/plugins/bitbake/const.py | 2 - .../app/plugins/bitbake/in_container.py | 3 +- src/oebuild/app/plugins/deploy/deploy.py | 119 ---- .../app/plugins/deploy/deploy_in_container.py | 412 -------------- src/oebuild/app/plugins/qemu_run/qemu_run.py | 131 ----- src/oebuild/app/plugins/run_qemu/run_qemu.py | 518 ++++++++++++++++++ src/oebuild/configure.py | 8 + src/oebuild/util.py | 34 ++ 11 files changed, 578 insertions(+), 696 deletions(-) delete mode 100644 src/oebuild/app/plugins/deploy/deploy.py delete mode 100644 src/oebuild/app/plugins/deploy/deploy_in_container.py delete mode 100644 src/oebuild/app/plugins/qemu_run/qemu_run.py create mode 100644 src/oebuild/app/plugins/run_qemu/run_qemu.py diff --git a/README.md b/README.md index 244d518..05dfb7e 100644 --- a/README.md +++ b/README.md @@ -286,42 +286,27 @@ oebuild bitbake -h 由于oebuild二进制包存放在gitee仓库中,因此oebuild在升级时会先克隆最新的二进制仓到用户根目录,并以一个随机文件名命名,然后执行`sudo pip install `来完成升级,在这之中会要求用户输入root密码,在完成升级后会自动删除oebuild二进制包 -##### oebuild compile +##### oebuild runqemu -对指定C或者C++文件,进行交叉编译的指令。执行后,静态编译过的二进制文件将被复制到用户当前目录下。 +qemu 仿真命令,通过该命令可以在本地实现qemu仿真,该功能运行依赖qemuboot.conf,如果用户不指定或创建,oebuild会根据相关参数自动创建,该文件中记录有qemu启动相关参数,用户可以手动进行修改,如果在当下目录中有多个qemuboot.conf,则会给出选择交互由用户进行选择 ``` -oebuild compile [-d source_directory] [-p platform] +oebuild runqemu [-f conf] [-a arch] [-c create] [-k kernel] [-r rootfs] [-s smp] [-m mem] ``` -source_directory: 表示目标源代码文件目录。 +conf: qemuboot.conf配置文件,qemu仿真依赖该文件进行启动,该文件中记录有qemu启动所有必要的参数,注意,该参数一旦使用则其他参数则不再生效 -platform:表示交叉编译的目标架构。可选项:arm, aarch64, riscv64, x86_64。此值默认为aarch64。 +arch: 运行架构,目前可选的架构有aarch64,arm,x86_64和riscv64,可以缺省启动,此时会进入交互界面供用户选择 -##### oebuild qemu_run +create: 创建新qemuboot.conf文件名,应用该参数则会自动创建新的qemuboot.conf -运行二进制文件的指令。在容器内一键执行交叉编译过的二进制文件,并返回执行结果。使用qemu user模式实现。 +kernel:内核文件,该参数用于指定启动内核文件路径,对应qemu的`-kernel xxx` -``` -oebuild compile [-d target_directory] [-p platform] -``` - -target_directory: 表示目标二进制文件目录。 - -platform:表示可执行文件运行目标平台架构。可选项:arm, aarch64, riscv64, x86_64。此值默认为aarch64。 - -##### oebuild deploy - -将openEuler镜像部署到指定平台的指令。该指令将编译或者下载的镜像文件,部署到指定平台。命令自动配置qemu的网络功能,实现传送文件的可能。目前仅支持qemu-system平台。 -一键执行后,命令行将直接跳转到qemu用户登录界面。 - -``` -oebuild deploy [-d target_directory] [-p platform] [-m mode] -``` +rootfs:系统文件,该参数用于指定系统文件路径,对应qemu的`-initrd xxx` -target_directory: 表示目标镜像文件夹的目录。程序会将整个文件夹复制到容器内,再在容器端进行部署。 +smp:内核数,该参数用于指定系统启动的内核数,默认为1,对应qemu的`-smp 1 ` -platform:表示部署目标平台的架构。可选项:arm, aarch64, riscv64, x86_64。此值默认为aarch64。 +mem:内存,该参数用于指定系统启动的内存,默认为1G,对应qemu的`-m 1G` #### 配置文件介绍 diff --git a/src/oebuild/app/conf/plugins.yaml b/src/oebuild/app/conf/plugins.yaml index e36edbf..64c0311 100644 --- a/src/oebuild/app/conf/plugins.yaml +++ b/src/oebuild/app/conf/plugins.yaml @@ -17,6 +17,6 @@ plugins: - name: clear class: Clear path: plugins/clear/clear.py -- name: deploy - class: Deploy - path: plugins/deploy/deploy.py +- name: runqemu + class: RunQemu + path: plugins/run_qemu/run_qemu.py diff --git a/src/oebuild/app/plugins/bitbake/base_build.py b/src/oebuild/app/plugins/bitbake/base_build.py index 0331cdd..efc2b6b 100644 --- a/src/oebuild/app/plugins/bitbake/base_build.py +++ b/src/oebuild/app/plugins/bitbake/base_build.py @@ -54,7 +54,7 @@ class BaseBuild: new_content = '' for line in old_content.split('\n'): line: str = line - if line.endswith(bitbake_const.BASH_END_FLAG) or line.replace(" ", '') == '': + if line.endswith(oebuild_util.BASH_END_FLAG) or line.replace(" ", '') == '': continue new_content = new_content + line + '\n' return new_content @@ -62,7 +62,7 @@ class BaseBuild: def _add_bashrc(self, content: str, line: str): if not content.endswith('\n'): content = content + '\n' - content = content + line + bitbake_const.BASH_END_FLAG + '\n' + content = content + line + oebuild_util.BASH_END_FLAG + '\n' return content @@ -70,6 +70,6 @@ class BaseBuild: new_content = self._restore_bashrc_content(old_content=old_content) for command in init_command: - new_content = new_content + command + bitbake_const.BASH_END_FLAG + '\n' + new_content = new_content + command + oebuild_util.BASH_END_FLAG + '\n' return new_content diff --git a/src/oebuild/app/plugins/bitbake/const.py b/src/oebuild/app/plugins/bitbake/const.py index 4da16d0..d6d8082 100644 --- a/src/oebuild/app/plugins/bitbake/const.py +++ b/src/oebuild/app/plugins/bitbake/const.py @@ -9,8 +9,6 @@ EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. See the Mulan PSL v2 for more details. ''' - -BASH_END_FLAG = " ###!!!###" CONTAINER_SRC = '/usr1/openeuler/src' CONTAINER_BUILD = '/home/openeuler' CONTAINER_USER = "openeuler" diff --git a/src/oebuild/app/plugins/bitbake/in_container.py b/src/oebuild/app/plugins/bitbake/in_container.py index 12b008c..c1cf45d 100644 --- a/src/oebuild/app/plugins/bitbake/in_container.py +++ b/src/oebuild/app/plugins/bitbake/in_container.py @@ -22,6 +22,7 @@ from oebuild.parse_compile import ParseCompile from oebuild.m_log import logger import oebuild.app.plugins.bitbake.const as bitbake_const from oebuild.app.plugins.bitbake.base_build import BaseBuild +import oebuild.util as oebuild_util class InContainer(BaseBuild): ''' @@ -157,7 +158,7 @@ class InContainer(BaseBuild): else: content = self._get_bashrc_content(container=container) for b_s in bitbake_const.BASH_BANNER.split('\n'): - b_s = f"echo {b_s}{bitbake_const.BASH_END_FLAG}" + b_s = f"echo {b_s}{oebuild_util.BASH_END_FLAG}" content = self._add_bashrc(content=content, line=b_s) self.update_bashrc(container=container, content=content) os.system( diff --git a/src/oebuild/app/plugins/deploy/deploy.py b/src/oebuild/app/plugins/deploy/deploy.py deleted file mode 100644 index f964d4f..0000000 --- a/src/oebuild/app/plugins/deploy/deploy.py +++ /dev/null @@ -1,119 +0,0 @@ -''' -Copyright (c) 2023 openEuler Embedded -oebuild is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. -''' - -import argparse -import textwrap -import os -from shutil import copyfile - -from docker.models.containers import Container -from oebuild.docker_proxy import DockerProxy -import oebuild.util as oebuild_util -from docker.errors import DockerException - -from oebuild.command import OebuildCommand -from oebuild.m_log import logger, INFO_COLOR -from oebuild.parse_compile import ParseCompile,CheckCompileError -from oebuild.configure import Configure, ConfigBasicRepo, YOCTO_META_OPENEULER -from oebuild.parse_template import BaseParseTemplate, ParseTemplate, BUILD_IN_DOCKER, BUILD_IN_HOST -from oebuild.m_log import logger, INFO_COLOR -from oebuild.check_docker_tag import CheckDockerTag -from oebuild.app.plugins.deploy.deploy_in_container import InContainer -from oebuild.parse_env import ParseEnv - -''' -The command for deploy specific platform, like qemu or other boards. -''' - -class Deploy(OebuildCommand): - - def __init__(self): - self.compile_conf_dir = os.path.join(os.getcwd(), 'compile.yaml') - self.configure = Configure() - self.client = DockerProxy() - self.container_id = None - - super().__init__( - 'deploy', - 'Deploy for the platform', - textwrap.dedent(''' - This command will deploy on target platform. - ''') - ) - - def do_add_parser(self, parser_adder): - parser = self._parser( - parser_adder, - usage=''' - - %(prog)s [-p platform] -''') - - parser.add_argument('-p', dest='platform', default="aarch64", - help=''' - this param is for arch. All possible choices: arm, aarch64, riscv64, x86_64 - ''' - ) - - return parser - - def do_run(self, args: argparse.Namespace, unknown=None): - - parse_compile = ParseCompile(self.compile_conf_dir) - # perpare parse help command - if self.pre_parse_help(args, unknown): - return - args = args.parse_args(unknown) - - try: - oebuild_util.check_docker() - except DockerException as d_e: - logger.error(str(d_e)) - return - logger.info('Deploying QEMU......') - _runqemu_command_backup = ( - "chmod a+x /etc/qemu-ifup" - ) - parse_env = ParseEnv(env_dir='.env') - in_container = InContainer(self.configure) - - timestamp_folder = self.get_timestamp_folder_name() - if timestamp_folder: - in_container.timestamp = timestamp_folder - else: - logger.error("No valid timestamp folder found in the output directory.") - in_container.arch = args.platform - # "chmod a+x /etc/qemu-ifup && " - _runqemu_command=( - - f"runqemu ./generated_conf.qemuboot.conf output/{in_container.timestamp}/openeuler-image-*-{in_container.arch}-{in_container.timestamp}.rootfs.cpio.gz qemuparams='-M virt-4.0 -cpu cortex-a57 ' nographic" - ) - - in_container.exec(parse_env=parse_env, - parse_compile=parse_compile, - command=_runqemu_command) - - logger.info("Deploy finished ...") - - def get_timestamp_folder_name(self): - all_subdirectories = [d for d in os.listdir("./output/") if os.path.isdir(os.path.join("./output/", d))] - - if len(all_subdirectories) == 1 and all_subdirectories[0].isdigit(): - # logger.info(all_subdirectories[0]) - return all_subdirectories[0] - else: - return None - - - - - diff --git a/src/oebuild/app/plugins/deploy/deploy_in_container.py b/src/oebuild/app/plugins/deploy/deploy_in_container.py deleted file mode 100644 index 13a61be..0000000 --- a/src/oebuild/app/plugins/deploy/deploy_in_container.py +++ /dev/null @@ -1,412 +0,0 @@ -''' -Copyright (c) 2023 openEuler Embedded -oebuild is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. -''' -import re -import os -import subprocess - -from docker.models.containers import Container - -from oebuild.local_conf import NATIVE_GCC_DIR, SSTATE_CACHE -from oebuild.parse_env import ParseEnv, EnvContainer -from oebuild.docker_proxy import DockerProxy -from oebuild.configure import Configure -from oebuild.parse_compile import ParseCompile -from oebuild.m_log import logger -import oebuild.app.plugins.bitbake.const as bitbake_const -from oebuild.app.plugins.bitbake.base_build import BaseBuild - -class InContainer(BaseBuild): - ''' - bitbake command execute in container - ''' - - def __init__(self, configure: Configure): - self.configure = configure - self.client = DockerProxy() - self.container_id = None - self.arch = None - self.timestamp = None - - - def __del__(self): - if self.container_id is None: - return - # try: - # container = self.client.get_container(self.container_id) - # self.client.stop_container(container=container) - # except Exception as e_p: - # raise e_p - - def exec(self, parse_env: ParseEnv, parse_compile: ParseCompile, command): - ''' - execute bitbake command - ''' - logger.info("bitbake starting ...") - # check docker image if exists - docker_proxy = DockerProxy() - if not docker_proxy.is_image_exists(parse_compile.docker_image): - logger.error(f'''the docker image does not exists, please run fellow command: - `oebuild update docker`''') - return - - self.deal_env_container( - env=parse_env, - toolchain_dir=parse_compile.toolchain_dir, - sstate_cache=parse_compile.sstate_cache, - docker_image=parse_compile.docker_image) - - self.exec_compile(parse_compile=parse_compile, command=command) - - def deal_env_container(self, - env: ParseEnv, - toolchain_dir=None, - sstate_cache = None, - docker_image = ""): - ''' - This operation realizes the processing of the container, - controls how the container is processed by parsing the env - variable, if the container does not exist, or the original - environment and the current environment that needs to be set - are inconsistent, you need to create a new container, otherwise - directly enable the sleeping container - ''' - cwd_name = os.path.basename(os.getcwd()) - volumns = [] - volumns.append(self.configure.source_dir() + ':' + bitbake_const.CONTAINER_SRC) - volumns.append(os.path.join(self.configure.build_dir(), cwd_name) - + ':' + - os.path.join(bitbake_const.CONTAINER_BUILD, cwd_name)) - if toolchain_dir is not None: - volumns.append(toolchain_dir + ":" + NATIVE_GCC_DIR) - - if sstate_cache is not None: - volumns.append(sstate_cache + ":" + SSTATE_CACHE) - - try: - env_container = EnvContainer( - - volumns=volumns, - short_id="" - ) - check_container = env.is_same_container(data=env_container) - except Exception as e_p: - raise e_p - - if not check_container \ - or env.container.short_id is None \ - or not self.client.is_container_exists(env.container.short_id): - # judge which container - config = self.configure.parse_oebuild_config() - container_config = config.docker - container:Container = self.client.container_run_simple( - image=docker_image, - volumes=volumns) # type: ignore - - env_container.short_id = container.short_id - env.set_env_container(env_container) - env.export_env() - - self.container_id = env.container.short_id - container:Container = self.client.get_container(self.container_id) # type: ignore - if not self.client.is_container_running(container): - self.client.start_container(container) - - def exec_compile(self, parse_compile: ParseCompile, command: str = ""): - ''' - execute compile task - ''' - container:Container = self.client.get_container(self.container_id) # type: ignore - - self.init_bash(container=container, - build_dir=os.path.basename(os.getcwd())) - - try: - self.init_bitbake(container=container) - except ValueError as v_e: - logger.error(str(v_e)) - return - - # add bblayers, this action must before replace local_conf - bblayers_dir = os.path.join(os.getcwd(), "conf", "bblayers.conf") - self.add_bblayers( - bblayers_dir=bblayers_dir, - pre_dir=bitbake_const.CONTAINER_SRC, - base_dir=self.configure.source_dir(), - layers=parse_compile.layers) - - # replace local_conf - local_dir = os.path.join(os.getcwd(), 'conf', 'local.conf') - self.replace_local_conf( - parse_compile=parse_compile, local_dir=local_dir) - - self._copy_qemuboot_file(container,self.arch, self.timestamp) - # self._copy_script_file(container) - _work_space = f"/home/{bitbake_const.CONTAINER_USER}" - - content = self._get_bashrc_content(container=container) - for b_s in bitbake_const.BASH_BANNER.split('\n'): - b_s = f"echo {b_s}{bitbake_const.BASH_END_FLAG}" - content = self._add_bashrc(content=content, line=b_s) - - content = self._add_bashrc(content=content, line=command) - self.update_bashrc(container=container, content=content) - - docker_cmd = f'docker exec --privileged -it -u {bitbake_const.CONTAINER_USER} {container.short_id} bash' - os_tty_result = os.system(docker_cmd) - - print(f"docker_cmd returned: {docker_cmd}") - print(f"os.system() returned: {os_tty_result}") - - try: - os.remove("generated_conf.qemuboot.conf") - print(f"Conf file deleted") - except FileNotFoundError: - print(f"Conf file does not exist") - - self.restore_bashrc(container=container) - - def init_bitbake(self, container: Container): - ''' - init_bitbake will start a container with pty and then check - bblayers.conf and local.conf if exists in 10 seconds, otherwise - raise init bitbake faild - ''' - self._check_change_ugid(container=container) - self._install_sudo(container=container) - - res = self.client.container_exec_command( - container=container, - command=f"bash /home/{bitbake_const.CONTAINER_USER}/.bashrc", - user=bitbake_const.CONTAINER_USER, - work_space=f"/home/{bitbake_const.CONTAINER_USER}", - stream=False) - if res.exit_code != 0: - raise ValueError(res.output.decode()) - # raise ValueError("bitbake init faild") - - def _check_change_ugid(self, container: Container): - res = self.client.container_exec_command( - container=container, - user='root', - command=f"id {bitbake_const.CONTAINER_USER}", - work_space=f"/home/{bitbake_const.CONTAINER_USER}", - stream=False) - if res.exit_code != 0: - raise ValueError("check docker user id faild") - - res_cont = res.output.decode() - - cuids = res_cont.split(' ') - # get uid from container in default user - pattern = re.compile(r'(?<=uid=)\d{1,}(?=\(' + bitbake_const.CONTAINER_USER + r'\))') - match_uid = pattern.search(cuids[0]) - if match_uid: - cuid = match_uid.group() - else: - raise ValueError(f"can not get container {bitbake_const.CONTAINER_USER} uid") - # get gid from container in default user - pattern = re.compile(r'(?<=gid=)\d{1,}(?=\(' + bitbake_const.CONTAINER_USER + r'\))') - match_gid = pattern.search(cuids[1]) - if match_gid: - cgid = match_gid.group() - else: - raise ValueError(f"can not get container {bitbake_const.CONTAINER_USER} gid") - - # judge host uid and gid are same with container uid and gid - # if not same and change container uid and gid equal to host's uid and gid - if os.getuid() != cuid: - self._change_container_uid(container=container, uid=os.getuid()) - if os.getgid() != cgid: - self._change_container_gid(container=container, gid=os.getgid()) - - def _change_container_uid(self, container: Container, uid: int): - self.client.container_exec_command( - container=container, - user='root', - command=f"usermod -u {uid} {bitbake_const.CONTAINER_USER}", - work_space=f"/home/{bitbake_const.CONTAINER_USER}", - stream=False) - - def _change_container_gid(self, container: Container, gid: int): - self.client.container_exec_command( - container=container, - user='root', - command=f"groupmod -g {gid} {bitbake_const.CONTAINER_USER}", - work_space=f"/home/{bitbake_const.CONTAINER_USER}", - stream=False) - - def _install_sudo(self, container: Container): - self._replace_yum_mirror(container=container) - - resp = self.client.container_exec_command( - container=container, - user='root', - command="which sudo", - work_space=f"/home/{bitbake_const.CONTAINER_USER}", - stream=False - ) - if resp.exit_code != 0: - logger.info( - "=========================install sudo===============================") - self._install_software(container=container, software="sudo") - - def _replace_yum_mirror(self, container: Container): - self.client.container_exec_command( - container=container, - user='root', - command=r"sed -i 's/repo.openeuler.org/mirrors.huaweicloud.com\/openeuler/g' /etc/yum.repos.d/openEuler.repo", - work_space=f"/home/{bitbake_const.CONTAINER_USER}", - stream=False - ) - - def _install_software(self, container: Container, software: str): - resp = self.client.container_exec_command( - container=container, - user='root', - command=f"yum install {software} -y", - work_space=f"/home/{bitbake_const.CONTAINER_USER}", - stream=True - ) - for line in resp.output: - logger.info(line.decode().strip('\n')) - - def init_bash(self, container: Container, build_dir): - ''' - Bitbake will initialize the compilation environment by reading - the user initialization script first, then making directional - substitutions, and finally writing the initialization script - ''' - # read container default user .bashrc content - content = self._get_bashrc_content(container=container) - - init_sdk_command = '. /opt/buildtools/nativesdk/environment-setup-x86_64-pokysdk-linux' - set_template = f'export TEMPLATECONF="{bitbake_const.CONTAINER_SRC}/yocto-meta-openeuler/.oebuild"' - init_oe_comand = f'. {bitbake_const.CONTAINER_SRC}/yocto-poky/oe-init-build-env \ - {bitbake_const.CONTAINER_BUILD}/{build_dir}' - init_command = [init_sdk_command, set_template, init_oe_comand] - new_content = self._init_bashrc_content(content, init_command) - - self.update_bashrc(container=container, content=new_content) - - def update_bashrc(self, container: Container, content: str): - ''' - update user initialization script by replace file, first create - a file and writed content and copy it to container's .bashrc, finally - remove it - ''' - tmp_file = self._set_tmpfile_content(content) - self.client.copy_to_container( - container=container, - source_path=tmp_file, - to_path=f'/home/{bitbake_const.CONTAINER_USER}') - container.exec_run( - cmd=f"mv /home/{bitbake_const.CONTAINER_USER}/{tmp_file} /home/{bitbake_const.CONTAINER_USER}/.bashrc", - user="root" - ) - os.remove(tmp_file) - - def restore_bashrc(self, container: Container): - ''' - Restoring .bashrc will strip out the command line - content added during bitbake initialization - ''' - old_content = self._get_bashrc_content(container=container) - self.update_bashrc(container=container, - content=self._restore_bashrc_content(old_content=old_content)) - - def _get_bashrc_content(self, container: Container): - content = self.client.container_exec_command( - container=container, - command=f"cat /home/{bitbake_const.CONTAINER_USER}/.bashrc", - user="root", - work_space=None, - stream=False).output - - return content.decode() - - def _copy_qemuboot_file(self, container, arch, time_stamp): - - current_directory = os.getcwd() - last_folder_name = os.path.basename(current_directory) - folder_path = "./tmp/deploy/images/" - subfolders = [f for f in os.listdir(folder_path) if os.path.isdir(os.path.join(folder_path, f))] - unique_subfolder = "" - # unique_subfolder - if len(subfolders) == 1: - unique_subfolder = subfolders[0] - - else: - print("No unique subfolders") - - - script_content = f""" - [config_bsp] - - machine=qemuarm64 - initrd=./output/{time_stamp}/openeuler-image-*-{arch}-*.rootfs.cpio.gz - kernel=./output/{time_stamp}/zImage-5.10.0 - SERIAL_CONSOLES = "115200;ttyS0 115200;ttyS1" - QB_NET=none - QB_MEM=1024 - QB_DEFAULT_FSTYPE=cpio.gz - # QB_SLIRP_OPT="-netdev user,id=net0,hostfwd=tcp::8080-:80,hostfwd=tcp::2222-:22" - # QB_NETWORK_DEVICE="-device virtio-net-device,netdev=tap0" - STAGING_DIR_NATIVE=/opt/buildtools/nativesdk/sysroots/ - STAGING_BINDIR_NATIVE=/opt/buildtools/nativesdk/sysroots/x86_64-pokysdk-linux/usr/bin - """ - - with open("generated_conf.qemuboot.conf", "w") as file: - file.write(script_content) - - # directory = './tmp/deploy/images/' - # subdirs = [d for d in os.listdir(directory) if os.path.isdir(os.path.join(directory, d))] - - # if len(subdirs) == 1: - # full_path = os.path.join(directory, subdirs[0]) - # print(full_path) - # else: - # print("Wrong directory for qemuboot file.") - - - logger.info("Copying qemuboot file into the docker container...") - - # to_path=f"/home/{bitbake_const.CONTAINER_USER}/{last_folder_name}/tmp/deploy/images/{unique_subfolder}/{time_stamp}/" - to_path = f"/home/{bitbake_const.CONTAINER_USER}/{last_folder_name}/" - logger.info(f"To path: {to_path}") - self.client.copy_to_container( - container=container, - source_path="./generated_conf.qemuboot.conf", - to_path=to_path) #aarch64-std {bitbake_const.CONTAINER_USER} - # to_path=f"/home/{bitbake_const.CONTAINER_USER}/{last_folder_name}/") #aarch64-std {bitbake_const.CONTAINER_USER} - - - - # /home/openeuler/build/aarch64-std/tmp/deploy/images/qemu-aarch64/ - - -# def _copy_script_file(self,container): -# ''' -# Copy script file for enabling network of qemu -# ''' -# script_content = """#!/bin/bash -# ifconfig $1 192.168.10.1 up -# """ -# # chmod a+x /etc/qemu-ifup -# with open("qemu-ifup", "w") as file: -# file.write(script_content) -# os.chmod("qemu-ifup", 0o755) -# logger.info("Copying script file into the docker container...") -# self.client.copy_to_container( -# container=container, -# source_path="./qemu-ifup", -# to_path="/etc/") -# os.remove("qemu-ifup") \ No newline at end of file diff --git a/src/oebuild/app/plugins/qemu_run/qemu_run.py b/src/oebuild/app/plugins/qemu_run/qemu_run.py deleted file mode 100644 index 8afff5c..0000000 --- a/src/oebuild/app/plugins/qemu_run/qemu_run.py +++ /dev/null @@ -1,131 +0,0 @@ -''' -Copyright (c) 2023 openEuler Embedded -oebuild is licensed under Mulan PSL v2. -You can use this software according to the terms and conditions of the Mulan PSL v2. -You may obtain a copy of Mulan PSL v2 at: - http://license.coscl.org.cn/MulanPSL2 -THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, -EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, -MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. -See the Mulan PSL v2 for more details. -''' - -import argparse -import textwrap -import os - -from docker.errors import DockerException - -from docker.models.containers import Container -from oebuild.docker_proxy import DockerProxy - -from oebuild.command import OebuildCommand -from oebuild.configure import Configure -from oebuild.m_log import logger - -class Qemu_run(OebuildCommand): - ''' - The command for run executable file under qemu. - ''' - def __init__(self): - self.compile_conf_dir = os.path.join(os.getcwd(), 'compile.yaml') - self.configure = Configure() - self.client = None - self.container_id = None - - super().__init__( - 'qemu_run', - 'Qemu_run for the code file', - textwrap.dedent(''' - This command will run executable file under qemu for openEuler platform. - ''') - ) - - def do_add_parser(self, parser_adder): - parser = self._parser( - parser_adder, - usage=''' - - %(prog)s [-d target_directory] [-p platform] -''') - - parser.add_argument('-d', dest='target_directory', nargs='?', default=None, - help=''' - Target file path - ''' - ) - - parser.add_argument('-p', dest='platform', default="aarch64", - help=''' - this param is for arch. All possible choices: arm, aarch64, riscv64, x86_64 - ''' - ) - - parser.add_argument('-m', dest='mode', default="user", - help=''' - this param is for the mode for running qemu. All possible choices: user, system - ''' - ) - - return parser - - def do_run(self, args: argparse.Namespace, unknown=None): - # perpare parse help command - if self.pre_parse_help(args, unknown): - return - # Parse the command-line arguments - args = args.parse_args(unknown) - - try: - self.client = DockerProxy() - except DockerException: - logger.error("please install docker first!!!") - return - - if(args.target_directory) is None: - logger.error('Please specify directory of the target file') - self._check_file(args) - self._init_docker() - self._qemu_user_run(args) - logger.info("qemu-run finished ...") - - self.container.stop() - self.container.remove() - - def _check_file(self, args): - file_path = args.target_directory - if not os.path.exists(file_path): - raise ValueError(f"The file '{file_path}' does not exist, please check again") - else: - self._target_file_name = os.path.basename(file_path) # hello.c - - def _init_docker(self): - logger.info("Docker for qemu starting ...") - default_image = "swr.cn-north-4.myhuaweicloud.com/openeuler-embedded/openeuler-container:latest" - if not self.client.is_image_exists(default_image): - logger.error(f'''the docker image does not exists, please run fellow command: - `oebuild update docker`''') - self.container:Container = self.client.container_run_simple(image=default_image, volumes=[]) - # exec_result = self.container.exec_run("/bin/bash -c 'source /opt/buildtools/nativesdk/environment-setup-x86_64-pokysdk-linux; which qemu-aarch64'") - # logger.info(exec_result) - - def _qemu_user_run(self, args): - _qemu_target_file = args.target_directory - _platform = args.platform - container_target_dir = "./" - logger.info("Target file copying into the docker ...") - self.client.copy_to_container( - container=self.container, - source_path=_qemu_target_file, - to_path=container_target_dir) - # ls_command = f"/bin/sh -c ls" - # exec_result = self.container.exec_run(ls_command) - qemu_user_run_command = f"/bin/sh -c 'source /opt/buildtools/nativesdk/environment-setup-x86_64-pokysdk-linux; qemu-{_platform} {container_target_dir}{self._target_file_name}'" - exec_result = self.container.exec_run(qemu_user_run_command) - - # 提取output内容并记录 - output = exec_result.output.decode('utf-8') - logger.info(f"ExecResult(exit_code={exec_result.exit_code})") - logger.info(f'''Output: - =================== - {output}''') diff --git a/src/oebuild/app/plugins/run_qemu/run_qemu.py b/src/oebuild/app/plugins/run_qemu/run_qemu.py new file mode 100644 index 0000000..db549dc --- /dev/null +++ b/src/oebuild/app/plugins/run_qemu/run_qemu.py @@ -0,0 +1,518 @@ +''' +Copyright (c) 2023 openEuler Embedded +oebuild is licensed under Mulan PSL v2. +You can use this software according to the terms and conditions of the Mulan PSL v2. +You may obtain a copy of Mulan PSL v2 at: + http://license.coscl.org.cn/MulanPSL2 +THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +See the Mulan PSL v2 for more details. +''' + +import argparse +import textwrap +import os +import sys +import re +import configparser +import time + +import git + +from docker.models.containers import Container +from docker.errors import DockerException + +import oebuild.util as oebuild_util +from oebuild.docker_proxy import DockerProxy +from oebuild.command import OebuildCommand +from oebuild.configure import Configure +from oebuild.m_log import logger + +CONTAINER_POKY = '/usr1/openeuler/src/yocto-poky' +CONTAINER_IMAGE = '/home/openeuler/image' +CONTAINER_USER = "openeuler" +DEFAULT_MEM = "1G" +DEFAULT_SMP = "-smp 1" +POKY_REMOTE = "https://gitee.com/openeuler/yocto-poky" +POKY_BRANCH = "v4.0.10" +DEFAULT_DOCKER = "swr.cn-north-4.myhuaweicloud.com/openeuler-embedded/openeuler-container:latest" + +ARCH_MAP = { + "aarch64": "qemuarm64", + "arm": "qemuarm", + "x86_64": "qemux86-64", + "riscv64": "qemuriscv64" +} + +MACHINE_MAP = { + "qemuarm64": "virt-4.0", + "qemuarm": "virt-2.12", + "qemux86-64": "microvm", + "qemuriscv64": "virt" +} + +CPU_MAP = { + "qemuarm64": "cortex-a57", + "qemuarm": "cortex-a15", + "qemux86-64": "qemu64", + "qemuriscv64": "rv64" +} + +class RunQemu(OebuildCommand): + ''' + The command for run in qemu platform. + ''' + def __init__(self): + self.compile_conf_dir = os.path.join(os.getcwd(), 'compile.yaml') + self.configure = Configure() + self.client = None + self.container_id = None + self.machine = None + self.kernel_path = None + self.kernel = None + self.rootfs_path = None + self.rootfs = None + self.mem = None + self.smp = None + self.poky_dir = None + self.work_dir = os.getcwd() + self.qemuboot_path = None + + super().__init__( + 'run_qemu', + 'run in qemu platform', + textwrap.dedent(''' + The command for run in qemu platform. + ''') + ) + + def __del__(self): + if self.client is not None: + print(""" + +the destroy container, please wait ... + +""") + try: + container = self.client.get_container(self.container_id) + self.client.stop_container(container=container) + self.client.delete_container(container=container) + except DockerException: + print(f"destory container {self.container_id} faild, please run `docker rm {self.container_id} menully`") + + def do_add_parser(self, parser_adder): + parser = self._parser( + parser_adder, + usage=''' + + %(prog)s [-f conf] [-a arch] [-c create] [-k kernel] [-r rootfs] [-s smp] [-m mem] +''') + parser.add_argument('-f','--conf', dest='conf', + help=''' + this param is for qemuboot.conf, you can point qemuboot.conf, the qemuboot.conf is runqemu nessary file + ''' + ) + parser.add_argument('-a','--arch', dest='arch', + help=''' + this param is for arch. All possible choices: arm, aarch64, riscv64, x86_64, if you dont use the param, we will give + you valid choices interactive + ''' + ) + parser.add_argument('-c','--create', dest='create', + help=''' + this param is for new qemuboot.conf, if you want create a new qemuboot.conf, use it. + ''' + ) + parser.add_argument('-k','--kernel', dest='kernel', + help=''' + this param is for kernel, you can use it point kernel, but if you use it in command, the current directory +can not exist any qemuboot.conf, else the param can not be effective, but you can modify qemuboot.conf's kernel_path +and kernel key if you want point the kernel. + ''' + ) + parser.add_argument('-r','--rootfs', dest='rootfs', + help=''' + this param is for rootfs, you can use it point rootfs, but if you use it in command, the current directory +can not exist any qemuboot.conf, else the param can not be effective, but you can modify qemuboot.conf's rootfs_path +and rootfs key if you want point the rootfs. + ''' + ) + parser.add_argument('-s','--smp', dest='smp', + help=''' + this param is for smp, mapping qemu param -smp + ''' + ) + parser.add_argument('-m','--mem', dest='mem', + help=''' + this param is for mem, mapping qemu param mem + ''' + ) + + return parser + + def do_run(self, args: argparse.Namespace, unknown=None): + # perpare parse help command + if self.pre_parse_help(args, unknown): + return + args = args.parse_args(unknown) + try: + self.client = DockerProxy() + except DockerException: + logger.error("please install docker first!!!") + return + logger.info('Run QEMU......') + self._check_and_set(args) + self.poky_dir = self._get_and_set_poky_dir() + docker_image = self.get_docker_image() + volumns = [] + if self.kernel is None: + self.kernel_path = self._get_from_qemu_boot("kernel_path") + self.kernel = os.path.basename(self.kernel_path) + volumns.append(os.path.abspath(self.kernel_path)+":"+os.path.join(CONTAINER_IMAGE, self.kernel)) + if self.rootfs is None: + self.rootfs_path = self._get_from_qemu_boot("rootfs_path") + self.rootfs = os.path.basename(self.rootfs_path) + volumns.append(os.path.abspath(self.rootfs_path)+":"+os.path.join(CONTAINER_IMAGE, self.rootfs)) + if not os.path.exists(os.path.join(self.work_dir, "build")): + os.mkdir(os.path.join(self.work_dir, "build")) + volumns.append(os.path.join(self.work_dir, "build")+":"+os.path.join(CONTAINER_IMAGE, "build")) + volumns.append(os.path.abspath(self.qemuboot_path)+":"+os.path.join( + CONTAINER_IMAGE, os.path.basename(self.qemuboot_path))) + self.deal_env_container(docker_image=docker_image, other_volumns=volumns) + + self.exec_qemu() + + def exec_qemu(self): + ''' + xxx + ''' + container:Container = self.client.get_container(self.container_id) # type: ignore + + self.init_bash(container=container) + + content = self._get_bashrc_content(container=container) + content = oebuild_util.add_bashrc(content=content, line=f""" + runqemu {os.path.basename(self.qemuboot_path)} nographic + """) + self.update_bashrc(container=container, content=content) + if self._get_from_qemu_boot("machine") == "qemux86-64": + print(""" + + x86-64 machine has not start log, please wait ... + +""") + time.sleep(3) + os.system( + f"docker exec -it -u {CONTAINER_USER} -w {CONTAINER_IMAGE} {container.short_id} bash") + + self.restore_bashrc(container=container) + + def restore_bashrc(self, container: Container): + ''' + Restoring .bashrc will strip out the command line + content added during bitbake initialization + ''' + old_content = self._get_bashrc_content(container=container) + self.update_bashrc(container=container, + content=oebuild_util.restore_bashrc_content(old_content=old_content)) + + def _get_and_set_poky_dir(self): + if not self.configure.is_oebuild_dir(): + # it's not oebuild workspace, so need to download yocto-poky temporary + # first mkdir src + if not os.path.exists("src/yocto-poky"): + os.mkdir("src") + print("prepare rely code, please wait ...") + git.Repo.clone_from(POKY_REMOTE, "src/yocto-poky",branch = POKY_BRANCH, depth=1) + return os.path.abspath("src/yocto-poky") + return self.configure.source_poky_dir() + + def deal_env_container(self,docker_image, other_volumns): + ''' + This operation realizes the processing of the container, + controls how the container is processed by parsing the env + variable, if the container does not exist, or the original + environment and the current environment that needs to be set + are inconsistent, you need to create a new container, otherwise + directly enable the sleeping container + ''' + volumns = [] + volumns.append("/dev/net/tun:/dev/net/tun") + volumns.append(self.poky_dir + ':' + CONTAINER_POKY) + volumns.extend(other_volumns) + + container:Container = self.client.container_run_simple( + image=docker_image, + volumes=volumns) # type: ignore + + self.container_id = container.short_id + container:Container = self.client.get_container(self.container_id) # type: ignore + if not self.client.is_container_running(container): + self.client.start_container(container) + + def get_docker_image(self): + ''' + this is function is to get openeuler docker image automatic + ''' + return DEFAULT_DOCKER + + def init_bash(self, container: Container): + ''' + Bitbake will initialize the compilation environment by reading + the user initialization script first, then making directional + substitutions, and finally writing the initialization script + ''' + # read container default user .bashrc content + content = self._get_bashrc_content(container=container) + + init_sdk_command = '. /opt/buildtools/nativesdk/environment-setup-x86_64-pokysdk-linux' + init_oe_comand = f'. {CONTAINER_POKY}/oe-init-build-env' + cd_image_comand = f'cd {CONTAINER_IMAGE}' + init_command = [init_sdk_command, init_oe_comand, cd_image_comand] + new_content = oebuild_util.init_bashrc_content(content, init_command) + + self.update_bashrc(container=container, content=new_content) + + def _get_bashrc_content(self, container: Container): + content = self.client.container_exec_command( + container=container, + command=f"cat /home/{CONTAINER_USER}/.bashrc", + user="root", + work_space=None, + stream=False).output + + return content.decode() + + def update_bashrc(self, container: Container, content: str): + ''' + update user initialization script by replace file, first create + a file and writed content and copy it to container's .bashrc, finally + remove it + ''' + tmp_file = self._set_tmpfile_content(content) + self.client.copy_to_container( + container=container, + source_path=tmp_file, + to_path=f'/home/{CONTAINER_USER}') + container.exec_run( + cmd=f"mv /home/{CONTAINER_USER}/{tmp_file} /home/{CONTAINER_USER}/.bashrc", + user="root" + ) + os.remove(tmp_file) + + def _set_tmpfile_content(self, content: str): + while True: + tmp_file = oebuild_util.generate_random_str(6) + if os.path.exists(tmp_file): + continue + with open(tmp_file, 'w', encoding="utf-8") as w_f: + w_f.write(content) + break + return tmp_file + + def _get_auto_kernel(self, search_dir): + file_list = os.listdir(search_dir) + for file_path in file_list: + if not os.path.isfile(file_path): + continue + file_name:str = os.path.basename(file_path) + if re.search(r'\.bin$', file_name) or re.search('bzImage', file_name) or \ + re.search('zImage', file_name) or re.search('uImage', file_name) or \ + re.search('fitImage', file_name) or re.search('Image', file_name): + if file_name.endswith(".sha256sum"): + continue + return file_path, file_name + print(f"can not find any kernel file in {search_dir}") + while True: + kernel_path = input("please enter valid kernel path, or enter q for exit:") + if kernel_path == "q": + sys.exit(0) + if not os.path.exists(kernel_path): + print(f"the {kernel_path} is not exists") + continue + return kernel_path, os.path.basename(kernel_path) + + def _get_auto_rootfs(self, search_dir): + file_list = os.listdir(search_dir) + for file_path in file_list: + if not os.path.isfile(file_path): + continue + file_name:str = os.path.basename(file_path) + if file_name.endswith("cpio.gz") or file_name.endswith("cpio"): + return file_path, file_name + print(f"can not find any rootfs file in {search_dir}") + while True: + rootfs_path = input("please enter valid rootfs path, or enter q for exit:") + if rootfs_path == "q": + sys.exit(0) + if not os.path.exists(rootfs_path): + print(f"the {rootfs_path} is not exists") + continue + return rootfs_path, os.path.basename(rootfs_path) + + def _get_auto_machine(self) -> str: + while True: + print("we support the archs:") + for index,arch in enumerate(ARCH_MAP): + print(f"{index+1},{arch}") + input_str = input("please enter arch, or enter q for exit:") + if input_str == "q": + sys.exit(0) + try: + num_index = int(input_str) + except ValueError: + print("please enter right number\n") + continue + try: + machine = ARCH_MAP[list(ARCH_MAP.keys())[num_index-1]] + return machine + except IndexError: + print("please enter right number\n") + continue + + def _find_qemu_boot(self,) -> (bool,str): + ''' + if can not find any qemuboot.conf, we create a new qemuboot.conf + if find a qemuboot.conf, we use it directly + if find more than one qemuboot.conf, we give some choice to select + ''' + qemu_boot_list = [] + file_list = os.listdir(self.work_dir) + for file_path in file_list: + if not os.path.isfile(file_path): + continue + if file_path.endswith(".qemuboot.conf"): + qemu_boot_list.append(file_path) + if len(qemu_boot_list) == 0: + return True, "app.qemuboot.conf" + if len(qemu_boot_list) == 1: + return False, qemu_boot_list[0] + print("check some qemu_boot conf") + + while True: + for index,item in enumerate(qemu_boot_list): + print(f"{index+1}, {item}") + input_str = input(""" +check some qemu_boot conf, please select one if you need, +or enter e for create a new, enter q for quit:""") + if input_str == "q": + sys.exit(0) + if input_str == "e": + return True,self._get_qemu_boot_input_name() + try: + num_index = int(input_str) + except ValueError: + print("please enter right number\n") + continue + try: + qemu_path = qemu_boot_list[num_index-1] + return True,qemu_path + except IndexError: + print("please enter right number\n") + continue + + def _get_qemu_boot_input_name(self): + while True: + name = input("please enter qemuboot.conf prefix") + if os.path.exists(f"{name}.qemuboot.conf"): + print(f"the {name}.qemuboot.conf has exists!!!\n") + continue + return f"{name}.qemuboot.conf" + + def _check_and_set(self,args): + # first, check the qemuboot file, if not exists, raise exception + if args.conf is not None: + if not os.path.exists(args.conf): + raise ValueError(f"the {args.conf} not exists") + else: + self.work_dir = os.path.dirname(args.conf) + self.qemuboot_path = args.conf + return + if args.create is None: + is_new, self.qemuboot_path = self._find_qemu_boot() + if not is_new: + return + else: + while True: + if not args.create.endswith(".qemuboot.conf"): + args.create = f"{args.create}.qemuboot.conf" + if os.path.exists(os.path.join(self.work_dir, args.create)): + args.create = input(f""" +the name {args.create} has exists, please enter other one: +""") + continue + self.qemuboot_path = args.create + break + + # make sure machine param + if args.arch is None: + self.machine = self._get_auto_machine() + else: + if args.arch in ARCH_MAP: + self.machine = ARCH_MAP[args.arch] + else: + self.machine = self._get_auto_machine() + + # make sure kernel param + if args.kernel is None: + self.kernel_path,self.kernel = self._get_auto_kernel(self.work_dir) + else: + if not os.path.exists(args.kernel): + print(f"the file {args.kernel} not exists") + sys.exit(0) + self.kernel_path = args.kernel + self.kernel = os.path.basename(args.kernel) + + # make sure rootfs param + if args.rootfs is None: + self.rootfs_path, self.rootfs = self._get_auto_rootfs(os.getcwd()) + else: + if not os.path.exists(args.rootfs): + print(f"the file {args.rootfs} not exists") + sys.exit(0) + self.rootfs_path = args.rootfs + self.rootfs = os.path.basename(args.rootfs) + + # make sure smp param + if args.smp is None: + self.smp = DEFAULT_SMP + else: + self.smp = args.smp + + # make sure mem param + if args.mem is None: + self.mem = DEFAULT_MEM + else: + self.mem = args.mem + + self._create_new_qemu_boot() + + def _create_new_qemu_boot(self): + config_bsp = {} + config_bsp['staging_dir_native'] = '/opt/buildtools/nativesdk/sysroots/' + config_bsp['staging_bindir_native'] = '/opt/buildtools/nativesdk/sysroots/x86_64-pokysdk-linux/usr/bin/' + config_bsp['machine'] = self.machine + config_bsp['kernel_path'] = self.kernel_path + config_bsp['kernel'] = self.kernel + config_bsp['rootfs_path'] = self.rootfs_path + config_bsp['rootfs'] = self.rootfs + config_bsp['qb_default_fstype'] = "cpio.gz" + config_bsp['qb_net'] = "none" + config_bsp['qb_machine'] = f"-M {MACHINE_MAP[self.machine]}" + config_bsp['qb_cpu'] = f"-cpu {CPU_MAP[self.machine]}" + config_bsp['qb_mem'] = self.mem + config_bsp['qb_smp'] = self.smp + config = configparser.ConfigParser() + config['config_bsp'] = config_bsp + with open(self.qemuboot_path, 'w', encoding="utf-8") as cf: + config.write(cf, False) + return + + def _get_from_qemu_boot(self, key:str): + cf = configparser.ConfigParser() + cf.read(self.qemuboot_path) + for k,v in cf.items('config_bsp'): + if k.upper() == key.upper(): + return v + return "" diff --git a/src/oebuild/configure.py b/src/oebuild/configure.py index 6481210..921826a 100644 --- a/src/oebuild/configure.py +++ b/src/oebuild/configure.py @@ -20,6 +20,7 @@ import oebuild.util as oebuild_util PathType = Union[str, os.PathLike] YOCTO_META_OPENEULER = "yocto_meta_openeuler" +YOCTO_POKY = "yocto-poky" CONFIG = "config" COMPILE_YAML = "compile.yaml.sample" @@ -131,6 +132,13 @@ class Configure: yocto_dir = yocto_config.path return os.path.join(Configure.source_dir(), yocto_dir) + @staticmethod + def source_poky_dir(): + ''' + return src/yocto-poky path + ''' + return os.path.join(Configure.source_dir(), YOCTO_POKY) + @staticmethod def yocto_bak_dir(): ''' diff --git a/src/oebuild/util.py b/src/oebuild/util.py index 5378b3e..a32b61c 100644 --- a/src/oebuild/util.py +++ b/src/oebuild/util.py @@ -25,6 +25,7 @@ from oebuild.version import __version__ CONFIG_YAML = 'config.yaml' UPGRADE_YAML = 'upgrade.yaml' COMPILE_YAML = 'compile.yaml.sample' +BASH_END_FLAG = " ###!!!###" def read_yaml(yaml_dir : pathlib.Path): ''' @@ -138,3 +139,36 @@ def get_instance(factory): Instantiate a class ''' return factory() + +def restore_bashrc_content(old_content): + ''' + restore .bashrc + ''' + new_content = '' + for line in old_content.split('\n'): + line: str = line + if line.endswith(BASH_END_FLAG) or line.replace(" ", '') == '': + continue + new_content = new_content + line + '\n' + return new_content + +def init_bashrc_content(old_content, init_command: list): + ''' + init bashrc + ''' + new_content = restore_bashrc_content(old_content=old_content) + + for command in init_command: + new_content = new_content + command + BASH_END_FLAG + '\n' + + return new_content + +def add_bashrc(content: str, line: str): + ''' + add command line to bashrc + ''' + if not content.endswith('\n'): + content = content + '\n' + content = content + line + BASH_END_FLAG + '\n' + + return content -- Gitee