lbkit 0.8.4__tar.gz → 0.8.6__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 (68) hide show
  1. lbkit-0.8.6/MANIFEST.in +3 -0
  2. {lbkit-0.8.4/lbkit.egg-info → lbkit-0.8.6}/PKG-INFO +1 -1
  3. lbkit-0.8.6/lbkit/__init__.py +2 -0
  4. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/cli.py +11 -26
  5. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/template/server.c.mako +1 -1
  6. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/log.py +1 -0
  7. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/misc.py +42 -0
  8. lbkit-0.8.6/lbkit/tasks/config.py +187 -0
  9. lbkit-0.8.6/lbkit/tasks/executor.py +258 -0
  10. lbkit-0.8.6/lbkit/tasks/task.py +132 -0
  11. lbkit-0.8.4/lbkit/integration/build_image.py → lbkit-0.8.6/lbkit/tasks/task_build_image.py +4 -6
  12. lbkit-0.8.4/lbkit/integration/build_manifest.py → lbkit-0.8.6/lbkit/tasks/task_build_manifest.py +4 -4
  13. lbkit-0.8.6/lbkit/tasks/task_build_prepare.py +98 -0
  14. lbkit-0.8.4/lbkit/integration/build_rootfs.py → lbkit-0.8.6/lbkit/tasks/task_build_rootfs.py +4 -5
  15. lbkit-0.8.6/lbkit/tasks/task_download.py +129 -0
  16. lbkit-0.8.6/lbkit/tasks/task_test.py +29 -0
  17. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/tools.py +0 -1
  18. lbkit-0.8.6/lbkit/utils/env_detector.py +38 -0
  19. {lbkit-0.8.4 → lbkit-0.8.6/lbkit.egg-info}/PKG-INFO +1 -1
  20. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit.egg-info/SOURCES.txt +14 -9
  21. lbkit-0.8.6/test/test_config.py +66 -0
  22. lbkit-0.8.4/MANIFEST.in +0 -3
  23. lbkit-0.8.4/lbkit/__init__.py +0 -2
  24. lbkit-0.8.4/lbkit/integration/build_prepare.py +0 -59
  25. lbkit-0.8.4/lbkit/integration/config.py +0 -91
  26. lbkit-0.8.4/lbkit/integration/task.py +0 -58
  27. {lbkit-0.8.4 → lbkit-0.8.6}/AUTHORS +0 -0
  28. {lbkit-0.8.4 → lbkit-0.8.6}/LICENSE +0 -0
  29. {lbkit-0.8.4 → lbkit-0.8.6}/README.md +0 -0
  30. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/build_conan_parallel.py +0 -0
  31. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/ci_robot/__init__.py +0 -0
  32. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/ci_robot/gitee.py +0 -0
  33. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/__init__.py +0 -0
  34. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/codegen.py +0 -0
  35. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/ctype_defination.py +0 -0
  36. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/idf_interface.py +0 -0
  37. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/renderer.py +0 -0
  38. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/template/client.c.mako +0 -0
  39. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/template/client.h.mako +0 -0
  40. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/template/interface.c.mako +0 -0
  41. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/template/interface.introspect.xml.mako +0 -0
  42. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/template/public.c.mako +0 -0
  43. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/template/public.h.mako +0 -0
  44. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/codegen/template/server.h.mako +0 -0
  45. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/component/__init__.py +0 -0
  46. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/component/arg_parser.py +0 -0
  47. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/component/build.py +0 -0
  48. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/component/template/conanbase.mako +0 -0
  49. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/component/template/deploy.mako +0 -0
  50. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/component/test.py +0 -0
  51. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/errors.py +0 -0
  52. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/helper.py +0 -0
  53. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/lbkit.py +0 -0
  54. {lbkit-0.8.4/lbkit/integration → lbkit-0.8.6/lbkit/tasks}/__init__.py +0 -0
  55. {lbkit-0.8.4/lbkit/integration → lbkit-0.8.6/lbkit/tasks}/template/conanfile.py.mako +0 -0
  56. {lbkit-0.8.4/lbkit/integration → lbkit-0.8.6/lbkit/tasks}/template/rootfs.py.mako +0 -0
  57. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/utils/__init__.py +0 -0
  58. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/utils/images/__init__.py +0 -0
  59. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit/utils/images/emmc.py +0 -0
  60. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit.egg-info/dependency_links.txt +0 -0
  61. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit.egg-info/entry_points.txt +0 -0
  62. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit.egg-info/requires.txt +0 -0
  63. {lbkit-0.8.4 → lbkit-0.8.6}/lbkit.egg-info/top_level.txt +0 -0
  64. {lbkit-0.8.4 → lbkit-0.8.6}/setup.cfg +0 -0
  65. {lbkit-0.8.4 → lbkit-0.8.6}/setup.py +0 -0
  66. {lbkit-0.8.4 → lbkit-0.8.6}/test/__init__.py +0 -0
  67. {lbkit-0.8.4 → lbkit-0.8.6}/test/test_codegen.py +0 -0
  68. {lbkit-0.8.4 → lbkit-0.8.6}/test/test_helper.py +0 -0
