lbkit 0.5.9__tar.gz → 0.5.11__tar.gz

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.
Files changed (53) hide show
  1. {lbkit-0.5.9/lbkit.egg-info → lbkit-0.5.11}/PKG-INFO +2 -1
  2. lbkit-0.5.11/lbkit/__init__.py +2 -0
  3. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/cli.py +3 -0
  4. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/idf_interface.py +2 -4
  5. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/component/build.py +8 -9
  6. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/errors.py +9 -0
  7. lbkit-0.5.11/lbkit/integration/build_image.py +89 -0
  8. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/integration/build_manifest.py +27 -12
  9. lbkit-0.5.11/lbkit/integration/build_prepare.py +56 -0
  10. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/integration/build_rootfs.py +34 -48
  11. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/integration/config.py +4 -2
  12. lbkit-0.5.11/lbkit/integration/task.py +63 -0
  13. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/misc.py +2 -1
  14. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/tools.py +54 -6
  15. {lbkit-0.5.9 → lbkit-0.5.11/lbkit.egg-info}/PKG-INFO +2 -1
  16. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit.egg-info/SOURCES.txt +1 -0
  17. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit.egg-info/requires.txt +1 -0
  18. {lbkit-0.5.9 → lbkit-0.5.11}/setup.py +1 -1
  19. lbkit-0.5.9/lbkit/__init__.py +0 -2
  20. lbkit-0.5.9/lbkit/integration/build_prepare.py +0 -89
  21. lbkit-0.5.9/lbkit/integration/task.py +0 -126
  22. {lbkit-0.5.9 → lbkit-0.5.11}/AUTHORS +0 -0
  23. {lbkit-0.5.9 → lbkit-0.5.11}/LICENSE +0 -0
  24. {lbkit-0.5.9 → lbkit-0.5.11}/MANIFEST.in +0 -0
  25. {lbkit-0.5.9 → lbkit-0.5.11}/README.md +0 -0
  26. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/ci_robot/__init__.py +0 -0
  27. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/ci_robot/gitee.py +0 -0
  28. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/__init__.py +0 -0
  29. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/codegen.py +0 -0
  30. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/ctype_defination.py +0 -0
  31. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/renderer.py +0 -0
  32. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/template/client.c.mako +0 -0
  33. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/template/client.h.mako +0 -0
  34. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/template/interface.c.mako +0 -0
  35. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/template/interface.introspect.xml.mako +0 -0
  36. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/template/public.c.mako +0 -0
  37. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/template/public.h.mako +0 -0
  38. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/template/server.c.mako +0 -0
  39. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/codegen/template/server.h.mako +0 -0
  40. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/component/__init__.py +0 -0
  41. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/component/arg_parser.py +0 -0
  42. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/component/template/conanbase.mako +0 -0
  43. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/component/template/deploy.mako +0 -0
  44. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/component/test.py +0 -0
  45. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/helper.py +0 -0
  46. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/integration/__init__.py +0 -0
  47. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/integration/template/conanfile.py.mako +0 -0
  48. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/lbkit.py +0 -0
  49. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit/log.py +0 -0
  50. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit.egg-info/dependency_links.txt +0 -0
  51. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit.egg-info/entry_points.txt +0 -0
  52. {lbkit-0.5.9 → lbkit-0.5.11}/lbkit.egg-info/top_level.txt +0 -0
  53. {lbkit-0.5.9 → lbkit-0.5.11}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lbkit
3
- Version: 0.5.9
3
+ Version: 0.5.11
4
4
  Summary: Tools provided by litebmc.com
5
5
  Home-page: https://www.litebmc.com
6
6
  Author: xuhj@litebmc.com
@@ -20,5 +20,6 @@ Requires-Dist: conan==2.1
20
20
  Requires-Dist: requests
21
21
  Requires-Dist: gitpython
22
22
  Requires-Dist: inflection
23
+ Requires-Dist: meson==1.4.0
23
24
 
24
25
  build and code generate tools
@@ -0,0 +1,2 @@
1
+
2
+ __version__ = '0.5.11'
@@ -13,6 +13,7 @@ from lbkit.component.test import TestComponent
13
13
  from lbkit.integration.build_manifest import BuildManifest
14
14
  from lbkit.integration.build_rootfs import BuildRootfs
15
15
  from lbkit.integration.build_prepare import BuildPrepare
16
+ from lbkit.integration.build_image import BuildImage
16
17
  from lbkit.component.arg_parser import ArgParser
17
18
  from lbkit.integration.config import Config
18
19
  from lbkit.ci_robot.gitee import Gitee
@@ -93,6 +94,8 @@ class Command(object):
93
94
  build.run()
94
95
  build = BuildRootfs(cfg, "create_rootfs")
95
96
  build.run()
97
+ build = BuildImage(cfg, "create_image")
98
+ build.run()
96
99
 
97
100
  def gitee(self, *args):
