组件增加产品特性开关经验分享

问题背景

在实际开发过程中会遇到多个产品,需要做不同定制的情况,如果不想维护多个特性分支,那么可以采用 options (选项开关/特性开关)做一个通用的处理方式。

对此有两种方式可供选择CMakeLists.txt和conanfile,由于不是全部组件都有CMakeLists.txt,本文将以conanfile方式,webui组件为例进行说明。

新增options开关

通过options进行选择,需要修改以下文件

  • 1.service.json
    options下新增board_name,举例为board_A,board_B,openUBMC
"options": {
#省略
    "board_name":{
      "option": [
          "openUBMC",
          "board_A",
          "board_B"
        ],
      "default": "openUBMC"
    }
  },
  • 2.conanfile.py

注意:warning:
文件替换操作需要放到build当中,这样通过选择options,构建的组件包内容才会成功替换。:smiling_face_with_tear:

class WebUIConan(ConanBase):
    # 省略

    def _package(self)
        if self.options.xxx:
            src_file = os.path.join(self.source_folder, "file.lua")
            dst_file  = os.path.join(self.package_folder, "file.lua")
            self.run(f"cp -f {src_file} {dst_file}")
        else:
            # copy b to xxx

    def build(self):
        ldap_enabled = self.options.ldap_enabled
        energy_enabled = self.options.energy_enabled
        if ldap_enabled == False:
            build_ldap_enabled_configure()
        if energy_enabled == False:
            build_energy_enabled_configure()

        self._package()

        self.run(f"npm install")
        self.run(f"npm run build")

优化修改

进行产品定制时肯定不止修改一个产品,一处文件,那么可以在原有基础上,增加文件映射的方式,简化替换文件操作,在使用的时候,只需要在对应的映射文件当中增添srcdst即可

定义存放映射文件路径为:组件名/oem/board_name/mappings.json

  • 1.mappings.json
{
  "files": [
    {
      "src": "源文件路径",
      "dst": "目标文件路径"
    }
  ]
}
  • 2.conanfile.py
class WebUIConan(ConanBase):
    # 省略
    def build(self):
        ldap_enabled = self.options.ldap_enabled
        energy_enabled = self.options.energy_enabled
        if ldap_enabled == False:
            build_ldap_enabled_configure()
        if energy_enabled == False:
            build_energy_enabled_configure()

        try:
            # 默认openUBMC无需进行替换
            if self.options.board_name != "openUBMC":
                if not self.process_oem_files(self.options.board_name):
                    raise Exception(f"OEM 文件处理失败: {self.options.board_name}")
        
        except Exception as e:
            print(f"产品组件特性构建错误: {e}")
            raise

        self.run(f"npm install")
        self.run(f"npm run build")


    # 根据 board_name 加载配置文件并执行文件替换
    def process_oem_files(self, board_name):
        print(f"开始处理 {board_name} OEM 文件替换")

        board_name_str = str(board_name)
        config_path = Path("./oem") / board_name_str / "mappings.json"

        if not config_path.exists():
            raise Exception(f"未找到配置文件: {config_path}")  # 如果配置文件不存在,抛出异常

        try:
            with open(config_path, "r", encoding="utf-8") as f:
                config = json.load(f)
            print(f"加载配置: {config_path}")
        except Exception as e:
            raise Exception(f"读取配置失败: {e}")

        files = config.get("files", [])
        if not files:
            print("没有文件映射,跳过处理")
            return True

        success_count = 0
        error_count = 0

        for item in files:
            src_file = item.get("src")
            dst_file = item.get("dst")

            if not src_file or not dst_file:
                raise Exception(f"配置错误: src 或 dst 缺失")

            src_path = Path(src_file)
            dst_path = Path(dst_file)

            if not src_path.exists():
                raise Exception(f"源文件不存在: {src_path}")

            if not dst_path.exists():
                raise Exception(f"目标文件不存在: {dst_path}")

            try:
                # 执行文件替换操作
                self.run(f"cp -f {src_path} {dst_path}")
                print(f"复制成功: {src_path} -> {dst_path}")
                success_count += 1
            except Exception as e:
                raise Exception(f"复制失败 {src_path} -> {dst_path}: {e}")

        print(f"完成: {success_count} 成功, {error_count} 失败")
        return True

CI建议修改

由于组件进行构建时只会进入到默认board_name,建议在CI中新增检查对应的检查

  • 1.mappings.json中定义的src和dst文件路径存在
  • 2.src和对应的dst文件名相同
2 个赞