openubmc-bingo 0.5.275__py3-none-any.whl → 0.6.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of openubmc-bingo might be problematic. Click here for more details.
- bmcgo/__init__.py +1 -1
- bmcgo/bmcgo_config.py +22 -10
- bmcgo/cli/cli.py +86 -39
- bmcgo/cli/config.conan2.yaml +9 -0
- bmcgo/codegen/lua/codegen.py +1 -1
- bmcgo/codegen/lua/script/gen_intf_rpc_json.py +15 -14
- bmcgo/component/analysis/analysis.py +8 -8
- bmcgo/component/analysis/intf_validation.py +23 -12
- bmcgo/component/build.py +76 -14
- bmcgo/component/component_dt_version_parse.py +3 -2
- bmcgo/component/component_helper.py +43 -15
- bmcgo/component/coverage/incremental_cov.py +2 -2
- bmcgo/component/deploy.py +68 -14
- bmcgo/component/gen.py +1 -1
- bmcgo/component/package_info.py +128 -38
- bmcgo/component/template_v2/conanbase.py.mako +352 -0
- bmcgo/component/template_v2/conanfile.deploy.py.mako +26 -0
- bmcgo/component/test.py +53 -20
- bmcgo/frame.py +7 -3
- bmcgo/functional/analysis.py +3 -2
- bmcgo/functional/check.py +10 -6
- bmcgo/functional/conan_index_build.py +79 -20
- bmcgo/functional/csr_build.py +11 -3
- bmcgo/functional/diff.py +1 -1
- bmcgo/functional/fetch.py +1 -1
- bmcgo/functional/full_component.py +32 -24
- bmcgo/functional/git_history.py +220 -0
- bmcgo/functional/maintain.py +55 -12
- bmcgo/functional/new.py +1 -1
- bmcgo/functional/schema_valid.py +2 -2
- bmcgo/logger.py +2 -3
- bmcgo/misc.py +130 -9
- bmcgo/tasks/conan/__init__.py +10 -0
- bmcgo/tasks/conan/conanfile.py +45 -0
- bmcgo/tasks/task.py +27 -4
- bmcgo/tasks/task_build_conan.py +433 -65
- bmcgo/tasks/task_buildgppbin.py +8 -2
- bmcgo/tasks/task_download_buildtools.py +76 -0
- bmcgo/tasks/task_download_dependency.py +1 -0
- bmcgo/tasks/task_hpm_envir_prepare.py +1 -1
- bmcgo/utils/build_conans.py +231 -0
- bmcgo/utils/component_post.py +6 -4
- bmcgo/utils/component_version_check.py +10 -5
- bmcgo/utils/config.py +76 -40
- bmcgo/utils/fetch_component_code.py +44 -25
- bmcgo/utils/installations/install_plans/qemu.yml +6 -0
- bmcgo/utils/installations/install_plans/studio.yml +6 -0
- bmcgo/utils/installations/install_workflow.py +28 -2
- bmcgo/utils/merge_csr.py +124 -0
- bmcgo/utils/tools.py +239 -117
- bmcgo/worker.py +2 -2
- {openubmc_bingo-0.5.275.dist-info → openubmc_bingo-0.6.0.dist-info}/METADATA +4 -2
- {openubmc_bingo-0.5.275.dist-info → openubmc_bingo-0.6.0.dist-info}/RECORD +56 -46
- {openubmc_bingo-0.5.275.dist-info → openubmc_bingo-0.6.0.dist-info}/WHEEL +0 -0
- {openubmc_bingo-0.5.275.dist-info → openubmc_bingo-0.6.0.dist-info}/entry_points.txt +0 -0
- {openubmc_bingo-0.5.275.dist-info → openubmc_bingo-0.6.0.dist-info}/top_level.txt +0 -0
bmcgo/utils/config.py
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
import json
|
|
14
14
|
import os
|
|
15
15
|
import sys
|
|
16
|
+
import re
|
|
16
17
|
import copy
|
|
17
18
|
import hashlib
|
|
18
19
|
import stat
|
|
@@ -40,6 +41,7 @@ log = Logger("config")
|
|
|
40
41
|
VERSION = "Version"
|
|
41
42
|
BUILD_NUMBER = "BuildNum"
|
|
42
43
|
VERSION_LEN = 5
|
|
44
|
+
BMCSDKVERSION = "BMCSDKVersion"
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
class Config:
|
|
@@ -63,11 +65,11 @@ class Config:
|
|
|
63
65
|
_profile = ""
|
|
64
66
|
conan_index_options = []
|
|
65
67
|
_manifest_data = {}
|
|
66
|
-
schema_file = f"{misc.
|
|
68
|
+
schema_file = f"{misc.schema_path()}/manifest.schema.json"
|
|
67
69
|
schema_need_validate = False # 由frame.py或target文件配置是否启用
|
|
68
70
|
enable_debug_model = False # # 是否启用调测模式,默认为false,个人构建启用
|
|
69
71
|
|
|
70
|
-
def __init__(self, bconfig: BmcgoConfig, board_name=
|
|
72
|
+
def __init__(self, bconfig: BmcgoConfig, board_name=misc.boardname_default(), target="target_personal"):
|
|
71
73
|
self.bconfig = bconfig
|
|
72
74
|
|
|
73
75
|
# 单板名
|
|
@@ -130,15 +132,20 @@ class Config:
|
|
|
130
132
|
self.docker_tag = None
|
|
131
133
|
# 伙伴/开源远端相关
|
|
132
134
|
self.open_remote = None
|
|
135
|
+
self.platform_package = None
|
|
133
136
|
|
|
134
137
|
@property
|
|
135
138
|
def partner_mode(self):
|
|
136
139
|
"""是否是伙伴模式"""
|
|
137
140
|
return self.bconfig.partner_mode
|
|
138
141
|
|
|
142
|
+
@property
|
|
143
|
+
def rtos_path(self):
|
|
144
|
+
return f"/opt/{self.rtos_offering}/{self.rtos_version}"
|
|
145
|
+
|
|
139
146
|
@property
|
|
140
147
|
def sysroot(self):
|
|
141
|
-
return f"
|
|
148
|
+
return f"{self.rtos_path}/arm64le_{self.rtos_kernel}/sdk"
|
|
142
149
|
|
|
143
150
|
@property
|
|
144
151
|
def self_sign(self):
|
|
@@ -207,15 +214,16 @@ class Config:
|
|
|
207
214
|
|
|
208
215
|
@staticmethod
|
|
209
216
|
def argparser(code_path, partner_mode=False):
|
|
210
|
-
parser = argparse.ArgumentParser(description="构建
|
|
217
|
+
parser = argparse.ArgumentParser(description="构建" + misc.boardname_default(),
|
|
218
|
+
formatter_class=argparse.RawTextHelpFormatter)
|
|
211
219
|
boards = Config.get_all_board(code_path)
|
|
212
|
-
help_txt = "单板包,可选值为build/product/<Kunpeng|...>/下的目录名\n默认:
|
|
220
|
+
help_txt = "单板包,可选值为build/product/<Kunpeng|...>/下的目录名\n默认:" + misc.boardname_default()
|
|
213
221
|
help_txt += "\n支持的单板列表:"
|
|
214
222
|
for board, _ in boards.items():
|
|
215
223
|
help_txt += "\n" + board
|
|
216
|
-
parser.add_argument("-b", "--board_name", help=help_txt, default=
|
|
217
|
-
parser.add_argument("-bt", "--build_type",
|
|
218
|
-
help="构建类型,可选:debug(调试包), release
|
|
224
|
+
parser.add_argument("-b", "--board_name", help=help_txt, default=misc.boardname_default())
|
|
225
|
+
parser.add_argument("-bt", "--build_type",
|
|
226
|
+
help="构建类型,可选:debug(调试包), release(正式包)", default="debug")
|
|
219
227
|
parser.add_argument(
|
|
220
228
|
"-s",
|
|
221
229
|
"--from_source",
|
|
@@ -329,6 +337,14 @@ class Config:
|
|
|
329
337
|
template_sha256 = template_hash.hexdigest()
|
|
330
338
|
return sha256 + template_sha256
|
|
331
339
|
|
|
340
|
+
@staticmethod
|
|
341
|
+
def _get_number_version(version):
|
|
342
|
+
if "B" in version:
|
|
343
|
+
version = version.rsplit(".", 1)[0]
|
|
344
|
+
elif "b" in version:
|
|
345
|
+
version = version.rsplit(".", 1)[0]
|
|
346
|
+
return version
|
|
347
|
+
|
|
332
348
|
@functools.cached_property
|
|
333
349
|
def rtos_offering(self):
|
|
334
350
|
return self.get_manufacture_config("base/rtos_offering", "RTOS")
|
|
@@ -406,7 +422,7 @@ class Config:
|
|
|
406
422
|
"""
|
|
407
423
|
if self.version == "":
|
|
408
424
|
return self.version
|
|
409
|
-
version = self.
|
|
425
|
+
version = self._get_number_version(self.version)
|
|
410
426
|
correction_version = str(int(version.rsplit(".", 1)[-1]) + 1)
|
|
411
427
|
version = "{}.{}".format(version.rsplit(".", 1)[0], correction_version.zfill(2))
|
|
412
428
|
return version
|
|
@@ -420,7 +436,7 @@ class Config:
|
|
|
420
436
|
|
|
421
437
|
# 模板化,manifest.yml替换时使用
|
|
422
438
|
def get_template(self) -> dict:
|
|
423
|
-
version = self.
|
|
439
|
+
version = self._get_number_version(self.version)
|
|
424
440
|
template = {
|
|
425
441
|
"code_root": self.code_root,
|
|
426
442
|
"product": f"{self.code_path}/product",
|
|
@@ -554,12 +570,12 @@ class Config:
|
|
|
554
570
|
def set_build_type(self, arg_build_type="debug"):
|
|
555
571
|
'''
|
|
556
572
|
这只编译app的cmake编译选项
|
|
557
|
-
参数:arg_build_type 可选值:"debug";"release"
|
|
573
|
+
参数:arg_build_type 可选值:"debug";"release"
|
|
558
574
|
'''
|
|
559
575
|
# 可以编译的版本
|
|
560
|
-
build_type_turple = (
|
|
576
|
+
build_type_turple = misc.build_type()
|
|
561
577
|
if arg_build_type not in build_type_turple:
|
|
562
|
-
log.info("不支持的构建类型, 请从以下构建类型中输入: [
|
|
578
|
+
log.info(f"不支持的构建类型, 请从以下构建类型中输入: [{misc.build_type_str()}]")
|
|
563
579
|
return
|
|
564
580
|
|
|
565
581
|
self.build_type = arg_build_type
|
|
@@ -599,7 +615,7 @@ class Config:
|
|
|
599
615
|
|
|
600
616
|
def set_common_params(self):
|
|
601
617
|
# conan仓
|
|
602
|
-
self.remote =
|
|
618
|
+
self.remote = misc.conan_remote()
|
|
603
619
|
self.remote_list = []
|
|
604
620
|
self.temp_platform_dir_name = f"temp/board_{self.board_name}_platform"
|
|
605
621
|
self.temp_platform_build_dir = os.path.join(self.code_path, self.temp_platform_dir_name)
|
|
@@ -632,6 +648,8 @@ class Config:
|
|
|
632
648
|
boards = self.get_all_board(self.code_path)
|
|
633
649
|
# 单板配置路径
|
|
634
650
|
self.ori_board_path = boards.get(self.board_name)
|
|
651
|
+
if not self.ori_board_path:
|
|
652
|
+
raise errors.ConfigException(f"未知的单板 {self.board_name},请检查参数配置是否正确")
|
|
635
653
|
log.info(f"单板源配置路径: {self.ori_board_path}")
|
|
636
654
|
# openUBMC get Kunpeng
|
|
637
655
|
self.productline = os.path.abspath(self.ori_board_path).split('/')[-2]
|
|
@@ -646,7 +664,7 @@ class Config:
|
|
|
646
664
|
tools.run_command(f"cp -rf {self.ori_board_path} {self.board_path}")
|
|
647
665
|
|
|
648
666
|
def set_enable_luajit(self, enable):
|
|
649
|
-
if self.build_type == "Dt":
|
|
667
|
+
if misc.conan_v1() and self.build_type == "Dt":
|
|
650
668
|
self.enable_luajit = False
|
|
651
669
|
self.enable_luajit = enable
|
|
652
670
|
|
|
@@ -671,7 +689,10 @@ class Config:
|
|
|
671
689
|
self.enable_qemu = True if enable_qemu is True else self.enable_qemu
|
|
672
690
|
|
|
673
691
|
def set_stage(self, stage: str):
|
|
674
|
-
|
|
692
|
+
if misc.conan_v2():
|
|
693
|
+
in_stage = [member.value for member in misc.StageEnum]
|
|
694
|
+
else:
|
|
695
|
+
in_stage = [member.value for member in misc.StageEnum if member is not misc.StageEnum.STAGE_PRE]
|
|
675
696
|
if stage not in in_stage:
|
|
676
697
|
raise errors.ConfigException(f"stage 参数错误, 可用选项为: {in_stage}, 请检查参数")
|
|
677
698
|
self.stage = stage
|
|
@@ -761,11 +782,11 @@ class Config:
|
|
|
761
782
|
com_package_split = com_package[index].split("/")
|
|
762
783
|
if len(com_package_split) == 1:
|
|
763
784
|
com_package[index] = sub_name
|
|
764
|
-
elif len(com_package_split) == 2:
|
|
785
|
+
elif len(com_package_split) == 2 and "@" not in com_package[index]:
|
|
765
786
|
stage = self.stage
|
|
766
787
|
if stage != misc.StageEnum.STAGE_STABLE.value:
|
|
767
788
|
stage = misc.StageEnum.STAGE_RC.value
|
|
768
|
-
user_channel = f"@{
|
|
789
|
+
user_channel = f"@{misc.conan_user()}/{stage}"
|
|
769
790
|
com_package[index] += user_channel
|
|
770
791
|
|
|
771
792
|
def get_subsys_coms(self):
|
|
@@ -781,7 +802,10 @@ class Config:
|
|
|
781
802
|
return comps
|
|
782
803
|
|
|
783
804
|
def load_platform_manifest(self, package):
|
|
784
|
-
|
|
805
|
+
self.platform_package = package[misc.CONAN]
|
|
806
|
+
if misc.conan_v2():
|
|
807
|
+
self.platform_package = self.platform_package.lower()
|
|
808
|
+
package_conan_dir = tools.install_platform(package, self.stage, self.remote, self.update_conan_cache)
|
|
785
809
|
if os.path.isdir(self.temp_platform_build_dir):
|
|
786
810
|
shutil.rmtree(self.temp_platform_build_dir)
|
|
787
811
|
os.makedirs(os.path.join(self.code_path, "temp"), exist_ok=True)
|
|
@@ -850,6 +874,11 @@ class Config:
|
|
|
850
874
|
if template is None:
|
|
851
875
|
template = self.get_template()
|
|
852
876
|
mani = self._load_manifest_with_template(filename, template)
|
|
877
|
+
schema_file = mani.get("schema", "")
|
|
878
|
+
if schema_file:
|
|
879
|
+
self.schema_file = schema_file
|
|
880
|
+
else:
|
|
881
|
+
self.schema_file = misc.get_decleared_schema_file(filename)
|
|
853
882
|
inc_filename = mani.get("include")
|
|
854
883
|
if not inc_filename:
|
|
855
884
|
return mani
|
|
@@ -865,17 +894,13 @@ class Config:
|
|
|
865
894
|
mani_inc = self.load_manifest_yml(inc_filename, template)
|
|
866
895
|
self.merge_manifest_yml(mani, mani_inc, "")
|
|
867
896
|
if top_manifest and self.schema_need_validate:
|
|
868
|
-
|
|
869
|
-
if
|
|
870
|
-
schema_file = misc.get_decleared_schema_file(filename)
|
|
871
|
-
# 获取产品配置的schema,已通过schema校验,必然存在
|
|
872
|
-
if schema_file is None:
|
|
897
|
+
# 获取产品配置的schema,已通过schema校验,必然存在
|
|
898
|
+
if self.schema_file is None:
|
|
873
899
|
raise errors.BmcGoException("未找到应该遵循的manifest规范,无法校验manifest.yml文件正确性," +
|
|
874
900
|
"请检查配置项yaml-language-server或schema配置项是否正确")
|
|
875
|
-
if not os.path.isfile(schema_file):
|
|
876
|
-
raise errors.BmcGoException(f"产品配置了一个不存在的manifest规范文件{schema_file}," +
|
|
877
|
-
"无法校验manifest.yml文件正确性,请检查
|
|
878
|
-
self.schema_file = schema_file
|
|
901
|
+
if not os.path.isfile(self.schema_file):
|
|
902
|
+
raise errors.BmcGoException(f"产品配置了一个不存在的manifest规范文件{self.schema_file}," +
|
|
903
|
+
f"无法校验manifest.yml文件正确性,请检查{misc.tool_name()}是否正确安装")
|
|
879
904
|
with open(self.schema_file, "rb") as fp:
|
|
880
905
|
schema = json.load(fp)
|
|
881
906
|
log.debug(f"使用规范配置文件{self.schema_file}校验{filename}配置项")
|
|
@@ -886,7 +911,7 @@ class Config:
|
|
|
886
911
|
self.archive_conf = self.get_manufacture_config("archive")
|
|
887
912
|
self.archive_path = self.get_manufacture_config("archive/archive_path")
|
|
888
913
|
if self.target == "docker_build":
|
|
889
|
-
self.archive_path = "
|
|
914
|
+
self.archive_path = f"{misc.logo()}/ToCommunity"
|
|
890
915
|
self.partner_docker = self.get_manufacture_config("archive/partner_docker")
|
|
891
916
|
|
|
892
917
|
def version_split(self):
|
|
@@ -922,7 +947,9 @@ class Config:
|
|
|
922
947
|
with open(manifest_file, "r", encoding="utf-8") as fp:
|
|
923
948
|
mani = yaml.safe_load(fp)
|
|
924
949
|
mani["base"]["version"] = self.version
|
|
925
|
-
schema_file =
|
|
950
|
+
schema_file = mani.get("schema", "")
|
|
951
|
+
if not schema_file:
|
|
952
|
+
schema_file = misc.get_decleared_schema_file(manifest_file)
|
|
926
953
|
with os.fdopen(os.open(manifest_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC,
|
|
927
954
|
stat.S_IWUSR | stat.S_IRUSR), 'w+') as file_handler:
|
|
928
955
|
if schema_file != "":
|
|
@@ -961,6 +988,9 @@ class Config:
|
|
|
961
988
|
|
|
962
989
|
with open(dest_file, "r") as dst:
|
|
963
990
|
ver_dst_data = json.load(dst)
|
|
991
|
+
if self.platform_package:
|
|
992
|
+
version_temp = re.split("/|@", self.platform_package)[1].split(".")[:4]
|
|
993
|
+
ver_dst_data[BMCSDKVERSION] = ".".join(version_temp)
|
|
964
994
|
# 大版本字段为空,自动填充
|
|
965
995
|
if ver_dst_data[VERSION] == "":
|
|
966
996
|
real_version = ""
|
|
@@ -968,7 +998,10 @@ class Config:
|
|
|
968
998
|
real_version = fixed_version
|
|
969
999
|
else:
|
|
970
1000
|
real_version = version
|
|
971
|
-
|
|
1001
|
+
if misc.conan_v2():
|
|
1002
|
+
ver_dst_data[VERSION] = self._get_number_version(real_version)
|
|
1003
|
+
else:
|
|
1004
|
+
ver_dst_data[VERSION] = real_version.rsplit(".", 1)[0] if "B" in real_version else real_version
|
|
972
1005
|
# 如果包名中有manufacture字段,则自动加1
|
|
973
1006
|
if "manufacture" in package_name:
|
|
974
1007
|
version_list = ver_dst_data[VERSION].rsplit(".", 1)
|
|
@@ -976,10 +1009,10 @@ class Config:
|
|
|
976
1009
|
|
|
977
1010
|
# 小版本为空,则填充
|
|
978
1011
|
if ver_dst_data[BUILD_NUMBER] == "":
|
|
979
|
-
if fixed_version is not None and "B" in fixed_version:
|
|
980
|
-
ver_dst_data[BUILD_NUMBER] = fixed_version.rsplit(".", 1)[1].strip("B")
|
|
1012
|
+
if fixed_version is not None and ("B" in fixed_version or "b" in fixed_version):
|
|
1013
|
+
ver_dst_data[BUILD_NUMBER] = fixed_version.rsplit(".", 1)[1].strip("B").strip("b")
|
|
981
1014
|
else:
|
|
982
|
-
ver_dst_data[BUILD_NUMBER] = version.rsplit(".", 1)[1].strip("B")
|
|
1015
|
+
ver_dst_data[BUILD_NUMBER] = version.rsplit(".", 1)[1].strip("B").strip("b")
|
|
983
1016
|
|
|
984
1017
|
# 填充构建日期
|
|
985
1018
|
if ver_dst_data['ReleaseDate'] == "":
|
|
@@ -1027,9 +1060,6 @@ class Config:
|
|
|
1027
1060
|
self.set_build_type(args.build_type)
|
|
1028
1061
|
self.set_stage(args.stage)
|
|
1029
1062
|
self.set_board_name(args.board_name)
|
|
1030
|
-
self.asan = args.asan
|
|
1031
|
-
self.remote = args.remote
|
|
1032
|
-
self.remote_list = tools.get_conan_remote_list(self.remote)
|
|
1033
1063
|
self.verbose = args.verbose
|
|
1034
1064
|
if os.environ.get("LOG"):
|
|
1035
1065
|
self.verbose = True
|
|
@@ -1041,19 +1071,25 @@ class Config:
|
|
|
1041
1071
|
self.set_enable_arm_gcov(args.coverage)
|
|
1042
1072
|
self.set_enable_luajit(args.enable_luajit)
|
|
1043
1073
|
self.set_show_version()
|
|
1074
|
+
self.asan = args.asan
|
|
1075
|
+
self.remote = args.remote
|
|
1076
|
+
self.remote_list = tools.get_conan_remote_list(self.remote)
|
|
1044
1077
|
self.get_archive()
|
|
1045
1078
|
self.init_conan_profile(args.profile)
|
|
1046
1079
|
|
|
1047
1080
|
# 构建阶段检查
|
|
1048
|
-
bt =
|
|
1081
|
+
bt = misc.build_type()
|
|
1049
1082
|
if args.build_type not in bt:
|
|
1050
|
-
raise errors.ConfigException("构建类型 build_type 错误, 可用选项为:
|
|
1083
|
+
raise errors.ConfigException(f"构建类型 build_type 错误, 可用选项为: {misc.build_type_str()}, 请检查参数")
|
|
1051
1084
|
|
|
1052
1085
|
hpm_encrypt = self.bconfig.hpm_encrypt
|
|
1053
|
-
if hpm_encrypt and hpm_encrypt.need_encrypt:
|
|
1086
|
+
if misc.need_encrypt_hpm() or (hpm_encrypt and hpm_encrypt.need_encrypt):
|
|
1054
1087
|
if not shutil.which("crypto_tool"):
|
|
1055
1088
|
raise errors.BmcGoException(f"开启了hpm加密配置, 在环境中未找到 'crypto_tool', 请确认环境配置是否正确.")
|
|
1056
1089
|
os.environ["HPM_ENCRYPT"] = "true"
|
|
1090
|
+
else:
|
|
1091
|
+
log.info(f"未开启hpm加密配置")
|
|
1092
|
+
|
|
1057
1093
|
|
|
1058
1094
|
def _load_manifest_with_template(self, filename, template):
|
|
1059
1095
|
try:
|
|
@@ -19,7 +19,7 @@ import patch_ng
|
|
|
19
19
|
import yaml
|
|
20
20
|
from git import Repo
|
|
21
21
|
|
|
22
|
-
from bmcgo
|
|
22
|
+
from bmcgo import errors
|
|
23
23
|
from bmcgo import misc
|
|
24
24
|
from bmcgo.utils.tools import Tools
|
|
25
25
|
|
|
@@ -44,7 +44,7 @@ class FetchComponentCode:
|
|
|
44
44
|
@staticmethod
|
|
45
45
|
def resolve_version_range(component_name, version_range):
|
|
46
46
|
cmd = f"conan info '{version_range}' --package-filter={component_name}/*" \
|
|
47
|
-
f" -r {misc.
|
|
47
|
+
f" -r {misc.conan_remote()} --only None 2>/dev/null"
|
|
48
48
|
ret, output = subprocess.getstatusoutput(cmd)
|
|
49
49
|
output = output.strip()
|
|
50
50
|
if ret != 0 or not output:
|
|
@@ -93,7 +93,10 @@ class FetchComponentCode:
|
|
|
93
93
|
@staticmethod
|
|
94
94
|
def __apply_patches(conandata_file, patches, code_dir):
|
|
95
95
|
cwd = os.getcwd()
|
|
96
|
-
|
|
96
|
+
if misc.conan_v1():
|
|
97
|
+
recipe_folder = os.path.join(os.path.dirname(conandata_file), "..", "export_source")
|
|
98
|
+
else:
|
|
99
|
+
recipe_folder = os.path.join(os.path.dirname(conandata_file), "..", "es")
|
|
97
100
|
recipe_folder = os.path.realpath(recipe_folder)
|
|
98
101
|
os.chdir(os.path.join(cwd, code_dir))
|
|
99
102
|
for patch in patches:
|
|
@@ -136,17 +139,20 @@ class FetchComponentCode:
|
|
|
136
139
|
@staticmethod
|
|
137
140
|
def __update_code_by_branch(code_dir, deps):
|
|
138
141
|
url = deps['url']
|
|
139
|
-
branch = deps
|
|
140
|
-
|
|
142
|
+
branch = deps.get("branch", "").split('/')[-1] or deps.get("commit", "")
|
|
141
143
|
if os.path.exists(code_dir):
|
|
142
144
|
repo = Repo(code_dir)
|
|
143
|
-
repo.git.fetch('origin'
|
|
144
|
-
repo.git.pull('origin', branch)
|
|
145
|
-
repo.git.checkout(branch)
|
|
145
|
+
repo.git.fetch('origin')
|
|
146
146
|
else:
|
|
147
|
-
Repo.clone_from(url, to_path=code_dir
|
|
147
|
+
Repo.clone_from(url, to_path=code_dir)
|
|
148
148
|
repo = Repo(code_dir)
|
|
149
|
-
|
|
149
|
+
if re.match(r"^[0-9a-f]{40}$", branch):
|
|
150
|
+
repo.head.reset(branch, working_tree=True)
|
|
151
|
+
elif branch.startswith("refs/tags/"):
|
|
152
|
+
repo.head.reset(branch[len("refs/tags/"):], working_tree=True)
|
|
153
|
+
else:
|
|
154
|
+
repo.git.checkout(branch)
|
|
155
|
+
log.info("更新代码(组件: %s, 地址: %s, 节点: %s)", code_dir, url, branch)
|
|
150
156
|
|
|
151
157
|
@staticmethod
|
|
152
158
|
def __update_component_code_by_conandata(name, version, conandata_file):
|
|
@@ -207,26 +213,39 @@ class FetchComponentCode:
|
|
|
207
213
|
try:
|
|
208
214
|
os.chdir(self.target_dir)
|
|
209
215
|
log.info("更新 %s 组件代码到 %s 开始", component_name, conan_version)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
216
|
+
if misc.conan_v1():
|
|
217
|
+
version_split = re.split(r'[/, @]', conan_version)
|
|
218
|
+
conan_dir = os.path.join(os.path.expanduser('~'), '.conan/data/', *version_split, 'export')
|
|
219
|
+
conandata = os.path.join(conan_dir, 'conandata.yml')
|
|
220
|
+
if not os.path.exists(conan_dir):
|
|
221
|
+
conan_remote_list = tools.get_conan_remote_list(self.remote)
|
|
222
|
+
tools.download_conan_recipes(conan_version, conan_remote_list)
|
|
223
|
+
if os.path.exists(conandata):
|
|
224
|
+
self.__update_component_code_by_conandata(version_split[0], version_split[1], conandata)
|
|
225
|
+
return
|
|
226
|
+
|
|
227
|
+
conanfile = os.path.join(conan_dir, 'conanfile.py')
|
|
228
|
+
if os.path.exists(conanfile):
|
|
229
|
+
revision, url = self.__getinfo_from_conanfile(conanfile)
|
|
230
|
+
self.__update_code_by_commit_id(component_name, url, revision)
|
|
231
|
+
return
|
|
232
|
+
log.error("conandata(%s) 和 conanfile(%s) 都没有找到", conandata, conanfile)
|
|
233
|
+
else:
|
|
215
234
|
conan_remote_list = tools.get_conan_remote_list(self.remote)
|
|
216
235
|
tools.download_conan_recipes(conan_version, conan_remote_list)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
return
|
|
236
|
+
path_cmd = f"conan cache path {conan_version}"
|
|
237
|
+
path_cmd_info = Tools().run_command(path_cmd, capture_output=True)
|
|
220
238
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
239
|
+
version_split = re.split(r'[/, @]', conan_version)
|
|
240
|
+
conan_dir = path_cmd_info.stdout.strip()
|
|
241
|
+
conandata = os.path.join(conan_dir, 'conandata.yml')
|
|
242
|
+
if os.path.exists(conandata):
|
|
243
|
+
self.__update_component_code_by_conandata(version_split[0], version_split[1], conandata)
|
|
244
|
+
return
|
|
226
245
|
|
|
227
|
-
|
|
246
|
+
log.error("conandata(%s) 没有找到", conandata)
|
|
228
247
|
log.info("更新组件 %s 代码到 %s 失败, 无法获取到 conan 信息", component_name,
|
|
229
248
|
conan_version)
|
|
230
249
|
except Exception as exp:
|
|
231
250
|
log.error("工作状态错误: %s", exp)
|
|
232
|
-
log.error("更新组件代码到 %s 失败", conan_version)
|
|
251
|
+
log.error("更新组件代码到 %s 失败", conan_version)
|
|
@@ -26,6 +26,20 @@ class InstallWorkflow:
|
|
|
26
26
|
_workflows: Dict[str, List[str]] = {}
|
|
27
27
|
search_paths: List[Path] = [Path(__file__).resolve().parent / install_consts.PLUGIN_INSTALL_PLAN_PATH]
|
|
28
28
|
|
|
29
|
+
@staticmethod
|
|
30
|
+
def tool_exists(tool_name: str, install_type: str):
|
|
31
|
+
try:
|
|
32
|
+
if install_type == "pip":
|
|
33
|
+
ret = tools.run_command(f"pip show {tool_name}", capture_output=True, ignore_error=True)
|
|
34
|
+
return ret.returncode == 0
|
|
35
|
+
if install_type == "apt":
|
|
36
|
+
ret = tools.run_command(f"dpkg -s {tool_name}", capture_output=True, ignore_error=True)
|
|
37
|
+
return "Status: install ok installed" in ret.stdout
|
|
38
|
+
except Exception as e:
|
|
39
|
+
tools.log.info(f"校验工具存在性时报错:{str(e)}")
|
|
40
|
+
|
|
41
|
+
return False
|
|
42
|
+
|
|
29
43
|
@classmethod
|
|
30
44
|
def discover_workflows(cls):
|
|
31
45
|
for path in cls.search_paths:
|
|
@@ -35,7 +49,8 @@ class InstallWorkflow:
|
|
|
35
49
|
|
|
36
50
|
for yml_file in path.glob("*.yml"):
|
|
37
51
|
with open(yml_file) as conf:
|
|
38
|
-
|
|
52
|
+
file_content = yaml.safe_load(conf)
|
|
53
|
+
cls._register_workflows(yml_file=yml_file, file_content=file_content)
|
|
39
54
|
|
|
40
55
|
@classmethod
|
|
41
56
|
def parse(cls, name: str) -> List[str]:
|
|
@@ -51,4 +66,15 @@ class InstallWorkflow:
|
|
|
51
66
|
|
|
52
67
|
@classmethod
|
|
53
68
|
def get_all_plans(cls):
|
|
54
|
-
return cls._workflows.keys()
|
|
69
|
+
return cls._workflows.keys()
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def _register_workflows(cls, yml_file, file_content):
|
|
73
|
+
plans = file_content.get(install_consts.PLAN_STEPS, [])
|
|
74
|
+
for plan in plans:
|
|
75
|
+
inst_type = plan.get(install_consts.PLAN_INSTALL_TYPE)
|
|
76
|
+
if not inst_type:
|
|
77
|
+
raise ValueError(f"未配置 {yml_file}/{install_consts.PLAN_STEPS}/{install_consts.PLAN_INSTALL_TYPE}")
|
|
78
|
+
package_name = plan.get(install_consts.PLAN_PACKAGE_NAME)
|
|
79
|
+
if cls.tool_exists(package_name, inst_type): # 未安装的工具不加入_workflows
|
|
80
|
+
cls._workflows[yml_file.stem] = file_content
|
bmcgo/utils/merge_csr.py
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
# Copyright (c) 2025 Huawei Technologies Co., Ltd.
|
|
3
|
+
# openUBMC is licensed under Mulan PSL v2.
|
|
4
|
+
# You can use this software according to the terms and conditions of the Mulan PSL v2.
|
|
5
|
+
# You may obtain a copy of Mulan PSL v2 at:
|
|
6
|
+
# http://license.coscl.org.cn/MulanPSL2
|
|
7
|
+
# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
|
8
|
+
# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
|
9
|
+
# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
|
10
|
+
# See the Mulan PSL v2 for more details.
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import re
|
|
14
|
+
import os
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from bmcgo.utils.tools import Tools
|
|
17
|
+
|
|
18
|
+
tools = Tools("bmcgo_config")
|
|
19
|
+
log = tools.log
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Merger():
|
|
23
|
+
def __init__(self, output_dir):
|
|
24
|
+
self.output_dir = output_dir
|
|
25
|
+
self.special_suffix = ("_basic_info", "_mgmt_model", "_version")
|
|
26
|
+
self.tmp_dir = None
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def remove_json5_comments(text):
|
|
30
|
+
pattern = r"""
|
|
31
|
+
("(?:\\.|[^"\\])*"
|
|
32
|
+
|'(?:\\.|[^'\\])*'
|
|
33
|
+
)
|
|
34
|
+
|(/\*[\s\S]*?\*/
|
|
35
|
+
|//[^\n]*
|
|
36
|
+
)
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def replacer(match):
|
|
40
|
+
if match.group(1):
|
|
41
|
+
return match.group(1)
|
|
42
|
+
else:
|
|
43
|
+
return ''
|
|
44
|
+
return json.loads(re.sub(pattern, replacer, text, flags=re.VERBOSE))
|
|
45
|
+
|
|
46
|
+
def merge_json(self, dict1, dict2, is_top_level=True, path=None):
|
|
47
|
+
if path is None:
|
|
48
|
+
path = []
|
|
49
|
+
if not isinstance(dict1, dict) or not isinstance(dict2, dict):
|
|
50
|
+
if dict1 != dict2:
|
|
51
|
+
raise ValueError(f"字段冲突,路径{'/'.join(path)}的值不一致:{dict1} vs {dict2}")
|
|
52
|
+
return dict1
|
|
53
|
+
for key, value in dict2.items():
|
|
54
|
+
if key in dict1:
|
|
55
|
+
if is_top_level:
|
|
56
|
+
_ = self.merge_json(dict1[key], value, is_top_level=False, path=path + [key])
|
|
57
|
+
else:
|
|
58
|
+
raise ValueError(f"字段冲突:路径 {'/'.join(path + [key])} 在多个文件中重复出现")
|
|
59
|
+
else:
|
|
60
|
+
dict1[key] = value
|
|
61
|
+
return dict1
|
|
62
|
+
|
|
63
|
+
def generate_csr(self, filelist, csr_path):
|
|
64
|
+
log.info(f"正在创建临时csr文件")
|
|
65
|
+
merged = {}
|
|
66
|
+
for file_path in filelist:
|
|
67
|
+
p = Path(file_path)
|
|
68
|
+
if not p.exists():
|
|
69
|
+
raise FileNotFoundError(f"文件不存在:{file_path}")
|
|
70
|
+
with open(p, "r", encoding="utf-8") as fp:
|
|
71
|
+
content = self.remove_json5_comments(fp.read())
|
|
72
|
+
merged = self.merge_json(merged, content)
|
|
73
|
+
with open(csr_path, "w") as f:
|
|
74
|
+
json.dump(merged, f)
|
|
75
|
+
log.info(f"临时文件创建成功")
|
|
76
|
+
|
|
77
|
+
def update_tmp_dir(self, tmp_dir):
|
|
78
|
+
self.tmp_dir = tmp_dir
|
|
79
|
+
|
|
80
|
+
def get_single_csr(self, csr_path):
|
|
81
|
+
if os.path.exists(csr_path):
|
|
82
|
+
return csr_path
|
|
83
|
+
name, ext = os.path.splitext(csr_path)
|
|
84
|
+
modularize_csr = []
|
|
85
|
+
for suffix in self.special_suffix:
|
|
86
|
+
new_path = f"{name}{suffix}{ext}"
|
|
87
|
+
if not os.path.exists(new_path):
|
|
88
|
+
raise FileNotFoundError(f"路径 {new_path} 不存在")
|
|
89
|
+
modularize_csr.append(new_path)
|
|
90
|
+
csr_path = os.path.join(self.tmp_dir, os.path.basename(csr_path))
|
|
91
|
+
self.generate_csr(modularize_csr, csr_path)
|
|
92
|
+
return csr_path
|
|
93
|
+
|
|
94
|
+
def get_multi_files(self, dir_path):
|
|
95
|
+
srs = list(dir_path.glob("*.sr"))
|
|
96
|
+
stem_to_path = {p.stem: p for p in srs}
|
|
97
|
+
special_by_base = {}
|
|
98
|
+
for p in srs:
|
|
99
|
+
stem = p.stem
|
|
100
|
+
for suf in self.special_suffix:
|
|
101
|
+
if not stem.endswith(suf):
|
|
102
|
+
continue
|
|
103
|
+
base = stem.removesuffix(suf)
|
|
104
|
+
if base:
|
|
105
|
+
special_by_base.setdefault(base, {})[suf] = p
|
|
106
|
+
break
|
|
107
|
+
to_skip = set()
|
|
108
|
+
to_create = []
|
|
109
|
+
for base, mod_path in special_by_base.items():
|
|
110
|
+
if base in stem_to_path:
|
|
111
|
+
to_skip.update(mod_path.values())
|
|
112
|
+
else:
|
|
113
|
+
if not all(s in mod_path for s in self.special_suffix):
|
|
114
|
+
continue
|
|
115
|
+
plain = dir_path / f"{base}.sr"
|
|
116
|
+
if not plain.exists():
|
|
117
|
+
plain = os.path.join(self.tmp_dir, f"{base}.sr")
|
|
118
|
+
self.generate_csr([mod_path[s] for s in self.special_suffix], plain)
|
|
119
|
+
to_create.append(plain)
|
|
120
|
+
to_skip.update(mod_path.values())
|
|
121
|
+
res = [p for p in srs if p not in to_skip]
|
|
122
|
+
res.extend(to_create)
|
|
123
|
+
log.info(res)
|
|
124
|
+
return res
|