@@ -0,0 +1,3 @@
1
+ recursive-include lbkit/codegen/template *.mako
2
+ recursive-include lbkit/tasks/template *.mako
3
+ recursive-include lbkit/component/template *.mako
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: lbkit
3
- Version: 0.8.4
3
+ Version: 0.8.6
4
4
  Summary: Tools provided by litebmc.com
5
5
  Home-page: https://www.litebmc.com
6
6
  Author: xuhj@litebmc.com
@@ -0,0 +1,2 @@
1
+
2
+ __version__ = '0.8.6'
@@ -10,16 +10,13 @@ from lbkit import __version__ as client_version
10
10
  from lbkit.codegen.codegen import CodeGen
11
11
  from lbkit.component.build import BuildComponent
12
12
  from lbkit.component.test import TestComponent
13
- from lbkit.integration.build_manifest import BuildManifest
14
- from lbkit.integration.build_rootfs import BuildRootfs
15
- from lbkit.integration.build_prepare import BuildPrepare
16
- from lbkit.integration.build_image import BuildImage
13
+ from lbkit.tasks.executor import Executor
17
14
  from lbkit.component.arg_parser import ArgParser
18
- from lbkit.integration.config import Config
19
15
  from lbkit.ci_robot.gitee import Gitee
20
16
  from lbkit.log import Logger
21
17
  from lbkit import misc
22
18
  from lbkit import errors
19
+ from lbkit.utils.env_detector import EnvDetector
23
20
 
24
21
  log = Logger("cli")
25
22
 
@@ -80,26 +77,14 @@ class Command(object):
80
77
 
81
78
  组件需要支持多种跨平台构建场景,典型的包括DT(X86-64)、交叉编译(arm64)
82
79
  """
83
- arg_parser = ArgParser.new()
84
- build = BuildComponent(arg_parser, sys.argv[2:])
85
- build.run()
86
-
87
- # create product package
88
- def create(self, *args):
89
- """
90
- 构建组件.
91
-
92
- 组件需要支持多种跨平台构建场景,典型的包括DT(X86-64)、交叉编译(arm64)
93
- """
94
- cfg = Config(sys.argv[2:])
95
- build = BuildPrepare(cfg, "create_pre")
96
- build.run()
97
- build = BuildManifest(cfg, "create_run")
98
- build.run()
99
- build = BuildRootfs(cfg, "create_rootfs")
100
- build.run()
101
- build = BuildImage(cfg, "create_image")
102
- build.run()
80
+ env = EnvDetector()
81
+ if env.component:
82
+ arg_parser = ArgParser.new()
83
+ build = BuildComponent(arg_parser, sys.argv[2:])
84
+ build.run()
85
+ elif env.manifest:
86
+ exe = Executor(env)
87
+ exe.run()
103
88
 
104
89
  def gitee(self, *args):
105
90
  """