98
101
  """
@@ -5,7 +5,7 @@ import hashlib
5
5
  from lbkit.log import Logger
6
6
  from lbkit.codegen.renderer import Renderer
7
7
  from lbkit.codegen.ctype_defination import CTYPE_OBJS, RefObjArrayValidator, RefObjValidator
8
- from lbkit.misc import validate_yml_with_json_schema
8
+ from lbkit.misc import load_yml_with_json_schema_validate
9
9
 
10
10
  log = Logger("gen_interface")
11
11
 
@@ -786,10 +786,8 @@ class IdfInterface(IdfInterfaceBase):
786
786
  def load_elements(self):
787
787
  # 使用schema校验数据,确保IDF文件符合格式要求,减少程序处理过程中的异常处理
788
788
  # 验证失败时抛异常,此处不用处理,由外层处理
789
- validate_yml_with_json_schema(self.file, "/usr/share/litebmc/schema/idf.v1.json")
789
+ idf = load_yml_with_json_schema_validate(self.file, "/usr/share/litebmc/schema/idf.v1.json")
790
790
  log.info(f"validate {self.file} successfully")
791
- with open(self.file, "r") as fp:
792
- idf = yaml.load(fp, yaml.FullLoader)
793
791
  self.version = idf.get("version")
794
792
  self.description = idf.get("description", "")
795
793
  # 别名
@@ -10,7 +10,7 @@ from jsonschema import validate, ValidationError
10
10
  from git import Repo
11
11
  from git.exc import InvalidGitRepositoryError
12
12
  from mako.lookup import TemplateLookup
13
- from lbkit.misc import Color, validate_yml_with_json_schema, get_json_schema_file, load_json_schema
13
+ from lbkit.misc import Color, load_yml_with_json_schema_validate, get_json_schema_file, load_json_schema
14
14
  from lbkit import errors
15
15
  from lbkit.codegen.codegen import CodeGen
16
16
  from lbkit.tools import Tools
@@ -32,7 +32,7 @@ class DeployComponent():
32
32
  return
33
33
  cmd = f"conan cache path {self.package_ref}:{self.package_id}"
34
34
  package_folder = tools.run(cmd).stdout.strip()
35
- log.info(f">>copy {package_folder} to {self.rootfs_dir}")
35
+ log.info(f">>>> deploy {self.package_ref}")
36
36
  cmd = f"cp -rT {package_folder}/ {self.rootfs_dir}"
37
37
  tools.exec(cmd)
38
38
 
@@ -98,13 +98,9 @@ class BuildComponent():
98
98
  if not os.path.isfile(package_yml):
99
99
  raise FileNotFoundError("metadata/package.yml文件不存在")
100
100
  # 验证失败时抛异常,此处不用处理,由外层处理
101
- validate_yml_with_json_schema(package_yml, "/usr/share/litebmc/schema/cdf.v1.json")
101
+ pkg = load_yml_with_json_schema_validate(package_yml, "/usr/share/litebmc/schema/cdf.v1.json")
102
102
  log.info(f"validate {package_yml} successfully")
103
103
 
104
- # 加载package.yml文件
105
- fp = open(package_yml, "r")
106
- pkg = yaml.safe_load(fp)
107
- fp.close()
108
104
  self.user = pkg.get("user")
109
105
  if self.user is None:
110
106
  raise errors.PackageConfigException("metadata/package.yml未正确配置user字段")
@@ -193,13 +189,16 @@ class BuildComponent():
193
189
  os.chdir(dir)
194
190
 
195
191
  cmd = self.base_cmd.replace(self.package, "")
196
- cmd = f"conan graph info . {cmd} --build=missing -f json --filter=ref --filter=package_id"
192
+ cmd = f"conan graph info . {cmd} --build=missing -f json --filter=ref --filter=package_id --filter=context"
197
193
  res = tools.run(cmd).stdout.strip()
198
194
  graph = json.loads(res)
199
195
  nodes = graph.get("graph", {}).get("nodes", {})
200
196
  for id, info in nodes.items():
201
197
  ref = info.get("ref")
202
198
  id = info.get("package_id")
199
+ context = info.get("context")
200
+ if context != "host":
201
+ continue
203
202
  dep = DeployComponent(ref, id, self.rootfs_dir)
204
203
  dep.run()
205
204
 
@@ -264,7 +263,7 @@ class BuildComponent():
264
263
  continue
265
264
  log.info(f"start validate {file} with schema {schema_file}")
266
265
  # 验证全局odf验证
267
- validate_yml_with_json_schema(file, schema_file)
266
+ load_yml_with_json_schema_validate(file, schema_file)
268
267
  odf_ok = self._validate_odf_file(file)
269
268
  if not odf_ok:
270
269
  ok = False
@@ -79,3 +79,12 @@ class OdfValidateException(Exception):
79
79
 
80
80
  def __str__(self):
81
81
  return super(OdfValidateException, self).__str__()
82
+
83
+ class DigestNotMatchError(OSError):
84
+ """Raised when source and destination are the same file."""
85
+
86
+ class ExtractRootfsTarFileError(OSError):
87
+ """Raised when extract rootfs.tar.gz failed."""
88
+
89
+ class PermissionFormatError(OSError):
90
+ """permission.ini with format."""
@@ -0,0 +1,89 @@
1
+ """环境准备"""
2
+ import os
3
+ import tempfile
4
+ import tarfile
5
+ import shutil
6
+ from lbkit.integration.config import Config
7
+ from lbkit.integration.task import Task
8
+ from lbkit.log import Logger
9
+ from lbkit import errors
10
+
11
+ log = Logger("build_image")
12
+
13
+ src_cwd = os.path.split(os.path.realpath(__file__))[0]
14
+
15
+ class BuildImage(Task):
16
+ def download_uboot(self, img_file):
17
+ tar_file = os.path.join(self.config.download_path, "uboot.tar.gz")
18
+
19
+ url = self.get_manifest_config("components/uboot/url")
20
+ sha256 = None
21
+ if not self.config.not_check_download_sha:
22
+ sha256 = self.get_manifest_config("components/uboot/sha256")
23
+ self.tools.download(url, tar_file, sha256)
24
+ tar = tarfile.open(tar_file)
25
+ members = tar.getmembers()
26
+ for member in members:
27
+ if not member.isfile():
28
+ continue
29
+ if member.name != "uboot.bin" and member.name != "u-boot.bin":
30
+ continue
31
+ io = tar.extractfile(member)
32
+ fp = open(img_file, "wb+")
33
+ while True:
34
+ buf = io.read(65536)
35
+ if len(buf) == 0:
36
+ break
37
+ fp.write(buf)
38
+ fp.close()
39
+ return
40
+ raise errors.ExtractRootfsTarFileError("Extract failed, the u-boot.bin can't be found in " + url)
41
+
42
+ def download_kernel(self, img_file):
43
+ tar_file = os.path.join(self.config.download_path, "kernel.tar.gz")
44
+
45
+ url = self.get_manifest_config("components/kernel/url")
46
+ sha256 = None
47
+ if not self.config.not_check_download_sha:
48
+ sha256 = self.get_manifest_config("components/kernel/sha256")
49
+ self.tools.download(url, tar_file, sha256)
50
+ tar = tarfile.open(tar_file)
51
+ members = tar.getmembers()
52
+ for member in members:
53
+ if not member.isfile():
54
+ continue
55
+ if member.name != "Image":
56
+ continue
57
+ io = tar.extractfile(member)
58
+ fp = open(img_file, "wb+")
59
+ while True:
60
+ buf = io.read(65536)
61
+ if len(buf) == 0:
62
+ break
63
+ fp.write(buf)
64
+ fp.close()
65
+ return
66
+ raise errors.ExtractRootfsTarFileError("Extract failed, the Image can't be found in " + url)
67
+
68
+ def run(self):
69
+ """任务入口"""
70
+ """检查manifest文件是否满足schema格式描述"""
71
+ os.chdir(self.config.output_path)
72
+ self.download_uboot("u-boot.bin")
73
+ self.download_kernel("Image")
74
+ cmd = "qemu-system-aarch64 -M virt -cpu cortex-a57 -M virt,dumpdtb=virt.dtb"
75
+ self.exec(cmd)
76
+
77
+ cmd = 'lbpack_emmc.sh ./Image ./virt.dtb ./rootfs.img ./qemu.img'
78
+ self.exec(cmd)
79
+ cmd = 'cp /usr/share/litebmc/qemu.conf ./qemu.conf'
80
+ self.exec(cmd)
81
+ output_img = os.path.join(self.config.output_path, "litebmc_qemu.tar.gz")
82
+ cmd = f'tar -czf {output_img} -C . qemu.img u-boot.bin qemu.conf'
83
+ self.exec(cmd)
84
+ log.success(f"Create litebmc image {output_img} successfully")
85
+
86
+ if __name__ == "__main__":
87
+ config = Config()
88
+ build = BuildImage(config)
89
+ build.run()
@@ -1,6 +1,7 @@
1
1
  """应用构建任务"""
2
2
  import os
3
3
  import shutil
4
+ import json
4
5
  from mako.lookup import TemplateLookup
5
6
  from lbkit.integration.config import Config
6
7
  from lbkit.integration.task import Task
@@ -15,14 +16,13 @@ class ManifestValidateError(OSError):
15
16
  src_cwd = os.path.split(os.path.realpath(__file__))[0]
16
17
 
17
18
  class BuildManifest(Task):
18
- """根据产品配置构建所有app并将应用安装到self.config.conan_install目录"""
19
- def __init__(self, cfg: Config):
20
- super().__init__(cfg)
19
+ """根据产品配置构建所有app,记录待安装应用路径到self.config.conan_install路径"""
20
+ def __init__(self, cfg: Config, name: str):
21
+ super().__init__(cfg, name)
21
22
  self.conan_build = os.path.join(self.config.temp_path, "conan")
22
- shutil.rmtree(self.conan_build, ignore_errors=True)
23
+ if os.path.isdir(self.conan_build):
24
+ shutil.rmtree(self.conan_build)
23
25
  os.makedirs(self.conan_build)
24
- shutil.rmtree(self.config.conan_install, ignore_errors=True)
25
- os.makedirs(self.config.conan_install)
26
26
  if self.config.build_debug:
27
27
  self.conan_settings = " -s build_type=Debug"
28
28
  else:
@@ -30,11 +30,29 @@ class BuildManifest(Task):
30
30
  self.common_args = "-r " + self.config.remote
31
31
  self.common_args += " -pr:b {} -pr:h {}".format(self.config.profile_build, self.config.profile_host)
32
32
 
33
+ def deploy(self):
34
+ cmd = f"conan graph info . {self.common_args} --build=missing -f json --filter=ref --filter=package_id"
35
+ cmd += f" --filter=context"
36
+ res = self.tools.run(cmd).stdout.strip()
37
+ graph = json.loads(res)
38
+ nodes = graph.get("graph", {}).get("nodes", {})
39
+ for id, info in nodes.items():
40
+ ref = info.get("ref")
41
+ id = info.get("package_id")
42
+ context = info.get("context")
43
+ if context != "host":
44
+ continue
45
+ cmd = f"conan cache path {ref}:{id}"
46
+ package_folder = self.tools.run(cmd).stdout.strip()
47
+ self.config.conan_install.append(package_folder)
48
+
33
49
  def build_litebmc(self):
34
50
  """构建产品conan包"""
35
51
  log.info("build litebmc")
36
52
 
37
53
  manifest = self.load_manifest()
54
+ hook_name = "hook.prepare_manifest"
55
+ self.do_hook(hook_name)
38
56
  # 使用模板生成litebmc组件的配置
39
57
  lookup = TemplateLookup(directories=os.path.join(src_cwd, "template"))
40
58
  template = lookup.get_template("conanfile.py.mako")
@@ -49,7 +67,7 @@ class BuildManifest(Task):
49
67
 
50
68
  # 安装所有应用
51
69
  if self.config.from_source:
52
- cmd = "conan install . --build " + self.common_args
70
+ cmd = "conan install . --build=\"*\" " + self.common_args
53
71
  else:
54
72
  cmd = "conan install . --build=missing " + self.common_args
55
73
  cmd = cmd + self.conan_settings
@@ -59,10 +77,7 @@ class BuildManifest(Task):
59
77
  cmd = cmd + self.conan_settings
60
78
  self.exec(cmd, verbose=True)
61
79
  # 部署应用到self.config.conan_install
62
- log.info("install litebmc to %s", self.config.conan_install)
63
- cmd = "conan install . {} -d direct_deploy --deployer-folder={}".format(self.common_args, self.config.conan_install)
64
- cmd = cmd + self.conan_settings
65
- self.exec(cmd, verbose=self.config.verbose)
80
+ self.deploy()
66
81
 
67
82
  def run(self):
68
83
  """任务入口"""
@@ -71,4 +86,4 @@ class BuildManifest(Task):
71
86
  if __name__ == "__main__":
72
87
  config = Config()
73
88
  build = BuildManifest(config)
74
- build.run()
89
+ build.run()
@@ -0,0 +1,56 @@
1
+ """环境准备"""
2
+ import os
3
+ import shutil
4
+ import jinja2
5
+ import configparser
6
+ from lbkit.integration.config import Config
7
+ from lbkit.integration.task import Task
8
+ from lbkit.log import Logger
9
+
10
+ log = Logger("product_prepare")
11
+
12
+
13
+ class ManifestValidateError(OSError):
14
+ """Raised when validation manifest.yml failed."""
15
+
16
+ src_cwd = os.path.split(os.path.realpath(__file__))[0]
17
+
18
+ class BuildPrepare(Task):
19
+ def load_conan_profile(self):
20
+ profile = self.get_manifest_config("metadata/profile")
21
+ if not os.path.isfile(profile):
22
+ raise FileNotFoundError(f"profile {profile} not found")
23
+ log.info("Copy profile %s", profile)
24
+ profiles_dir = os.path.expanduser("~/.conan2/profiles")
25
+ dst_profile = os.path.join(profiles_dir, os.path.basename(profile))
26
+ if os.path.isdir(profiles_dir):
27
+ shutil.copyfile(profile, dst_profile, follow_symlinks=False)
28
+
29
+ with open(dst_profile, "r") as fp:
30
+ profile_data = jinja2.Template(fp.read()).render()
31
+ parser = configparser.ConfigParser()
32
+ parser.read_string(profile_data)
33
+ strip = "strip"
34
+ if parser.has_option("buildenv", "STRIP"):
35
+ strip = parser.get("buildenv", "STRIP")
36
+ path = ""
37
+ if parser.has_option("buildenv", "PATH+"):
38
+ path = parser.get("buildenv", "PATH+")
39
+ if path.startswith("(path)"):
40
+ path = path[6:]
41
+ elif parser.has_option("buildenv", "PATH"):
42
+ path = parser.get("buildenv", "PATH")
43
+ if path.startswith("(path)"):
44
+ path = path[6:]
45
+ self.config.strip = os.path.join(path, strip)
46
+
47
+ def run(self):
48
+ """任务入口"""
49
+ """检查manifest文件是否满足schema格式描述"""
50
+ self.load_manifest()
51
+ self.load_conan_profile()
52
+
53
+ if __name__ == "__main__":
54
+ config = Config()
55
+ build = BuildPrepare(config)
56
+ build.run()
@@ -3,20 +3,15 @@ import os
3
3
  import shutil
4
4
  import tarfile
5
5
  from lbkit.integration.config import Config
6
- from lbkit.integration.task import Task, DigestNotMatchError
6
+ from lbkit.integration.task import Task
7
7
  from lbkit.log import Logger
8
+ from lbkit import errors
8
9
 
9
10
  log = Logger("build_rootfs")
10
11
 
11
12
  src_cwd = os.path.split(os.path.realpath(__file__))[0]
12
13
  IMG_FILE = "rootfs.img"
13
14
 
14
- class ExtractRootfsTarFileError(OSError):
15
- """Raised when extract rootfs.tar.gz failed."""
16
-
17
- class PermissionFormatError(OSError):
18
- """permission.ini with format."""
19
-
20
15
  class BuildRootfs(Task):
21
16
  """构建rootfs镜像"""
22
17
  def do_permission(self, per_file: str):
@@ -32,14 +27,14 @@ class BuildRootfs(Task):
32
27
  log.debug("Permission line: %s", line)
33
28
  chunk = line.split()
34
29
  if len(chunk) < 5:
35
- raise PermissionFormatError(f"Permission format with error, line: {line}")
30
+ raise errors.PermissionFormatError(f"Permission format with error, line: {line}")
36
31
  if not chunk[0].startswith("/"):
37
- raise PermissionFormatError(f"Permission file error, must begin with \"/\", get: {chunk[0]}")
32
+ raise errors.PermissionFormatError(f"Permission file error, must begin with \"/\", get: {chunk[0]}")
38
33
  if not chunk[3].isnumeric() or not chunk[4].isnumeric():
39
- raise PermissionFormatError(f"Permission uid or gid error, must is numeric, uid({chunk[3]}), gid({chunk[4]})")
34
+ raise errors.PermissionFormatError(f"Permission uid or gid error, must is numeric, uid({chunk[3]}), gid({chunk[4]})")
40
35
  chunk[0] = chunk[0].lstrip("/")
41
36
  if chunk[1] != "f" and chunk[1] != "d" and chunk[1] != "s" and chunk[1] != "r":
42
- raise PermissionFormatError(f"Permission type error, only support 'f' 's' 'd' 'r', get({chunk[1]}), ignore")
37
+ raise errors.PermissionFormatError(f"Permission type error, only support 'f' 's' 'd' 'r', get({chunk[1]}), ignore")
43
38
  if chunk[1] == "d" and not os.path.isdir(chunk[0]):
44
39
  log.error("Permission error, %s is not directory", chunk[0])
45
40
  if chunk[1] == "s" and not os.path.islink(chunk[0]):
@@ -69,22 +64,10 @@ class BuildRootfs(Task):
69
64
  rootfs_tar = "rootfs.tar.gz"
70
65
 
71
66
  url = self.get_manifest_config("components/rootfs/url")
72
- sha256 = self.get_manifest_config("components/rootfs/sha256")
73
- digest = None
74
- if os.path.isfile(rootfs_tar):
75
- digest = self.file_digest_sha256(rootfs_tar)
76
- if digest != sha256:
77
- art_user = os.environ.get("ART_USER", None)
78
- art_token = os.environ.get("ART_TOKEN", None)
79
- if art_user is None or art_token is None:
80
- log.info("The environment variables ART_USER and ART_TOKEN are not set, the file is downloaded directily")
81
- cmd = f"curl -fL \"{url}\" -o {rootfs_tar}"
82
- else:
83
- cmd = f"curl -fL -u {art_user}:{art_token} \"{url}\" -o {rootfs_tar}"
84
- self.exec(cmd, verbose=True, sensitive=True)
85
- digest = self.file_digest_sha256(rootfs_tar)
86
- if digest != sha256:
87
- raise DigestNotMatchError(f"File {rootfs_tar} with sha256 error, need: {sha256}, get: {digest}")
67
+ sha256 = None
68
+ if not self.config.not_check_download_sha:
69
+ sha256 = self.get_manifest_config("components/rootfs/sha256")
70
+ self.tools.download(url, rootfs_tar, sha256)
88
71
  tar = tarfile.open(rootfs_tar)
89
72
  members = tar.getmembers()
90
73
  for member in members:
@@ -97,11 +80,11 @@ class BuildRootfs(Task):
97
80
  while True:
98
81
  buf = io.read(65536)
99
82
  if len(buf) == 0:
100
- break;
83
+ break
101
84
  fp.write(buf)
102
85
  fp.close()
103
86
  return
104
- raise ExtractRootfsTarFileError("Extract failed, the rootfs.img can't be found in rootfs.tar")
87
+ raise errors.ExtractRootfsTarFileError("Extract failed, the rootfs.img can't be found in rootfs.tar")
105
88
 
106
89
  def merge_rootfs(self, rootfs_img):
107
90
  """将产品依赖的所有组件安装到rootfs镜像中"""
@@ -117,13 +100,9 @@ class BuildRootfs(Task):
117
100
  # 切换到rootfs挂载目录
118
101
  os.chdir(mnt_path)
119
102
  log.info("Copy customization rootfs......")
120
- deploy_dir = os.path.join(self.config.conan_install, "direct_deploy")
121
- for component in os.listdir(deploy_dir):
122
- src_dir = os.path.join(deploy_dir, component)
123
- if not os.path.isdir(src_dir):
124
- continue
103
+ for src_dir in self.config.conan_install:
125
104
  log.info("copy %s to %s", src_dir, mnt_path)
126
- cmd = f"rsync -auHK {src_dir}/ {mnt_path}"
105
+ cmd = f"rsync -aHK {src_dir}/ {mnt_path}"
127
106
  self.exec(cmd)
128
107
  per_file = os.path.join(src_dir, "permissions")
129
108
  self.do_permission(per_file)
@@ -131,7 +110,7 @@ class BuildRootfs(Task):
131
110
  # copy product self-owned rootfs
132
111
  product_rootfs = os.path.join(self.config.work_dir, "rootfs")
133
112
  if os.path.isdir(product_rootfs):
134
- cmd = f"rsync -auKH {product_rootfs}/ {mnt_path}"
113
+ cmd = f"rsync -aKH {product_rootfs}/ {mnt_path}"
135
114
  self.exec(cmd)
136
115
  per_file = os.path.join(product_rootfs, "permissions")
137
116
  self.do_permission(per_file)
@@ -142,18 +121,21 @@ class BuildRootfs(Task):
142
121
  self.do_hook(hook_name)
143
122
 
144
123
  # 清理冗余文件
145
- cmd = "rm -rf " + os.path.join(self.config.mnt_path, "include")
146
- self.exec(cmd)
124
+ inc_dir = os.path.join(self.config.mnt_path, "include")
125
+ if os.path.isdir(inc_dir):
126
+ cmd = "rm -rf " + inc_dir
127
+ self.exec(cmd)
147
128
  cmd = "rm " + os.path.join(self.config.mnt_path, "permissions")
148
129
  self.exec(cmd)
149
130
 
150
131
  # 清理冗余静态文件
151
- cmd = "find " + os.path.join(self.config.mnt_path, "include") + " -name *.a -type f | xargs rm -f"
152
- self.exec(cmd)
132
+ cmds = ["find " + self.config.mnt_path + " -name *.a -type f", "xargs -i{} rm {} -f"]
133
+ self.pipe(cmds)
153
134
 
154
135
  # 按manifest配置的大小调整rootfs
155
136
  strip = self.get_manifest_config("metadata/strip")
156
137
  if strip:
138
+ log.info("Start strip files")
157
139
  file_list = os.path.join(self.config.temp_path, "strip.filelist")
158
140
  unstrip_regex = [
159
141
  "*.yaml$",
@@ -172,15 +154,19 @@ class BuildRootfs(Task):
172
154
  "*.conf$"]
173
155
  cmds = ["find " + self.config.mnt_path + " -type f"]
174
156
  for unstrip in unstrip_regex:
175
- cmds.append[f"grep -vE \"{unstrip}\""]
157
+ cmds.append(f"grep -vE {unstrip}")
176
158
  self.pipe(cmds, out_file=file_list)
177
159
 
178
160
  cmds = [f"cat {file_list}", "xargs file", "grep 'not stripped'", "awk -F':' '{{print $1}}'"]
179
- cmds.append[f"xargs {self.config.strip} -s"]
180
- self.pipe(cmd)
181
-
182
- cmds = [f"find {self.config.mnt_path} -name *.a", "xargs rm"]
183
- self.pipe(cmd)
161
+ cmds.append(f"xargs -i{{}} {self.config.strip} -s {{}}")
162
+ self.pipe(cmds)
163
+
164
+ log.info("remove static libraries")
165
+ cmds = [f"find {self.config.mnt_path} -name *.a", "xargs -i{} rm {}"]
166
+ self.pipe(cmds)
167
+ log.info("remove all .fuse_hiddeng* files")
168
+ cmds = [f"find {self.config.mnt_path} -name .fuse_hidden*", "xargs -i{} rm {}"]
169
+ self.pipe(cmds)
184
170
  self.exec("umount " + mnt_path)
185
171
 
186
172
  def run(self):
@@ -190,8 +176,8 @@ class BuildRootfs(Task):
190
176
  # 任务入口
191
177
  self.download_rootfs(rootfs_img)
192
178
  self.merge_rootfs(rootfs_img)
193
- tgt_img = os.path.join(self.config.output_path, IMG_FILE)
194
- os.rename(rootfs_img, tgt_img)
179
+ os.rename(rootfs_img, self.config.rootfs_img)
180
+ log.success(f"Create image {self.config.rootfs_img} successfully")
195
181
 
196
182
  if __name__ == "__main__":
197
183
  config = Config()
@@ -25,6 +25,7 @@ class Config(object):
25
25
  self.build_debug = args.debug
26
26
  # conan中心仓
27
27
  self.remote = args.remote
28
+ self.not_check_download_sha = args.not_check_download_sha
28
29
 
29
30
  if not os.path.isfile(self.manifest):
30
31
  raise FileNotFoundError(f"File {args.manifest} not exist")
@@ -41,14 +42,14 @@ class Config(object):
41
42
  self.tool_path = os.path.join(self.temp_path, "tools")
42
43
  self.download_path = os.path.join(self.temp_path, "download")
43
44
  self.rootfs_path = os.path.join(self.temp_path, "rootfs")
44
- self.conan_install = os.path.join(self.temp_path, "apps")
45
+ self.conan_install = []
45
46
  self.mnt_path = os.path.join(self.temp_path, "mnt_path")
47
+ self.rootfs_img = os.path.join(self.output_path, "rootfs.img")
46
48
  os.makedirs(self.temp_path, exist_ok=True)
47
49
  os.makedirs(self.tool_path, exist_ok=True)
48
50
  os.makedirs(self.output_path, exist_ok=True)
49
51
  os.makedirs(self.download_path, exist_ok=True)
50
52
  os.makedirs(self.rootfs_path, exist_ok=True)
51
- os.makedirs(self.conan_install, exist_ok=True)
52
53
  # 制作rootfs时需要strip镜像,所以需要单独指定stip路径
53
54
  self.strip = "strip"
54
55
 
@@ -61,5 +62,6 @@ class Config(object):
61
62
  parser.add_argument("-pr", "--profile", help="Apply the specified profile to the host machine", default="litebmc.ini")
62
63
  parser.add_argument("-pr:b", "--profile_build", help="Apply the specified profile to the build machine", default="default")
63
64
  parser.add_argument("-d", "--debug", help="Set the build type to debug", action="store_false")
65
+ parser.add_argument("--not_check_download_sha", help="don't check sha256 of download file", action="store_true")
64
66
  parser.add_argument("-r", "--remote", help="specified conan server", default="litebmc")
65
67
  return parser
@@ -0,0 +1,63 @@
1
+ """任务基础类"""
2
+ import shutil
3
+ import importlib
4
+ import os
5
+ import hashlib
6
+ import requests
7
+ import yaml
8
+ from multiprocessing import Process
9
+ from lbkit.log import Logger
10
+ from lbkit.tools import Tools
11
+ from lbkit.misc import load_yml_with_json_schema_validate
12
+
13
+ from lbkit.integration.config import Config
14
+
15
+ class ManifestValidateError(OSError):
16
+ """Raised when validation manifest.yml failed."""
17
+
18
+ class Task(Process):
19
+ """任务基础类,提供run和install默认实现以及其它基础该当"""
20
+ def __init__(self, config: Config, name: str):
21
+ super().__init__()
22
+ self.log: Logger = Logger("task")
23
+ self.tools: Tools = Tools(name)
24
+ self.config: Config = config
25
+
26
+ def install(self):
27
+ """安装任务"""
28
+ self.log.info("install...........")
29
+
30
+ def exec(self, cmd: str, verbose=False, ignore_error = False, sensitive=False, log_prefix=""):
31
+ return self.tools.exec(cmd, verbose, ignore_error, sensitive, log_prefix)
32
+
33
+ def pipe(self, cmds: list[str], ignore_error=False, out_file = None):
34
+ self.tools.pipe(cmds, ignore_error, out_file)
35
+
36
+ def run(self, cmd, ignore_error=False):
37
+ return self.tools.run(cmd, ignore_error)
38
+
39
+ def do_hook(self, path):
40
+ """执行任务钓子,用于定制化"""
41
+ try:
42
+ module = importlib.import_module(path)
43
+ except TypeError:
44
+ self.log.info("Load module(%s) failed, skip", path)
45
+ return
46
+ self.log.info(module)
47
+ hook = module.TaskHook(self.config, "do_hook")
48
+ hook.run()
49
+
50
+ def get_manifest_config(self, key: str):
51
+ """从manifest中读取配置"""
52
+ with open(self.config.manifest, "r", encoding="utf-8") as fp:
53
+ manifest = yaml.load(fp, yaml.FullLoader)
54
+ keys = key.split("/")
55
+ for k in keys:
56
+ manifest = manifest.get(k, None)
57
+ if manifest is None:
58
+ return None
59
+ return manifest
60
+
61
+ def load_manifest(self):
62
+ """加载manifest.yml并验证schema文件"""
63
+ return load_yml_with_json_schema_validate(self.config.manifest, "/usr/share/litebmc/schema/pdf.v1.json")
@@ -73,7 +73,7 @@ def load_json_schema(schema_file):
73
73
  tmp = fp.read()
74
74
  return json.loads(tmp)
75
75
 
76
- def validate_yml_with_json_schema(yml_file, default_json_schema_file):
76
+ def load_yml_with_json_schema_validate(yml_file, default_json_schema_file):
77
77
  """使用json schema文件校验yml_file配置文件"""
78
78
  schema_file = get_json_schema_file(yml_file, default_json_schema_file)
79
79
  if schema_file is None:
@@ -85,6 +85,7 @@ def validate_yml_with_json_schema(yml_file, default_json_schema_file):
85
85
  data = yaml.safe_load(fp)
86
86
  fp.close()
87
87
  validate(data, schema)
88
+ return data
88
89
  except ValidationError as exc:
89
90
  raise PackageConfigException(f"validate {yml_file} failed, schema file is {schema_file}, "
90
91
  f"message: {exc.message}\n"
@@ -5,7 +5,9 @@ import inspect
5
5
  import os
6
6
  import sys
7
7
  import tempfile
8
+ import requests
8
9
  import shlex
10
+ import hashlib
9
11
  from lbkit.log import Logger
10
12
  from lbkit import errors
11
13
  from lbkit import misc
@@ -51,8 +53,8 @@ class Tools(object):
51
53
  show_cmd = "(" + log_prefix + ") " + show_cmd
52
54
 
53
55
  fd = os.fdopen(os.open(self.log_name, os.O_APPEND | os.O_CREAT | os.O_RDWR), "a+")
54
- self.log.debug("{}>>>>{} {}".format(Color.GREEN, Color.RESET_ALL, show_cmd))
55
- fd.write(">>>> {}".format(show_cmd))
56
+ self.log.info("{}>>>>{} {}".format(Color.GREEN, Color.RESET_ALL, show_cmd))
57
+ fd.write(">>>> {}\n".format(show_cmd))
56
58
  if os.environ.get("VERBOSE", False):
57
59
  verbose = True
58
60
  real_cmd = self._real_command(cmd)
@@ -66,16 +68,19 @@ class Tools(object):
66
68
  fd.close()
67
69
  result.communicate()
68
70
  if result is None or result.returncode != 0:
69
- self.log.error(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
70
- msg = f"{file}:{line} > Run command failed, cmd: {show_cmd}, error code: {result.returncode}, log: {self.log_name}"
71
+ msg = f"{file}:{line} > Run command ({show_cmd}) failed but ignore, log file is {self.log_name}"
71
72
  if not ignore_error:
73
+ self.log.error(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>")
72
74
  raise errors.RunCommandException(msg)
73
- self.log.error(msg)
75
+ self.log.warn(msg)
74
76
  return result
75
77
 
76
78
  def pipe(self, cmds: list[str], ignore_error=False, out_file = None):
77
79
  if not isinstance(cmds, list):
78
80
  raise errors.ArgException("Command ({}) with type error, only list[str] can be accepted".format(cmds))
81
+ if out_file and os.path.isfile(out_file):
82
+ fp = open(out_file, "w")
83
+ fp.close()
79
84
  stdin = None
80
85
  for cmd in cmds:
81
86
  self.log.debug("{}>>>>{} {}".format(Color.GREEN, Color.RESET_ALL, cmd))
@@ -85,7 +90,7 @@ class Tools(object):
85
90
  if ret is None:
86
91
  raise errors.RunCommandException(f"Run command {real_cmd[0]} failed")
87
92
  ret.communicate()
88
- if ret is None or ret.returncode != 0:
93
+ if ret.returncode != 0:
89
94
  if ignore_error:
90
95
  self.log.info("Run command ({}) failed".format(cmd))
91
96
  return
@@ -93,6 +98,8 @@ class Tools(object):
93
98
  if stdin:
94
99
  stdin.close()
95
100
  stdin = stdout
101
+ stdin.seek(0)
102
+
96
103
  if out_file:
97
104
  stdin.seek(0)
98
105
  with open(out_file, "w+b") as fp:
@@ -105,3 +112,44 @@ class Tools(object):
105
112
  check = False if ignore_error else True
106
113
  return subprocess.run(real_cmd, capture_output=True, check=check, encoding="utf-8")
107
114
 
115
+ @staticmethod
116
+ def file_digest_sha256(filename):
117
+ """计算文件的sha256值"""
118
+ sha256 = hashlib.sha256()
119
+ fp = open(filename, "rb")
120
+ while True:
121
+ data = fp.read(65536)
122
+ if len(data) == 0:
123
+ break
124
+ sha256.update(data)
125
+ fp.close()
126
+ return sha256.hexdigest()
127
+
128
+ def download(self, url, dst_file, sha256sum = None):
129
+ """下载文件"""
130
+ self.log.info("Start download %s", url)
131
+ is_local = False
132
+ if url.startswith("file://"):
133
+ path = url[7:]
134
+ shutil.copyfile(path, dst_file)
135
+ is_local = True
136
+ if os.path.isfile(dst_file):
137
+ digest = self.file_digest_sha256(dst_file)
138
+ if sha256sum is None or digest == sha256sum:
139
+ return
140
+ if is_local:
141
+ raise errors.DigestNotMatchError(f"File {dst_file} with sha256 error, need: {sha256sum}, get: {digest}")
142
+ os.unlink(dst_file)
143
+ verify = os.environ.get("HTTPS_VERIFY", True)
144
+ if verify:
145
+ response = requests.get(url, timeout=30, verify=True)
146
+ else:
147
+ response = requests.get(url, timeout=30)
148
+ fp = open(dst_file, "wb")
149
+ fp.write(response.content)
150
+ fp.close()
151
+ digest = self.file_digest_sha256(dst_file)
152
+ if sha256sum is None or digest == sha256sum:
153
+ self.log.info("Download %s successfully", url)
154
+ return
155
+ raise errors.DigestNotMatchError(f"File {dst_file} with sha256 error, need: {sha256sum}, get: {digest}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lbkit
3
- Version: 0.5.9
3
+ Version: 0.5.11
4
4
  Summary: Tools provided by litebmc.com
5
5
  Home-page: https://www.litebmc.com
6
6
  Author: xuhj@litebmc.com
@@ -20,5 +20,6 @@ Requires-Dist: conan==2.1
20
20
  Requires-Dist: requests
21
21
  Requires-Dist: gitpython
22
22
  Requires-Dist: inflection
23
+ Requires-Dist: meson==1.4.0
23
24
 
24
25
  build and code generate tools
@@ -39,6 +39,7 @@ lbkit/component/test.py
39
39
  lbkit/component/template/conanbase.mako
40
40
  lbkit/component/template/deploy.mako
41
41
  lbkit/integration/__init__.py
42
+ lbkit/integration/build_image.py
42
43
  lbkit/integration/build_manifest.py
43
44
  lbkit/integration/build_prepare.py
44
45
  lbkit/integration/build_rootfs.py
@@ -7,3 +7,4 @@ conan==2.1
7
7
  requests
8
8
  gitpython
9
9
  inflection
10
+ meson==1.4.0
@@ -19,7 +19,7 @@ setup(
19
19
  description="Tools provided by litebmc.com",
20
20
  long_description="build and code generate tools",
21
21
  long_description_content_type="text/markdown",
22
- install_requires=["pyyaml", "colorama", "mako", "node-semver==0.6.1", "jsonschema", "conan==2.1", "requests", "gitpython", "inflection"],
22
+ install_requires=["pyyaml", "colorama", "mako", "node-semver==0.6.1", "jsonschema", "conan==2.1", "requests", "gitpython", "inflection", "meson==1.4.0"],
23
23
  url="https://www.litebmc.com",
24
24
  packages=find_packages(),
25
25
  include_package_data=True,
@@ -1,2 +0,0 @@
1
-
2
- __version__ = '0.5.9'
@@ -1,89 +0,0 @@
1
- """环境准备"""
2
- import os
3
- import shutil
4
- import tarfile
5
- from lbkit.integration.config import Config
6
- from lbkit.integration.task import Task
7
- from lbkit.log import Logger
8
-
9
- log = Logger("product_prepare")
10
-
11
-
12
- class ManifestValidateError(OSError):
13
- """Raised when validation manifest.yml failed."""
14
-
15
- src_cwd = os.path.split(os.path.realpath(__file__))[0]
16
-
17
- class BuildPrepare(Task):
18
- def check_manifest(self):
19
- """检查manifest文件是否满足schema格式描述"""
20
- self.load_manifest()
21
-
22
- def install_tools(self):
23
- url = self.get_manifest_config("tools/compiler/url")
24
- sha256 = self.get_manifest_config("tools/compiler/sha256")
25
- path = self.get_manifest_config("tools/compiler/path")
26
- compiler_path = path if path else os.path.join(self.config.tool_path, "linaro")
27
-
28
- need_untar = True
29
-
30
- # 下载工具
31
- tool_file = os.path.join(self.config.download_path, "gcc.tar.bz2")
32
- # 先检查安装路径下是否存在sha256.lock文件,如果存在且值相同的不处理
33
- sha256_flag = os.path.join(compiler_path, "sha256.lock")
34
- if os.path.isfile(sha256_flag):
35
- fp = open(sha256_flag, "r", encoding="utf-8")
36
- lock_sha256 = fp.readline()
37
- fp.close
38
- if lock_sha256 == sha256:
39
- log.info("%s exist, skip untar", sha256_flag)
40
- need_untar = False
41
- # 如果需要解压值url或sha256为空时异常
42
- if need_untar and url is None or sha256 is None:
43
- raise ManifestValidateError(self.config.manifest + " with error, tools.compiler.url or tools.compiler.sha256 is None")
44
-
45
- # 解压到tools/linaro目录
46
- self.download_url(url, tool_file, sha256)
47
- tar = tarfile.open(tool_file)
48
- top_dir = tar.next().name
49
- if (need_untar):
50
- log.info("Start untar %s", tool_file)
51
- shutil.rmtree(compiler_path, ignore_errors=True)
52
- os.makedirs(compiler_path, exist_ok=True)
53
- tar.extractall(compiler_path)
54
- # 加了工具的目录名生成真实的工具目录地址
55
- compiler_path = os.path.join(compiler_path, top_dir)
56
- self.modify_profile(compiler_path)
57
- with open(sha256_flag, "w+", encoding="utf-8") as fp:
58
- fp.write(sha256)
59
- log.info("Set strip to %s", self.config.strip)
60
-
61
- def modify_profile(self, compiler_path):
62
- host = self.get_manifest_config("tools/compiler/host")
63
- # 制作rootfs时需要strip镜像,所以需要单独指定stip路径
64
- self.config.strip = os.path.join(compiler_path, "bin", host + "-strip")
65
- profile = self.get_manifest_config("tools/conan/profile")
66
- src_profile = os.path.join(self.config.work_dir, profile)
67
- log.info("Copy profile %s", src_profile)
68
- if not os.path.isfile(src_profile):
69
- raise FileNotFoundError(f"profile {src_profile} not found")
70
- profiles_dir = os.path.expanduser("~/.conan2/profiles")
71
- dst_profile = os.path.join(profiles_dir, os.path.basename(profile))
72
- if os.path.isdir(profiles_dir):
73
- shutil.copyfile(src_profile, dst_profile, follow_symlinks=False)
74
- cmd = f"sed -i 's@^toolchain=.*@toolchain={compiler_path}@g' {dst_profile}"
75
- self.exec(cmd)
76
- cmd = f"sed -i 's@^target_host=.*@target_host={host}@g' {dst_profile}"
77
- self.exec(cmd)
78
- # 重写profile配置
79
- log.info("Overwrite profile to %s", dst_profile)
80
- self.config.profile_host = os.path.basename(dst_profile)
81
-
82
- def run(self):
83
- """任务入口"""
84
- self.check_manifest()
85
- self.install_tools()
86
- if __name__ == "__main__":
87
- config = Config()
88
- build = BuildPrepare(config)
89
- build.run()
@@ -1,126 +0,0 @@
1
- """任务基础类"""
2
- import shutil
3
- import importlib
4
- import os
5
- import hashlib
6
- import requests
7
- import jsonschema
8
- import json, yaml
9
- from multiprocessing import Process
10
- from lbkit.log import Logger
11
- from lbkit.tools import Tools
12
-
13
- log = Logger("product_test")
14
-
15
- from lbkit.integration.config import Config
16
-
17
- class ManifestValidateError(OSError):
18
- """Raised when validation manifest.yml failed."""
19
-
20
- class DigestNotMatchError(OSError):
21
- """Raised when source and destination are the same file."""
22
-
23
- class Task(Process):
24
- """任务基础类,提供run和install默认实现以及其它基础该当"""
25
- def __init__(self, config: Config, name: str):
26
- super().__init__()
27
- self.tools = Tools(name)
28
- self.config = config
29
-
30
- def install(self):
31
- """安装任务"""
32
- log.info("install...........")
33
-
34
- def exec(self, cmd: str, verbose=False, ignore_error = False, sensitive=False, log_prefix=""):
35
- return self.tools.exec(cmd, verbose, ignore_error, sensitive, log_prefix)
36
-
37
- def pipe(self, cmds: list[str], ignore_error=False, out_file = None):
38
- self.tools.pipe(cmds, out_file, ignore_error)
39
-
40
- def run(self, cmd, ignore_error=False):
41
- return self.tools.run(cmd, ignore_error)
42
-
43
- def do_hook(self, path):
44
- """执行任务钓子,用于定制化"""
45
- try:
46
- module = importlib.import_module(path)
47
- except TypeError:
48
- log.info("Load module(%s) failed, skip", path)
49
- return
50
- log.info(module)
51
- hook = module.TaskHook(self.config)
52
- hook.run()
53
-
54
- def get_manifest_config(self, key: str):
55
- """从manifest中读取配置"""
56
- with open(self.config.manifest, "r", encoding="utf-8") as fp:
57
- manifest = yaml.load(fp, yaml.FullLoader)
58
- keys = key.split("/")
59
- for k in keys:
60
- manifest = manifest.get(k, None)
61
- if manifest is None:
62
- return None
63
- return manifest
64
-
65
- @staticmethod
66
- def file_digest_sha256(filename):
67
- """计算文件的sha256值"""
68
- sha256 = hashlib.sha256()
69
- fp = open(filename, "rb")
70
- while True:
71
- data = fp.read(65536)
72
- if len(data) == 0:
73
- break
74
- sha256.update(data)
75
- fp.close()
76
- return sha256.hexdigest()
77
-
78
- def download_url(self, url, dst_file, sha256sum = None):
79
- """下载文件"""
80
- log.info("Start download %s", url)
81
- is_local = False
82
- if url.startswith("file://"):
83
- path = url[7:]
84
- shutil.copyfile(path, dst_file)
85
- is_local = True
86
- if os.path.isfile(dst_file):
87
- digest = self.file_digest_sha256(dst_file)
88
- if sha256sum is None or digest == sha256sum:
89
- return
90
- if is_local:
91
- raise DigestNotMatchError(f"File {dst_file} with sha256 error, need: {sha256sum}, get: {digest}")
92
- os.unlink(dst_file)
93
- verify = os.environ.get("HTTPS_VERIFY", True)
94
- if verify:
95
- response = requests.get(url, timeout=30, verify=True)
96
- else:
97
- response = requests.get(url, timeout=30)
98
- fp = open(dst_file, "wb")
99
- fp.write(response.content)
100
- fp.close()
101
- digest = self.file_digest_sha256(dst_file)
102
- if sha256sum is None or digest == sha256sum:
103
- log.info("Download %s successfully", url)
104
- return
105
- raise DigestNotMatchError(f"File {dst_file} with sha256 error, need: {sha256sum}, get: {digest}")
106
-
107
- def load_manifest(self):
108
- """加载manifest.yml并验证schema文件"""
109
- with open(self.config.manifest, "r", encoding="utf-8") as fp:
110
- manifest = yaml.load(fp, yaml.FullLoader)
111
- schema_version = manifest.get("schema", None)
112
- if schema_version is None or not isinstance(schema_version, int):
113
- raise ManifestValidateError("Validation manifest.yml failed, Property schema not exist.")
114
- # 加载schmea文件验证文件合法性
115
- schema_url = "https://www.litebmc.com/standards/metadata/manifest.yml/schema_v" + str(schema_version)
116
- # 下载schema文件
117
- schema_file = os.path.join(self.config.temp_path, "manifest.schema.json")
118
- self.download_url(schema_url, schema_file)
119
- with open (schema_file, "rb") as fp:
120
- schema = json.load(fp)
121
- # 验证manifest.yml是否合法
122
- try:
123
- jsonschema.validate(manifest, schema)
124
- except jsonschema.exceptions.ValidationError as e:
125
- raise ManifestValidateError("manifest validate failed") from e
126
- return manifest
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes