#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # Copyright (c) 2022 Huawei Device Co., Ltd. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # import os import sys import shutil import argparse sys.path.append(os.path.dirname(os.path.dirname( os.path.dirname(os.path.abspath(__file__))))) from hb.resources.config import Config from hb.util.log_util import LogUtil from hb.util.system_util import SystemUtil from hb.util.io_util import IoUtil class Packer(): def __init__(self, packer_args) -> None: self.config = Config() self.replace_items = { r'${product_name}': self.config.product, r'${root_path}': self.config.root_path, r'${out_path}': self.config.out_path } self.packing_process = [ self.mv_usr_libs, self.create_fs_dirs, self.fs_link, self.fs_filemode, self.fs_make_cmd, self.fs_tear_down ] self.fs_cfg = None self.chmod_dirs = [] @classmethod def is_lib(cls, lib): return lib.startswith('lib') and lib.endswith('.so') @classmethod def is_incr(cls, fs_incr): exist_ok = False if fs_incr is None else True with_rm = True if fs_incr is None else False return exist_ok, with_rm @classmethod def chmod(cls, file, mode): mode = int(str(mode), base=8) if os.path.exists(file): os.chmod(file, mode) @classmethod def filter(cls, files, ignore_list): if ignore_list is None or not len(ignore_list): return files filter_files = [] for file in files: flag = True for ignore in ignore_list: if file.startswith(ignore) or file.endswith(ignore): flag = False break if flag: filter_files.append(file) return filter_files def mv_usr_libs(self): src_path = self.config.out_path libs = [lib for lib in os.listdir(src_path) if self.is_lib(lib)] target_path = os.path.join(src_path, 'usr', 'lib') os.makedirs(target_path, exist_ok=True) for lib in libs: source_file = os.path.join(src_path, lib) target_file = os.path.join(target_path, lib) shutil.move(source_file, target_file) def create_fs_dirs(self): fs_path = os.path.join(self.config.out_path, self.fs_cfg.get('fs_dir_name', 'rootfs')) exist_ok, with_rm = self.is_incr(self.fs_cfg.get('fs_incr', None)) if with_rm and os.path.exists(fs_path): shutil.rmtree(fs_path) os.makedirs(fs_path, exist_ok=exist_ok) self.replace_items[r'${fs_dir}'] = fs_path for fs_dir in self.fs_cfg.get('fs_dirs', []): source_dir = fs_dir.get('source_dir', '') target_dir = fs_dir.get('target_dir', '') if target_dir == '': continue source_path = self.fs_dirs_replace(source_dir, self.config.out_path) target_path = self.fs_dirs_replace(target_dir, fs_path) if source_dir == '' or not os.path.exists(source_path): os.makedirs(target_path, exist_ok=True) target_mode_tuple = (target_path, fs_dir.get('dir_mode', 755)) self.chmod_dirs.append(target_mode_tuple) continue self.copy_files(source_path, target_path, fs_dir) def fs_dirs_replace(self, path, default_path): source_path, is_changed = self.replace(path) if not is_changed: source_path = os.path.join(default_path, path) return source_path def copy_files(self, spath, tpath, fs_dir): ignore_files = fs_dir.get('ignore_files', []) dir_mode = fs_dir.get('dir_mode', 755) file_mode = fs_dir.get('file_mode', 555) def copy_file_process(source_path, target_path): if not os.path.isdir(target_path): os.makedirs(target_path, exist_ok=True) self.chmod_dirs.append((target_path, dir_mode)) tfile = os.path.join(target_path, os.path.basename(source_path)) try: shutil.copy(sfile, tfile, follow_symlinks=False) self.chmod_dirs.append((tfile, file_mode)) except FileExistsError: LogUtil.hb_warning(f'Target file: {tfile} already exists!') if os.path.isfile(spath): sfile = spath copy_file_process(spath, tpath) return for srelpath, sfile in self.list_all_files(spath, ignore_files): tdirname = srelpath.replace(spath, tpath) copy_file_process(sfile, tdirname) def list_all_files(self, path, ignore_list=None): for relpath, _, files in os.walk(path): files = self.filter(files, ignore_list) for file in files: full_path = os.path.join(path, relpath, file) if os.path.isfile(full_path): yield relpath, full_path def replace(self, raw_str): old_str = raw_str for old, new in self.replace_items.items(): raw_str = raw_str.replace(old, new) return raw_str, old_str != raw_str def fs_link(self): fs_symlink = self.fs_cfg.get('fs_symlink', []) for symlink in fs_symlink: source, _ = self.replace(symlink.get('source', '')) link_name, _ = self.replace(symlink.get('link_name', '')) if os.path.exists(link_name): os.remove(link_name) os.symlink(source, link_name) def fs_filemode(self): fs_filemode = self.fs_cfg.get('fs_filemode', []) for filestat in fs_filemode: file_dir = os.path.join(self.replace_items.get(r'${fs_dir}', ""), filestat.get('file_dir', '')) file_mode = filestat.get('file_mode', 0) if os.path.exists(file_dir) and file_mode > 0: self.chmod_dirs.append((file_dir, file_mode)) for file_dir, file_mode in self.chmod_dirs: self.chmod(file_dir, file_mode) def fs_make_cmd(self): fs_make_cmd = self.fs_cfg.get('fs_make_cmd', []) log_path = self.config.log_path for cmd in fs_make_cmd: cmd, _ = self.replace(cmd) cmd = cmd.split(' ') SystemUtil.exec_command(cmd, log_path=log_path) def fs_tear_down(self): while len(self.chmod_dirs): tfile = self.chmod_dirs.pop()[0] if os.path.isfile(tfile): self.chmod(tfile, 555) elif os.path.isdir(tfile): self.chmod(tfile, 755) def fs_attr_process(self, fs_cfg): fs_attr = fs_cfg.get('fs_attr', {}) for attr_key, attr_value in fs_attr.items(): if attr_key in self.config.fs_attr: for target_key, target_value in attr_value.items(): if target_key in fs_cfg: fs_cfg[target_key] += target_value else: fs_cfg[target_key] = target_value return fs_cfg def fs_make(self, cmd_args): fs_cfg_path = os.path.join(self.config.product_path, 'fs.yml') if not os.path.isfile(fs_cfg_path): LogUtil.hb_info(f'{fs_cfg_path} not found, stop packing fs. ' 'If the product does not need to be packaged, ignore it.') return if self.config.fs_attr is None: LogUtil.hb_info('component compiling, no need to pack fs') return fs_cfg_list = IoUtil.read_yaml_file(fs_cfg_path) for fs_cfg in fs_cfg_list: self.fs_cfg = self.fs_attr_process(fs_cfg) if self.fs_cfg.get('fs_dir_name', None) is None: continue for fs_process_func in self.packing_process: fs_process_func() if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('--product', required=True) parser.add_argument('--root-path', required=True) parser.add_argument('--out-path', required=True) parser.add_argument('--log-path', required=True) parser.add_argument('--product-path', required=True) args = parser.parse_args() packer = Packer(args) packer.replace_items = { r'${product_name}': args.product, r'${root_path}': args.root_path, r'${out_path}': args.out_path, r'${log_path}': args.log_path, r'${product_path}': args.product_path } packer.fs_make('')