@@ -126,7 +111,7 @@ class Command(object):
126
111
  """
127
112
  grps = [("Code Generate commands", ["gen"]),
128
113
  ("Build Component commands", ["new", "build", "test"]),
129
- ("Build Product commands", ["create"]),
114
+ ("Build Product commands", ["build"]),
130
115
  ("Misc commands", ["help"]),
131
116
  ("CI Robot commands", ["gitee"])
132
117
  ]
@@ -86,7 +86,7 @@ static LBInterface _${class_name}_interface = {
86
86
  % if intf.plugin.install_dir:
87
87
  .plugin_dir = "${intf.plugin.install_dir}",
88
88
  % else:
89
- .plugin_dir = NULL,
89
+ .plugin_dir = "/opt/litebmc/plugins/${class_name}",
90
90
  % endif
91
91
  #endif
92
92
  };
@@ -12,6 +12,7 @@ class Logger(logging.getLoggerClass()):
12
12
  self.logenv = os.environ.get("LOG")
13
13
  if self.logenv is None:
14
14
  formatter = logging.Formatter('%(message)s')
15
+ self.setLevel(logging.INFO)
15
16
  else:
16
17
  formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
17
18
  if self.logenv == "info":
@@ -5,12 +5,14 @@ import json
5
5
  import yaml
6
6
  import re
7
7
  import requests
8
+ import fcntl
8
9
  from string import Template
9
10
  from colorama import Fore, Style
10
11
  from jsonschema import validate, ValidationError
11
12
  from lbkit.errors import PackageConfigException, HttpRequestException
12
13
 
13
14
  LOG_DIR = "/tmp/lbkit/log"
15
+ TARGETS_DIR = "/usr/share/litebmc/targets"
14
16
 
15
17
 
16
18
  class Color(object):
@@ -103,3 +105,43 @@ def load_yml_with_json_schema_validate(yml_file, default_json_schema_file, **kwa
103
105
  raise PackageConfigException(f"validate {yml_file} failed, schema file is {schema_file}, "
104
106
  f"message: {exc.message}\n"
105
107
  "installing redhat.vscode-yaml plugin in vscode will help you write odf files")
108
+
109
+ class DownloadFlag():
110
+ @staticmethod
111
+ def clean(filename):
112
+ """清理文件标记"""
113
+ filename += ".flag"
114
+ fp = open(filename, "a+")
115
+ fcntl.flock(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
116
+ fp.truncate(0)
117
+ fcntl.flock(fp, fcntl.F_UNLCK)
118
+ fp.close()
119
+
120
+ @staticmethod
121
+ def create(filename, url, new_hash):
122
+ """创建文件标记"""
123
+ filename += ".flag"
124
+ fp = open(filename, "a+")
125
+ fcntl.flock(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
126
+ fp.seek(0, 0)
127
+ fp.truncate(0)
128
+ fp.write(url + "|" + new_hash)
129
+ fcntl.flock(fp, fcntl.F_UNLCK)
130
+ fp.close()
131
+
132
+ @staticmethod
133
+ def read(filename):
134
+ """读取文件标记"""
135
+ filename += ".flag"
136
+ if not os.path.isfile(filename):
137
+ return "", ""
138
+ fp = open(filename, "a+")
139
+ fcntl.flock(fp, fcntl.LOCK_EX | fcntl.LOCK_NB)
140
+ fp.seek(0, 0)
141
+ content = fp.read()
142
+ fcntl.flock(fp, fcntl.F_UNLCK)
143
+ fp.close()
144
+ if len(content) == 0:
145
+ return "", ""
146
+ chunk = content.split("|")
147
+ return chunk[0], chunk[1]
@@ -0,0 +1,187 @@
1
+ """集成构建配置项"""
2
+ import argparse
3
+ import os, sys
4
+ from lbkit.log import Logger
5
+ from lbkit.misc import load_yml_with_json_schema_validate, TARGETS_DIR
6
+
7
+ log = Logger("build_config")
8
+
9
+
10
+ class Config(object):
11
+ """集成构建的配置项"""
12
+ def __init__(self, args = None):
13
+ parser = self.arg_parser()
14
+ args = parser.parse_args(args)
15
+
16
+ # 配置项
17
+ self.manifest = os.path.join(os.getcwd(), args.manifest)
18
+ self.manifest = os.path.realpath(self.manifest)
19
+ # 配置项目录
20
+ self.work_dir = os.path.dirname(self.manifest)
21
+ sys.path.append(self.work_dir)
22
+ # 是否从源码构建
23
+ self.from_source = args.from_source
24
+ # 是否打印详细信息
25
+ self.verbose = True if os.environ.get("VERBOSE", False) else False
26
+ # 编译类型
27
+ self.build_type = args.build_type
28
+ # conan中心仓
29
+ self.remote = args.remote
30
+
31
+ if not os.path.isfile(self.manifest):
32
+ raise FileNotFoundError(f"File {args.manifest} not exist")
33
+
34
+ # 编译主机配置项
35
+ self.profile_build = args.profile_build
36
+ # 待所有参数确认后会调用refresh_profile_name设置正确的profile
37
+ self.profile_host = None
38
+
39
+ # conan.lock options
40
+ self.using_lockfile = args.lockfile
41
+ self.update_lockfile = args.update_lockfile
42
+ self.target = args.target
43
+ self.product = args.product
44
+
45
+ # 设置并创建构建所需目录
46
+ log.info("Work dir: %s", self.work_dir)
47
+ self.code_path = os.getcwd()
48
+ self.temp_path = os.path.join(self.code_path, ".temp")
49
+ self.output_path = os.path.join(self.temp_path, "output")
50
+ self.download_path = os.path.join(self.temp_path, "download")
51
+ self.tool_path = os.path.join(self.temp_path, "tools")
52
+ self.compiler_path = os.path.join(self.tool_path, "compiler")
53
+ # conan组件打包目录
54
+ self.conan_install = []
55
+ self.mnt_path = os.path.join(self.temp_path, "mnt_path")
56
+ self.rootfs_img = os.path.join(self.output_path, "rootfs.img")
57
+ # rootfs、uboot和kernel关键文件路径
58
+ self.uboot_bin = None
59
+ self.linux_bin = None
60
+ self.rootfs_tar = None
61
+ os.makedirs(self.temp_path, exist_ok=True)
62
+ os.makedirs(self.tool_path, exist_ok=True)
63
+ os.makedirs(self.output_path, exist_ok=True)
64
+ os.makedirs(self.download_path, exist_ok=True)
65
+ os.makedirs(self.compiler_path, exist_ok=True)
66
+ # 制作rootfs时需要strip镜像,所以需要单独指定stip路径
67
+ self.strip = "strip"
68
+ self.check_product()
69
+ # 刷新conan profile
70
+ self.refresh_profile_name()
71
+
72
+ @staticmethod
73
+ def target_list():
74
+ targets = {}
75
+ dirname = os.path.join(TARGETS_DIR)
76
+ for file in os.listdir(dirname):
77
+ if not file.endswith(".yml"):
78
+ continue
79
+ tgt_file = os.path.join(TARGETS_DIR, file)
80
+ targets[file] = tgt_file
81
+ return targets
82
+
83
+ @staticmethod
84
+ def arg_parser():
85
+ """返回配置项支持的参数"""
86
+ parser = argparse.ArgumentParser(description="Build LiteBMC", formatter_class=argparse.RawTextHelpFormatter)
87
+ parser.add_argument("-m", "--manifest", help="Specify the manifest.yml, ignored when -l is specified.", default="./manifest.yml")
88
+ parser.add_argument("-s", "--from_source", help="Build from source", action="store_true")
89
+ parser.add_argument("-pr:b", "--profile_build", help="Apply the specified profile to the build machine", default="default")
90
+ parser.add_argument("-bt", "--build_type", type=str, choices=['debug', 'release', 'minsize'], help="Set the build type", default="debug")
91
+ parser.add_argument("-r", "--remote", help="specified conan server", default="litebmc")
92
+ parser.add_argument("-l", "--lockfile", help="using conan.lock", action="store_true")
93
+ parser.add_argument("-ul", "--update_lockfile", help="update conan.lock", action="store_true")
94
+ parser.add_argument("-p", "--product", help="product name, default product is `default`", default="default")
95
+ targets = Config.target_list()
96
+ target_help = "build target:"
97
+ for tgt, _ in targets.items():
98
+ target_help += "\n* " + tgt[:-4]
99
+ parser.add_argument("-t", "--target", help=target_help, default="default")
100
+ return parser
101
+
102
+ def get_manifest_config(self, key: str, default=None):
103
+ """从manifest中读取配置"""
104
+ manifest = self.load_manifest()
105
+ keys = key.split("/")
106
+ for k in keys:
107
+ manifest = manifest.get(k, None)
108
+ if manifest is None:
109
+ return default
110
+ return manifest
111
+
112
+ @staticmethod
113
+ def merge_cfg(dst, src):
114
+ """合并两个配置项"""
115
+ # 如果源为None,则返回dst
116
+ if src is None:
117
+ return dst
118
+ # 如果目标是空的,直接返回src
119
+ if not dst:
120
+ return src
121
+ # 如果是数组、标量的直接覆盖dst即可,所以返回src
122
+ if not isinstance(dst, dict):
123
+ return src
124
+ # 如果目标非空,但源是空,直接返回目标
125
+ if not src:
126
+ return dst
127
+ if not isinstance(src, dict):
128
+ raise Exception(f"Merge configuration failed, source config {src} is not a dictionary")
129
+ output = {}
130
+ for key, val in src.items():
131
+ dst_val = dst.get(key)
132
+ output[key] = Config.merge_cfg(dst_val, val)
133
+ for key, val in dst.items():
134
+ # 已经合并过,即目标中存在,但源不存在,直接合并
135
+ if key in output:
136
+ continue
137
+ output[key] = val
138
+ return output
139
+
140
+ def get_product_config(self, key: str, default=None):
141
+ """获取产品配置,注意,key只需要基于products/[name]/即可,如 toolchain"""
142
+ global_cfg = self.get_manifest_config(key, default)
143
+ key = f"products/{self.product}/{key}"
144
+ product_cfg = self.get_manifest_config(key, None)
145
+ if not product_cfg:
146
+ return global_cfg
147
+ if not global_cfg:
148
+ return product_cfg
149
+ return Config.merge_cfg(global_cfg, product_cfg)
150
+
151
+ def load_manifest(self):
152
+ """加载manifest.yml并验证schema文件"""
153
+ template = {}
154
+ template["code_path"] = self.code_path
155
+ template["temp_path"] = self.temp_path
156
+ template["download_path"] = os.path.join(self.download_path)
157
+ return load_yml_with_json_schema_validate(self.manifest, "/usr/share/litebmc/schema/pdf.v1.json", **template)
158
+
159
+ def check_product(self):
160
+ products = self.get_manifest_config("products", {})
161
+ if products.get(self.product):
162
+ return
163
+ log.error(f"Only the following products are supported:")
164
+ for key, _ in products.items():
165
+ log.info(" * " + key)
166
+ # todo: 待manifest.yml整改到位到删除下面注释
167
+ # raise Exception(f"Unkown product {self.product}")
168
+
169
+ def refresh_profile_name(self):
170
+ self.profile_host = self.get_product_config("toolchain/profile/name", "litebmc")
171
+
172
+ def set_build_type(self, value):
173
+ self.build_type = value
174
+
175
+ def deal_conf(self, config_dict):
176
+ """
177
+ 处理Target级别的配置"target_config"
178
+ 当Config类有set_xxx类方法时,则可以在target文件中配置xxx
179
+ """
180
+ if not config_dict or not isinstance(config_dict, dict):
181
+ return
182
+ for key, conf in config_dict.items():
183
+ try:
184
+ method = getattr(self, f"set_{key}")
185
+ method(conf)
186
+ except Exception as e:
187
+ raise Exception(f"目标 config 无效配置: {key}") from e
@@ -0,0 +1,258 @@
1
+ #! /usr/bin/env python3
2
+
3
+ import importlib
4
+ import os
5
+ import time
6
+ import traceback
7
+ import sys
8
+
9
+ from multiprocessing import Process
10
+ from multiprocessing import Manager
11
+ from lbkit.errors import LiteBmcException
12
+ from lbkit.tools import Tools
13
+ from lbkit.tasks.config import Config
14
+ from lbkit.utils.env_detector import EnvDetector
15
+ import lbkit.misc as misc
16
+ from lbkit.misc import load_yml_with_json_schema_validate
17
+
18
+ # 任务失败状态
19
+ TASK_STATUS_FAILED = "Failed"
20
+ TASK_STATUS_SUCCED = "succed"
21
+ TASK_STATUS_EXCEPT = "Except"
22
+ TASK_STATUS_RUNNING = "Runing"
23
+ tool = Tools("executor")
24
+ log = tool.log
25
+ manager = Manager()
26
+ status_dict = manager.dict()
27
+ status_lock = manager.Lock()
28
+
29
+
30
+ def wait_finish(target_name, wait_list):
31
+ """
32
+ 等待任务结束
33
+ """
34
+ if not wait_list:
35
+ return True
36
+ start_time = time.time()
37
+ cnt = 0
38
+ while True:
39
+ finish = True
40
+ time.sleep(0.1)
41
+ for work_name in wait_list:
42
+ cur_time = time.time()
43
+ key = target_name + "/" + work_name
44
+ status = status_dict.get(key)
45
+ if status is None:
46
+ log.warn(f"等待不存在的任务{key}。如果要等待一个任务,这个任务必须在当前任务之前运行,否则触发异常")
47
+ return False
48
+ if status == TASK_STATUS_SUCCED:
49
+ continue
50
+ if status == TASK_STATUS_FAILED or status == TASK_STATUS_EXCEPT:
51
+ return False
52
+ finish = False
53
+ # 每等待60s打印一次日志
54
+ if int(cur_time - start_time) >= 60:
55
+ start_time = time.time()
56
+ cnt += 60
57
+ log.info("目标 {} 正在等待任务: {}, 当前已等待 {} 秒".format(target_name, work_name, cnt))
58
+ break
59
+ if finish:
60
+ return True
61
+
62
+
63
+ class TaskExecutor():
64
+ '''
65
+ '''
66
+ def __init__(self, target_name, work, config: Config):
67
+ super().__init__()
68
+ self.work = work
69
+ self.config: Config = config
70
+ self.target_name = target_name
71
+ self.work_name = self.work.get("task", "")
72
+ self.status_key = target_name + "/" + self.work_name
73
+ chunks = self.work_name.split(".", -1)
74
+ if len(chunks) == 1:
75
+ self.task_path = ""
76
+ self.work_name = chunks[1]
77
+ else:
78
+ self.task_path = "lbkit.tasks." + chunks[0]
79
+ self.work_name = chunks[1]
80
+ self.exception = None
81
+
82
+ def load_class(self):
83
+ if not self.task_path:
84
+ return None
85
+ log.debug("工作路径: {}".format(self.task_path))
86
+ work_py_file = importlib.import_module(self.task_path)
87
+ return getattr(work_py_file, "TaskClass")
88
+
89
+ def run(self):
90
+ '''
91
+ 功能描述:执行任务
92
+ '''
93
+ work_name = self.work_name
94
+ log.debug(f"任务{self.status_key}已就绪")
95
+ ret = wait_finish(self.target_name, self.work.get("wait"))
96
+ if not ret:
97
+ log.debug(f"任务{self.status_key}等待的其它任务发生错误")
98
+ return -1
99
+ work_class = self.load_class()
100
+ # 如果未指定类时,不需要执行
101
+ if work_class is not None:
102
+ work_x = work_class(self.config, work_name)
103
+ # work配置项和target配置项
104
+ work_config = self.work.get("config")
105
+ work_x.deal_conf(work_config)
106
+ with status_lock:
107
+ status = status_dict.get(self.status_key)
108
+ if status is None:
109
+ status_dict[self.status_key] = TASK_STATUS_RUNNING
110
+ if status is None:
111
+ # 创建进程并且等待完成或超时
112
+ ret = work_x.run()
113
+ if ret is not None and ret != 0:
114
+ return -1
115
+ else:
116
+ # 不需要创建进程,等待任务执行完成即可
117
+ wait_list = []
118
+ wait_list.append(work_name)
119
+ ret = wait_finish(self.target_name, wait_list)
120
+ if not ret:
121
+ log.debug(f"任务{self.status_key}等待的其它任务发生错误")
122
+ return -1
123
+
124
+ log.debug(f"任务 {work_name} 开始安装步骤")
125
+
126
+ # 创建子任务
127
+ ret = exec_works(self.work.get("subtasks", []), self.config, os.cpu_count())
128
+ ret = ret and exec_works(self.work.get("seqtasks", []), self.config, 1)
129
+ if not ret:
130
+ status_dict[self.status_key] = TASK_STATUS_FAILED
131
+ return -1
132
+
133
+ log.success(f"任务 {work_name} 完成")
134
+ with status_lock:
135
+ status_dict[self.status_key] = TASK_STATUS_SUCCED
136
+ return 0
137
+
138
+ def task_handler(te:TaskExecutor):
139
+ try:
140
+ ret = te.run()
141
+ except Exception as e:
142
+ log.error(traceback.print_exc())
143
+ log.error(f"Task {te.status_key} exit with exceiption: {str(e)}")
144
+ with status_lock:
145
+ status_dict[te.status_key] = TASK_STATUS_EXCEPT
146
+ return -1
147
+ if ret != 0:
148
+ with status_lock:
149
+ status_dict[te.status_key] = TASK_STATUS_FAILED
150
+ return -1
151
+ else:
152
+ with status_lock:
153
+ status_dict[te.status_key] = TASK_STATUS_SUCCED
154
+ return 0
155
+
156
+
157
+ class TaskInfo():
158
+ def __init__(self, te: TaskExecutor, proc: Process):
159
+ self.te = te
160
+ self.proc = proc
161
+
162
+
163
+ def wait_tasks(tasks: dict[str, TaskInfo], alow_processes_alive=0):
164
+ while True:
165
+ new_results = {}
166
+ cnt = 0
167
+ killall = False
168
+ for key, ti in tasks.items():
169
+ if ti.proc.is_alive():
170
+ new_results[key] = ti
171
+ cnt += 1
172
+ continue
173
+ with status_lock:
174
+ status = status_dict.get(ti.te.status_key)
175
+ if status == TASK_STATUS_EXCEPT:
176
+ killall = True
177
+ break
178
+ if killall:
179
+ for _, ti in tasks.items():
180
+ if ti.proc.is_alive():
181
+ ti.proc.terminate()
182
+ return None, False
183
+ tasks = new_results
184
+ if cnt > 0 and cnt >= alow_processes_alive:
185
+ time.sleep(0.1)
186
+ else:
187
+ return new_results, True
188
+
189
+ def exec_works(work_list, config, processes):
190
+ if not work_list:
191
+ return True
192
+ # 创建任务并等待完成
193
+ results: dict[str, TaskInfo] = {}
194
+ for work in work_list:
195
+ te = TaskExecutor(config.target, work, config)
196
+ result = Process(target=task_handler, args=(te, ))
197
+ results[te.status_key] = TaskInfo(te, result)
198
+ result.start()
199
+ results, ok = wait_tasks(results, processes)
200
+ if not ok:
201
+ return False
202
+ _, ok = wait_tasks(results, 0)
203
+ return ok
204
+
205
+
206
+ def target_executor(config):
207
+ log.info(f"创建新目标 {config.target} 构建计划表")
208
+ manifest_target = f"{config.code_path}/targets/{config.target}.yml"
209
+ lbkit_target = os.path.join(misc.TARGETS_DIR, config.target + ".yml")
210
+ if os.path.isfile(manifest_target):
211
+ target_file = manifest_target
212
+ elif os.path.isfile(lbkit_target):
213
+ target_file = lbkit_target
214
+ else:
215
+ raise Exception(f"构建目标文件 [target_]{config.target}.yml 不存在")
216
+
217
+ # 读取配置
218
+ work_list = load_yml_with_json_schema_validate(target_file, os.path.join(misc.TARGETS_DIR, "tdf.v1.json"))
219
+ target_cfg = work_list.get("config", {})
220
+ config.deal_conf(target_cfg)
221
+ environments = work_list.get("env", {})
222
+ for key, value in environments.items():
223
+ log.success(f"配置环境变量 {key}: {value}")
224
+ os.environ[key] = value
225
+ # 打印任务清单
226
+ log.debug(f"任务列表:{work_list}")
227
+ # 创建任务调度器
228
+ ret = exec_works(work_list.get("subtasks", []), config, os.cpu_count())
229
+ ret = ret and exec_works(work_list.get("seqtasks", []), config, 1)
230
+ return ret
231
+
232
+
233
+ class Executor(object):
234
+ def __init__(self, env: EnvDetector):
235
+ if not env.manifest:
236
+ raise LiteBmcException("未找到manifest.yml配置文件,当前目录不是一个合法的产品配置仓")
237
+ os.chdir(env.manifest.folder)
238
+
239
+ def run(self):
240
+ target = ""
241
+ succ = False
242
+ try:
243
+ config = Config(sys.argv[2:])
244
+ target = config.target
245
+ succ = target_executor(config)
246
+ except Exception as e:
247
+ log.error(str(e))
248
+ raise Exception(f"任务 {target} 执行失败")
249
+ if succ:
250
+ log.success(f"任务 {target} 执行成功")
251
+ return 0
252
+ else:
253
+ raise Exception(f"任务 {target} 执行失败")
254
+
255
+ if __name__ == "__main__":
256
+ env = EnvDetector()
257
+ exec = Executor(env)
258
+ exec.run()