lbkit 0.8.5__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.
- {lbkit-0.8.5/lbkit.egg-info → lbkit-0.8.6}/PKG-INFO +1 -1
- lbkit-0.8.6/lbkit/__init__.py +2 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/misc.py +41 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/tasks/config.py +62 -3
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/tasks/executor.py +2 -0
- lbkit-0.8.6/lbkit/tasks/task.py +132 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/tasks/task_build_image.py +1 -1
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/tasks/task_build_manifest.py +1 -1
- lbkit-0.8.6/lbkit/tasks/task_build_prepare.py +98 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/tasks/task_build_rootfs.py +1 -1
- lbkit-0.8.6/lbkit/tasks/task_download.py +129 -0
- {lbkit-0.8.5 → lbkit-0.8.6/lbkit.egg-info}/PKG-INFO +1 -1
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit.egg-info/SOURCES.txt +2 -1
- lbkit-0.8.6/test/test_config.py +66 -0
- lbkit-0.8.5/lbkit/__init__.py +0 -2
- lbkit-0.8.5/lbkit/tasks/task.py +0 -70
- lbkit-0.8.5/lbkit/tasks/task_build_prepare.py +0 -59
- lbkit-0.8.5/lbkit/tasks/test.py +0 -40
- {lbkit-0.8.5 → lbkit-0.8.6}/AUTHORS +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/LICENSE +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/MANIFEST.in +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/README.md +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/build_conan_parallel.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/ci_robot/__init__.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/ci_robot/gitee.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/cli.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/__init__.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/codegen.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/ctype_defination.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/idf_interface.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/renderer.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/template/client.c.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/template/client.h.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/template/interface.c.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/template/interface.introspect.xml.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/template/public.c.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/template/public.h.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/template/server.c.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/codegen/template/server.h.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/component/__init__.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/component/arg_parser.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/component/build.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/component/template/conanbase.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/component/template/deploy.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/component/test.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/errors.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/helper.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/lbkit.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/log.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/tasks/__init__.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/tasks/task_test.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/tasks/template/conanfile.py.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/tasks/template/rootfs.py.mako +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/tools.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/utils/__init__.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/utils/env_detector.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/utils/images/__init__.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit/utils/images/emmc.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit.egg-info/dependency_links.txt +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit.egg-info/entry_points.txt +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit.egg-info/requires.txt +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/lbkit.egg-info/top_level.txt +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/setup.cfg +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/setup.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/test/__init__.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/test/test_codegen.py +0 -0
- {lbkit-0.8.5 → lbkit-0.8.6}/test/test_helper.py +0 -0
|
@@ -5,6 +5,7 @@ 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
|
|
@@ -104,3 +105,43 @@ def load_yml_with_json_schema_validate(yml_file, default_json_schema_file, **kwa
|
|
|
104
105
|
raise PackageConfigException(f"validate {yml_file} failed, schema file is {schema_file}, "
|
|
105
106
|
f"message: {exc.message}\n"
|
|
106
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]
|
|
@@ -15,6 +15,7 @@ class Config(object):
|
|
|
15
15
|
|
|
16
16
|
# 配置项
|
|
17
17
|
self.manifest = os.path.join(os.getcwd(), args.manifest)
|
|
18
|
+
self.manifest = os.path.realpath(self.manifest)
|
|
18
19
|
# 配置项目录
|
|
19
20
|
self.work_dir = os.path.dirname(self.manifest)
|
|
20
21
|
sys.path.append(self.work_dir)
|
|
@@ -32,13 +33,14 @@ class Config(object):
|
|
|
32
33
|
|
|
33
34
|
# 编译主机配置项
|
|
34
35
|
self.profile_build = args.profile_build
|
|
35
|
-
#
|
|
36
|
-
self.profile_host =
|
|
36
|
+
# 待所有参数确认后会调用refresh_profile_name设置正确的profile
|
|
37
|
+
self.profile_host = None
|
|
37
38
|
|
|
38
39
|
# conan.lock options
|
|
39
40
|
self.using_lockfile = args.lockfile
|
|
40
41
|
self.update_lockfile = args.update_lockfile
|
|
41
42
|
self.target = args.target
|
|
43
|
+
self.product = args.product
|
|
42
44
|
|
|
43
45
|
# 设置并创建构建所需目录
|
|
44
46
|
log.info("Work dir: %s", self.work_dir)
|
|
@@ -47,6 +49,7 @@ class Config(object):
|
|
|
47
49
|
self.output_path = os.path.join(self.temp_path, "output")
|
|
48
50
|
self.download_path = os.path.join(self.temp_path, "download")
|
|
49
51
|
self.tool_path = os.path.join(self.temp_path, "tools")
|
|
52
|
+
self.compiler_path = os.path.join(self.tool_path, "compiler")
|
|
50
53
|
# conan组件打包目录
|
|
51
54
|
self.conan_install = []
|
|
52
55
|
self.mnt_path = os.path.join(self.temp_path, "mnt_path")
|
|
@@ -59,8 +62,12 @@ class Config(object):
|
|
|
59
62
|
os.makedirs(self.tool_path, exist_ok=True)
|
|
60
63
|
os.makedirs(self.output_path, exist_ok=True)
|
|
61
64
|
os.makedirs(self.download_path, exist_ok=True)
|
|
65
|
+
os.makedirs(self.compiler_path, exist_ok=True)
|
|
62
66
|
# 制作rootfs时需要strip镜像,所以需要单独指定stip路径
|
|
63
67
|
self.strip = "strip"
|
|
68
|
+
self.check_product()
|
|
69
|
+
# 刷新conan profile
|
|
70
|
+
self.refresh_profile_name()
|
|
64
71
|
|
|
65
72
|
@staticmethod
|
|
66
73
|
def target_list():
|
|
@@ -79,12 +86,12 @@ class Config(object):
|
|
|
79
86
|
parser = argparse.ArgumentParser(description="Build LiteBMC", formatter_class=argparse.RawTextHelpFormatter)
|
|
80
87
|
parser.add_argument("-m", "--manifest", help="Specify the manifest.yml, ignored when -l is specified.", default="./manifest.yml")
|
|
81
88
|
parser.add_argument("-s", "--from_source", help="Build from source", action="store_true")
|
|
82
|
-
parser.add_argument("-pr", "--profile", help="Apply the specified profile to the host machine", default="litebmc.ini")
|
|
83
89
|
parser.add_argument("-pr:b", "--profile_build", help="Apply the specified profile to the build machine", default="default")
|
|
84
90
|
parser.add_argument("-bt", "--build_type", type=str, choices=['debug', 'release', 'minsize'], help="Set the build type", default="debug")
|
|
85
91
|
parser.add_argument("-r", "--remote", help="specified conan server", default="litebmc")
|
|
86
92
|
parser.add_argument("-l", "--lockfile", help="using conan.lock", action="store_true")
|
|
87
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")
|
|
88
95
|
targets = Config.target_list()
|
|
89
96
|
target_help = "build target:"
|
|
90
97
|
for tgt, _ in targets.items():
|
|
@@ -102,6 +109,45 @@ class Config(object):
|
|
|
102
109
|
return default
|
|
103
110
|
return manifest
|
|
104
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
|
+
|
|
105
151
|
def load_manifest(self):
|
|
106
152
|
"""加载manifest.yml并验证schema文件"""
|
|
107
153
|
template = {}
|
|
@@ -110,6 +156,19 @@ class Config(object):
|
|
|
110
156
|
template["download_path"] = os.path.join(self.download_path)
|
|
111
157
|
return load_yml_with_json_schema_validate(self.manifest, "/usr/share/litebmc/schema/pdf.v1.json", **template)
|
|
112
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
|
+
|
|
113
172
|
def set_build_type(self, value):
|
|
114
173
|
self.build_type = value
|
|
115
174
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import importlib
|
|
4
4
|
import os
|
|
5
5
|
import time
|
|
6
|
+
import traceback
|
|
6
7
|
import sys
|
|
7
8
|
|
|
8
9
|
from multiprocessing import Process
|
|
@@ -138,6 +139,7 @@ def task_handler(te:TaskExecutor):
|
|
|
138
139
|
try:
|
|
139
140
|
ret = te.run()
|
|
140
141
|
except Exception as e:
|
|
142
|
+
log.error(traceback.print_exc())
|
|
141
143
|
log.error(f"Task {te.status_key} exit with exceiption: {str(e)}")
|
|
142
144
|
with status_lock:
|
|
143
145
|
status_dict[te.status_key] = TASK_STATUS_EXCEPT
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""任务基础类"""
|
|
2
|
+
import importlib
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
import shutil
|
|
6
|
+
|
|
7
|
+
from multiprocessing import Process
|
|
8
|
+
from lbkit.log import Logger
|
|
9
|
+
from lbkit.tools import Tools
|
|
10
|
+
from lbkit.misc import load_yml_with_json_schema_validate, DownloadFlag
|
|
11
|
+
|
|
12
|
+
from lbkit.tasks.config import Config
|
|
13
|
+
|
|
14
|
+
class ManifestValidateError(OSError):
|
|
15
|
+
"""Raised when validation manifest.yml failed."""
|
|
16
|
+
|
|
17
|
+
class Task(Process):
|
|
18
|
+
"""任务基础类,提供run和install默认实现以及其它基础该当"""
|
|
19
|
+
def __init__(self, config: Config, name: str):
|
|
20
|
+
super().__init__()
|
|
21
|
+
self.log: Logger = Logger("task")
|
|
22
|
+
self.tools: Tools = Tools(name)
|
|
23
|
+
self.config: Config = config
|
|
24
|
+
self.name = name
|
|
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="", **kwargs):
|
|
31
|
+
kwargs["uptrace"] = kwargs.get("uptrace", 0) + 1
|
|
32
|
+
return self.tools.exec(cmd, verbose, ignore_error, sensitive, log_prefix, **kwargs)
|
|
33
|
+
|
|
34
|
+
def pipe(self, cmds: list[str], ignore_error=False, out_file = None, **kwargs):
|
|
35
|
+
kwargs["uptrace"] = kwargs.get("uptrace", 0) + 1
|
|
36
|
+
self.tools.pipe(cmds, ignore_error, out_file, **kwargs)
|
|
37
|
+
|
|
38
|
+
def exec_easy(self, cmd, ignore_error=False, **kwargs):
|
|
39
|
+
kwargs["uptrace"] = kwargs.get("uptrace", 0) + 1
|
|
40
|
+
return self.tools.run(cmd, ignore_error, **kwargs)
|
|
41
|
+
|
|
42
|
+
def do_hook(self, path):
|
|
43
|
+
"""执行任务钓子,用于定制化"""
|
|
44
|
+
try:
|
|
45
|
+
module = importlib.import_module(path)
|
|
46
|
+
except TypeError:
|
|
47
|
+
self.log.info("Load module(%s) failed, skip", path, uptrace=2)
|
|
48
|
+
return
|
|
49
|
+
self.log.info(f"load hook: {path}", uptrace=2)
|
|
50
|
+
hook = module.TaskHook(self.config, "do_hook")
|
|
51
|
+
hook.run()
|
|
52
|
+
|
|
53
|
+
def get_manifest_config(self, key: str, default=None):
|
|
54
|
+
return self.config.get_manifest_config(key, default)
|
|
55
|
+
|
|
56
|
+
def load_manifest(self):
|
|
57
|
+
"""加载manifest.yml并验证schema文件"""
|
|
58
|
+
return self.config.load_manifest()
|
|
59
|
+
|
|
60
|
+
def waitfile(self, src, timeout=1):
|
|
61
|
+
src = os.path.realpath(src)
|
|
62
|
+
if os.path.islink(src):
|
|
63
|
+
raise FileNotFoundError(f"Source file {src} is a symlink, copying failed")
|
|
64
|
+
if not os.path.isfile(src):
|
|
65
|
+
if timeout:
|
|
66
|
+
# 如果有timeout的,可能是正在下载的文件,需要等待超时时间再检查一次
|
|
67
|
+
time.sleep(timeout)
|
|
68
|
+
if not os.path.isfile(src):
|
|
69
|
+
raise FileNotFoundError(f"Source file {src} does not exist, copying failed")
|
|
70
|
+
else:
|
|
71
|
+
raise FileNotFoundError(f"Source file {src} does not exist, copying failed")
|
|
72
|
+
|
|
73
|
+
# 检查是否由download下载器下载,如果是,需要检查是否下载完成
|
|
74
|
+
if src.startswith(self.config.download_path):
|
|
75
|
+
timeout_cnt = 0
|
|
76
|
+
timeout_sec = 0
|
|
77
|
+
while True:
|
|
78
|
+
_, hash = DownloadFlag.read(src)
|
|
79
|
+
if hash:
|
|
80
|
+
filehash = self.tools.file_digest_sha256(src)
|
|
81
|
+
if hash != filehash:
|
|
82
|
+
DownloadFlag.clean(src)
|
|
83
|
+
raise Exception(f"The hash of file {src} is {filehash}, not equal to {hash}")
|
|
84
|
+
break
|
|
85
|
+
else:
|
|
86
|
+
time.sleep(0.1)
|
|
87
|
+
timeout_sec += 0.1
|
|
88
|
+
timeout_cnt += 0.1
|
|
89
|
+
# 每10秒打印一次等等日志
|
|
90
|
+
if timeout_sec == 10:
|
|
91
|
+
self.log.info(f"Wait file {src} download success")
|
|
92
|
+
timeout_sec = 0
|
|
93
|
+
# 60秒未创建文件的,判定文件不存在,中止构建
|
|
94
|
+
if timeout_cnt == 60 and not os.path.isfile(src):
|
|
95
|
+
raise Exception(f"Check file {src} failed because source file does not exist")
|
|
96
|
+
if timeout_cnt == 300:
|
|
97
|
+
raise Exception(f"Check file {src} failed because file was not downloaded within 300 seconds")
|
|
98
|
+
|
|
99
|
+
def copyfile(self, src, dst):
|
|
100
|
+
try:
|
|
101
|
+
self.waitfile(src)
|
|
102
|
+
except Exception as e:
|
|
103
|
+
raise Exception(f"Copy file {src} to {dst} failed because source file not ready") from e
|
|
104
|
+
|
|
105
|
+
dst = os.path.realpath(dst)
|
|
106
|
+
if not dst.startswith(self.config.temp_path):
|
|
107
|
+
raise FileNotFoundError(f"Destination file {dst} is not a subpath of {self.config.temp_path}")
|
|
108
|
+
|
|
109
|
+
if os.path.isfile(dst):
|
|
110
|
+
os.unlink(dst)
|
|
111
|
+
dst_dir = os.path.dirname(dst)
|
|
112
|
+
if not os.path.isdir(dst_dir):
|
|
113
|
+
os.makedirs(dst_dir)
|
|
114
|
+
if os.path.isfile(dst) or os.path.islink(dst):
|
|
115
|
+
os.unlink(dst)
|
|
116
|
+
self.log.debug(f"copy {src} to {dst}")
|
|
117
|
+
shutil.copyfile(src, dst)
|
|
118
|
+
|
|
119
|
+
def deal_conf(self, config_dict):
|
|
120
|
+
"""
|
|
121
|
+
处理每个Task的私有配置"work_config"
|
|
122
|
+
当work类有set_xxx类方法时,则可以在target文件中配置xxx
|
|
123
|
+
"""
|
|
124
|
+
if not config_dict:
|
|
125
|
+
return
|
|
126
|
+
for conf in config_dict:
|
|
127
|
+
try:
|
|
128
|
+
exist = getattr(self, f"set_{conf}")
|
|
129
|
+
val = config_dict.get(conf)
|
|
130
|
+
exist(val)
|
|
131
|
+
except Exception as e:
|
|
132
|
+
raise Exception(f"无效配置: {conf}, {e}") from e
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""环境准备"""
|
|
2
|
+
import os
|
|
3
|
+
import shutil
|
|
4
|
+
import jinja2
|
|
5
|
+
import configparser
|
|
6
|
+
from string import Template
|
|
7
|
+
from lbkit.tasks.config import Config
|
|
8
|
+
from lbkit.tasks.task import Task
|
|
9
|
+
from lbkit.log import Logger
|
|
10
|
+
from lbkit.misc import DownloadFlag
|
|
11
|
+
|
|
12
|
+
log = Logger("product_prepare")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ManifestValidateError(OSError):
|
|
16
|
+
"""Raised when validation manifest.yml failed."""
|
|
17
|
+
|
|
18
|
+
src_cwd = os.path.split(os.path.realpath(__file__))[0]
|
|
19
|
+
|
|
20
|
+
class TaskClass(Task):
|
|
21
|
+
def decompress_toolchain(self):
|
|
22
|
+
toolchain = self.config.get_product_config("toolchain")
|
|
23
|
+
if not toolchain:
|
|
24
|
+
# todo:toolchain成为强制配置项
|
|
25
|
+
return
|
|
26
|
+
file = toolchain.get("file")
|
|
27
|
+
strip = toolchain.get("strip_components", 0)
|
|
28
|
+
self.waitfile(file, 10)
|
|
29
|
+
compiler_file = os.path.join(self.config.compiler_path, "lbkit_cache")
|
|
30
|
+
_, hash = DownloadFlag.read(compiler_file)
|
|
31
|
+
if hash:
|
|
32
|
+
return
|
|
33
|
+
# 可能标记不匹配,所以尝试删除目录后重新创建
|
|
34
|
+
shutil.rmtree(self.config.compiler_path)
|
|
35
|
+
os.makedirs(self.config.compiler_path)
|
|
36
|
+
cmd = f"tar -xf {file} -C {self.config.compiler_path}"
|
|
37
|
+
if strip:
|
|
38
|
+
cmd += f" --strip-components={strip}"
|
|
39
|
+
self.exec(cmd)
|
|
40
|
+
sha256 = self.tools.file_digest_sha256(file)
|
|
41
|
+
DownloadFlag.create(compiler_file, file, sha256)
|
|
42
|
+
|
|
43
|
+
def get_conan_profile(self):
|
|
44
|
+
profile = self.config.get_product_config("toolchain/profile")
|
|
45
|
+
if profile:
|
|
46
|
+
file = profile.get("file")
|
|
47
|
+
name = profile.get("name")
|
|
48
|
+
else:
|
|
49
|
+
file = self.get_manifest_config("metadata/profile")
|
|
50
|
+
name = "litebmc"
|
|
51
|
+
self.waitfile(file)
|
|
52
|
+
return file, name
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def load_conan_profile(self):
|
|
56
|
+
profile, name = self.get_conan_profile()
|
|
57
|
+
log.info("Copy profile %s", profile)
|
|
58
|
+
profiles_dir = os.path.expanduser("~/.conan2/profiles")
|
|
59
|
+
if not os.path.isdir(profiles_dir):
|
|
60
|
+
cmd = "conan profile detect -f"
|
|
61
|
+
self.exec(cmd, ignore_error=True)
|
|
62
|
+
dst_profile = os.path.join(profiles_dir, name)
|
|
63
|
+
with open(dst_profile, "w+") as dst_fp:
|
|
64
|
+
src_fd = open(profile, "r")
|
|
65
|
+
template = Template(src_fd.read())
|
|
66
|
+
src_fd.close()
|
|
67
|
+
content = template.safe_substitute(compiler_path=self.config.compiler_path)
|
|
68
|
+
dst_fp.write(content)
|
|
69
|
+
|
|
70
|
+
with open(dst_profile, "r") as fp:
|
|
71
|
+
profile_data = jinja2.Template(fp.read()).render()
|
|
72
|
+
parser = configparser.ConfigParser()
|
|
73
|
+
parser.read_string(profile_data)
|
|
74
|
+
strip = "strip"
|
|
75
|
+
if parser.has_option("buildenv", "STRIP"):
|
|
76
|
+
strip = parser.get("buildenv", "STRIP")
|
|
77
|
+
path = ""
|
|
78
|
+
if parser.has_option("buildenv", "PATH+"):
|
|
79
|
+
path = parser.get("buildenv", "PATH+")
|
|
80
|
+
if path.startswith("(path)"):
|
|
81
|
+
path = path[6:]
|
|
82
|
+
elif parser.has_option("buildenv", "PATH"):
|
|
83
|
+
path = parser.get("buildenv", "PATH")
|
|
84
|
+
if path.startswith("(path)"):
|
|
85
|
+
path = path[6:]
|
|
86
|
+
self.config.strip = os.path.join(path, strip)
|
|
87
|
+
|
|
88
|
+
def run(self):
|
|
89
|
+
"""任务入口"""
|
|
90
|
+
self.decompress_toolchain()
|
|
91
|
+
"""检查manifest文件是否满足schema格式描述"""
|
|
92
|
+
self.config.load_manifest()
|
|
93
|
+
self.load_conan_profile()
|
|
94
|
+
|
|
95
|
+
if __name__ == "__main__":
|
|
96
|
+
config = Config()
|
|
97
|
+
build = TaskClass(config, "test")
|
|
98
|
+
build.run()
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""环境准备"""
|
|
2
|
+
import os
|
|
3
|
+
import requests
|
|
4
|
+
import shutil
|
|
5
|
+
import time
|
|
6
|
+
import traceback
|
|
7
|
+
from functools import partial
|
|
8
|
+
|
|
9
|
+
from lbkit.tasks.config import Config
|
|
10
|
+
from lbkit.tasks.task import Task
|
|
11
|
+
from lbkit.log import Logger
|
|
12
|
+
from lbkit.tools import Tools
|
|
13
|
+
from multiprocessing.pool import Pool, ApplyResult
|
|
14
|
+
from lbkit.misc import DownloadFlag
|
|
15
|
+
|
|
16
|
+
log = Logger("build_image")
|
|
17
|
+
|
|
18
|
+
src_cwd = os.path.split(os.path.realpath(__file__))[0]
|
|
19
|
+
|
|
20
|
+
class DownloadTask():
|
|
21
|
+
def __init__(self, record, dst_dir):
|
|
22
|
+
self.url = record.get("url")
|
|
23
|
+
self.file = record.get("file")
|
|
24
|
+
self.dst = os.path.join(dst_dir, self.file)
|
|
25
|
+
self.dst = os.path.realpath(self.dst)
|
|
26
|
+
if not self.dst.startswith(dst_dir):
|
|
27
|
+
raise Exception("Download {self.file} failed because file contain relative paths")
|
|
28
|
+
dir = os.path.dirname(self.dst)
|
|
29
|
+
if not os.path.isdir(dir):
|
|
30
|
+
os.makedirs(dir)
|
|
31
|
+
self.sha256 = record.get("sha256")
|
|
32
|
+
self.verify = record.get("verify", True)
|
|
33
|
+
|
|
34
|
+
def start(self):
|
|
35
|
+
last = time.time()
|
|
36
|
+
if os.path.isfile(self.dst):
|
|
37
|
+
calc_sha = Tools.file_digest_sha256(self.dst)
|
|
38
|
+
# 需要校验hash且hash不一致时删除文件
|
|
39
|
+
if self.sha256 != "any":
|
|
40
|
+
if self.sha256 != calc_sha:
|
|
41
|
+
os.unlink(self.dst)
|
|
42
|
+
else:
|
|
43
|
+
return
|
|
44
|
+
else:
|
|
45
|
+
url, hash = DownloadFlag.read(self.dst)
|
|
46
|
+
# flash标志文件记录的url、hash一致时无需重复下载
|
|
47
|
+
if url == self.url and hash == calc_sha:
|
|
48
|
+
return
|
|
49
|
+
print(f"Start downloading {self.file} from {self.url}")
|
|
50
|
+
DownloadFlag.clean(self.dst)
|
|
51
|
+
req = requests.get(self.url, stream=True, verify=self.verify, timeout=30)
|
|
52
|
+
req.raise_for_status()
|
|
53
|
+
total_size = int(req.headers.get('content-length', 0))
|
|
54
|
+
total_down = 0
|
|
55
|
+
fp = open(self.dst, 'wb')
|
|
56
|
+
for chunk in req.iter_content(chunk_size=16384):
|
|
57
|
+
if chunk:
|
|
58
|
+
fp.write(chunk)
|
|
59
|
+
total_down += len(chunk)
|
|
60
|
+
now = time.time()
|
|
61
|
+
# 每30秒打印一次进度
|
|
62
|
+
if now - last > 30:
|
|
63
|
+
print(f"File {self.dst} is downloading, downloaded {total_down} / {total_size} ")
|
|
64
|
+
last = now
|
|
65
|
+
fp.close()
|
|
66
|
+
calc_sha = Tools.file_digest_sha256(self.dst)
|
|
67
|
+
if self.sha256 != "any" and calc_sha != self.sha256:
|
|
68
|
+
os.unlink(self.dst)
|
|
69
|
+
raise Exception(f"File {self.file} downloaded but sha256 not match, need: {self.sha256}, get: {calc_sha}")
|
|
70
|
+
DownloadFlag.create(self.dst, self.url, calc_sha)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def download_filed(error, pool: Pool):
|
|
74
|
+
print(f"download file failed, error: {str(error)}")
|
|
75
|
+
pool.terminate()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class TaskClass(Task):
|
|
79
|
+
def run(self):
|
|
80
|
+
records = self.config.get_manifest_config("download", [])
|
|
81
|
+
if not records:
|
|
82
|
+
return
|
|
83
|
+
tasks: dict[str, DownloadTask] = {}
|
|
84
|
+
temps: list[DownloadTask] = []
|
|
85
|
+
for rec in records:
|
|
86
|
+
task = DownloadTask(rec, self.config.download_path)
|
|
87
|
+
temps.append(task)
|
|
88
|
+
if not os.path.isfile(task.dst):
|
|
89
|
+
with open(task.dst, "w+") as _:
|
|
90
|
+
pass
|
|
91
|
+
for task in temps:
|
|
92
|
+
if not task.url.startswith("file://"):
|
|
93
|
+
if tasks.get(task.file):
|
|
94
|
+
raise Exception(f"manifest.yml configuration error, file {task.file} repeatedly")
|
|
95
|
+
tasks[task.file] = task
|
|
96
|
+
continue
|
|
97
|
+
DownloadFlag.clean(task.dst)
|
|
98
|
+
src = task.url[7:]
|
|
99
|
+
# 源文件不存在
|
|
100
|
+
if not os.path.isfile(src):
|
|
101
|
+
raise Exception(f"Download failed, file {task.url} not exist")
|
|
102
|
+
if os.path.isfile(task.dst):
|
|
103
|
+
os.unlink(task.dst)
|
|
104
|
+
shutil.copyfile(src, task.dst)
|
|
105
|
+
calc_hash = self.tools.file_digest_sha256(task.dst)
|
|
106
|
+
DownloadFlag.create(task.dst, task.url, calc_hash)
|
|
107
|
+
pool = Pool()
|
|
108
|
+
results: list[(ApplyResult, DownloadTask)] = []
|
|
109
|
+
for _, task in tasks.items():
|
|
110
|
+
ec = partial(download_filed, pool=pool)
|
|
111
|
+
result = pool.apply_async(task.start, error_callback=ec)
|
|
112
|
+
results.append((result, task))
|
|
113
|
+
pool.close()
|
|
114
|
+
pool.join()
|
|
115
|
+
for result in results:
|
|
116
|
+
if not result[0].ready():
|
|
117
|
+
raise Exception(f"Download file {result[1].file} failed")
|
|
118
|
+
try:
|
|
119
|
+
result[0].get()
|
|
120
|
+
except:
|
|
121
|
+
log.error("Download with exception")
|
|
122
|
+
log.error(traceback.print_exc)
|
|
123
|
+
return -1
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
if __name__ == "__main__":
|
|
127
|
+
config = Config()
|
|
128
|
+
build = TaskClass(config, "test")
|
|
129
|
+
build.run()
|
|
@@ -47,8 +47,8 @@ lbkit/tasks/task_build_image.py
|
|
|
47
47
|
lbkit/tasks/task_build_manifest.py
|
|
48
48
|
lbkit/tasks/task_build_prepare.py
|
|
49
49
|
lbkit/tasks/task_build_rootfs.py
|
|
50
|
+
lbkit/tasks/task_download.py
|
|
50
51
|
lbkit/tasks/task_test.py
|
|
51
|
-
lbkit/tasks/test.py
|
|
52
52
|
lbkit/tasks/template/conanfile.py.mako
|
|
53
53
|
lbkit/tasks/template/rootfs.py.mako
|
|
54
54
|
lbkit/utils/__init__.py
|
|
@@ -57,4 +57,5 @@ lbkit/utils/images/__init__.py
|
|
|
57
57
|
lbkit/utils/images/emmc.py
|
|
58
58
|
test/__init__.py
|
|
59
59
|
test/test_codegen.py
|
|
60
|
+
test/test_config.py
|
|
60
61
|
test/test_helper.py
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from lbkit.tasks.config import Config
|
|
3
|
+
|
|
4
|
+
class TestConfig(unittest.TestCase):
|
|
5
|
+
def test_config_merge_config(self):
|
|
6
|
+
src = "123"
|
|
7
|
+
dst = "abc"
|
|
8
|
+
out = Config.merge_cfg(dst, src)
|
|
9
|
+
self.assertEqual(out, src)
|
|
10
|
+
src = None
|
|
11
|
+
dst = "abc"
|
|
12
|
+
out = Config.merge_cfg(dst, src)
|
|
13
|
+
self.assertEqual(out, dst)
|
|
14
|
+
src = "123"
|
|
15
|
+
dst = None
|
|
16
|
+
out = Config.merge_cfg(dst, src)
|
|
17
|
+
self.assertEqual(out, src)
|
|
18
|
+
src = ["123"]
|
|
19
|
+
dst = None
|
|
20
|
+
out = Config.merge_cfg(dst, src)
|
|
21
|
+
self.assertEqual(out, src)
|
|
22
|
+
src = ["123"]
|
|
23
|
+
dst = ["abc"]
|
|
24
|
+
out = Config.merge_cfg(dst, src)
|
|
25
|
+
self.assertEqual(out, src)
|
|
26
|
+
src = []
|
|
27
|
+
dst = ["abc"]
|
|
28
|
+
out = Config.merge_cfg(dst, src)
|
|
29
|
+
src = None
|
|
30
|
+
dst = ["abc"]
|
|
31
|
+
out = Config.merge_cfg(dst, src)
|
|
32
|
+
src = None
|
|
33
|
+
dst = []
|
|
34
|
+
out = Config.merge_cfg(dst, src)
|
|
35
|
+
self.assertEqual(out, dst)
|
|
36
|
+
src = {"key1": "val1"}
|
|
37
|
+
dst = {"key1": "val2"}
|
|
38
|
+
out = Config.merge_cfg(dst, src)
|
|
39
|
+
self.assertEqual(out, src)
|
|
40
|
+
src = {"key1": "val1", "key2": [123, 234]}
|
|
41
|
+
dst = {"key1": "val2"}
|
|
42
|
+
out = Config.merge_cfg(dst, src)
|
|
43
|
+
self.assertEqual(out, src)
|
|
44
|
+
src = {"key2": [123, 234]}
|
|
45
|
+
dst = {"key1": "val2"}
|
|
46
|
+
out = Config.merge_cfg(dst, src)
|
|
47
|
+
self.assertEqual(out, {"key1": "val2", "key2": [123, 234]})
|
|
48
|
+
src = {"key2": [123, 234]}
|
|
49
|
+
dst = {"key1": "val2", "key2": [456]}
|
|
50
|
+
out = Config.merge_cfg(dst, src)
|
|
51
|
+
self.assertEqual(out, {"key1": "val2", "key2": [123, 234]})
|
|
52
|
+
src = {"key2": [123, 234]}
|
|
53
|
+
dst = {"key1": "val2", "key2": []}
|
|
54
|
+
out = Config.merge_cfg(dst, src)
|
|
55
|
+
self.assertEqual(out, {"key1": "val2", "key2": [123, 234]})
|
|
56
|
+
src = {"key2": [123, 234]}
|
|
57
|
+
dst = {"key1": "val2", "key2": None}
|
|
58
|
+
out = Config.merge_cfg(dst, src)
|
|
59
|
+
self.assertEqual(out, {"key1": "val2", "key2": [123, 234]})
|
|
60
|
+
src = {"key2": None}
|
|
61
|
+
dst = {"key1": "val2", "key2": []}
|
|
62
|
+
out = Config.merge_cfg(dst, src)
|
|
63
|
+
self.assertEqual(out, {"key1": "val2", "key2": []})
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
unittest.main()
|
lbkit-0.8.5/lbkit/__init__.py
DELETED
lbkit-0.8.5/lbkit/tasks/task.py
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
"""任务基础类"""
|
|
2
|
-
import importlib
|
|
3
|
-
import traceback
|
|
4
|
-
from multiprocessing import Process
|
|
5
|
-
from lbkit.log import Logger
|
|
6
|
-
from lbkit.tools import Tools
|
|
7
|
-
from lbkit.misc import load_yml_with_json_schema_validate
|
|
8
|
-
|
|
9
|
-
from lbkit.tasks.config import Config
|
|
10
|
-
|
|
11
|
-
class ManifestValidateError(OSError):
|
|
12
|
-
"""Raised when validation manifest.yml failed."""
|
|
13
|
-
|
|
14
|
-
class Task(Process):
|
|
15
|
-
"""任务基础类,提供run和install默认实现以及其它基础该当"""
|
|
16
|
-
def __init__(self, config: Config, name: str):
|
|
17
|
-
super().__init__()
|
|
18
|
-
self.log: Logger = Logger("task")
|
|
19
|
-
self.tools: Tools = Tools(name)
|
|
20
|
-
self.config: Config = config
|
|
21
|
-
self.name = name
|
|
22
|
-
|
|
23
|
-
def install(self):
|
|
24
|
-
"""安装任务"""
|
|
25
|
-
self.log.info("install...........")
|
|
26
|
-
|
|
27
|
-
def exec(self, cmd: str, verbose=False, ignore_error = False, sensitive=False, log_prefix="", **kwargs):
|
|
28
|
-
kwargs["uptrace"] = kwargs.get("uptrace", 0) + 1
|
|
29
|
-
return self.tools.exec(cmd, verbose, ignore_error, sensitive, log_prefix, **kwargs)
|
|
30
|
-
|
|
31
|
-
def pipe(self, cmds: list[str], ignore_error=False, out_file = None, **kwargs):
|
|
32
|
-
kwargs["uptrace"] = kwargs.get("uptrace", 0) + 1
|
|
33
|
-
self.tools.pipe(cmds, ignore_error, out_file, **kwargs)
|
|
34
|
-
|
|
35
|
-
def exec_easy(self, cmd, ignore_error=False, **kwargs):
|
|
36
|
-
kwargs["uptrace"] = kwargs.get("uptrace", 0) + 1
|
|
37
|
-
return self.tools.run(cmd, ignore_error, **kwargs)
|
|
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, uptrace=2)
|
|
45
|
-
return
|
|
46
|
-
self.log.info(f"load hook: {path}", uptrace=2)
|
|
47
|
-
hook = module.TaskHook(self.config, "do_hook")
|
|
48
|
-
hook.run()
|
|
49
|
-
|
|
50
|
-
def get_manifest_config(self, key: str, default=None):
|
|
51
|
-
return self.config.get_manifest_config(key, default)
|
|
52
|
-
|
|
53
|
-
def load_manifest(self):
|
|
54
|
-
"""加载manifest.yml并验证schema文件"""
|
|
55
|
-
return self.config.load_manifest()
|
|
56
|
-
|
|
57
|
-
def deal_conf(self, config_dict):
|
|
58
|
-
"""
|
|
59
|
-
处理每个Task的私有配置"work_config"
|
|
60
|
-
当work类有set_xxx类方法时,则可以在target文件中配置xxx
|
|
61
|
-
"""
|
|
62
|
-
if not config_dict:
|
|
63
|
-
return
|
|
64
|
-
for conf in config_dict:
|
|
65
|
-
try:
|
|
66
|
-
exist = getattr(self, f"set_{conf}")
|
|
67
|
-
val = config_dict.get(conf)
|
|
68
|
-
exist(val)
|
|
69
|
-
except Exception as e:
|
|
70
|
-
raise Exception(f"无效配置: {conf}, {e}") from e
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
"""环境准备"""
|
|
2
|
-
import os
|
|
3
|
-
import shutil
|
|
4
|
-
import jinja2
|
|
5
|
-
import configparser
|
|
6
|
-
from lbkit.tasks.config import Config
|
|
7
|
-
from lbkit.tasks.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 TaskClass(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
|
-
if not os.path.isdir(profiles_dir):
|
|
26
|
-
cmd = "conan profile detect -f"
|
|
27
|
-
self.exec(cmd, ignore_error=True)
|
|
28
|
-
dst_profile = os.path.join(profiles_dir, os.path.basename(profile))
|
|
29
|
-
if os.path.isdir(profiles_dir):
|
|
30
|
-
shutil.copyfile(profile, dst_profile, follow_symlinks=False)
|
|
31
|
-
|
|
32
|
-
with open(dst_profile, "r") as fp:
|
|
33
|
-
profile_data = jinja2.Template(fp.read()).render()
|
|
34
|
-
parser = configparser.ConfigParser()
|
|
35
|
-
parser.read_string(profile_data)
|
|
36
|
-
strip = "strip"
|
|
37
|
-
if parser.has_option("buildenv", "STRIP"):
|
|
38
|
-
strip = parser.get("buildenv", "STRIP")
|
|
39
|
-
path = ""
|
|
40
|
-
if parser.has_option("buildenv", "PATH+"):
|
|
41
|
-
path = parser.get("buildenv", "PATH+")
|
|
42
|
-
if path.startswith("(path)"):
|
|
43
|
-
path = path[6:]
|
|
44
|
-
elif parser.has_option("buildenv", "PATH"):
|
|
45
|
-
path = parser.get("buildenv", "PATH")
|
|
46
|
-
if path.startswith("(path)"):
|
|
47
|
-
path = path[6:]
|
|
48
|
-
self.config.strip = os.path.join(path, strip)
|
|
49
|
-
|
|
50
|
-
def run(self):
|
|
51
|
-
"""任务入口"""
|
|
52
|
-
"""检查manifest文件是否满足schema格式描述"""
|
|
53
|
-
self.config.load_manifest()
|
|
54
|
-
self.load_conan_profile()
|
|
55
|
-
|
|
56
|
-
if __name__ == "__main__":
|
|
57
|
-
config = Config()
|
|
58
|
-
build = TaskClass(config)
|
|
59
|
-
build.run()
|
lbkit-0.8.5/lbkit/tasks/test.py
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
from multiprocessing import Pool
|
|
2
|
-
from multiprocessing.pool import ApplyResult
|
|
3
|
-
import time
|
|
4
|
-
from functools import partial
|
|
5
|
-
|
|
6
|
-
def square(number):
|
|
7
|
-
"""计算一个数的平方"""
|
|
8
|
-
print(f"计算 {number} 的平方")
|
|
9
|
-
time.sleep(1) # 模拟耗时操作
|
|
10
|
-
if number % 10 == 9:
|
|
11
|
-
raise Exception("sssssssssss")
|
|
12
|
-
# return number * number
|
|
13
|
-
|
|
14
|
-
def task_error(pool, number, error):
|
|
15
|
-
print(f"Work {number} exist with error: {error}")
|
|
16
|
-
pool.terminate()
|
|
17
|
-
|
|
18
|
-
if __name__ == "__main__":
|
|
19
|
-
# 创建任务并等待完成
|
|
20
|
-
results: dict[str, ApplyResult] = {}
|
|
21
|
-
pool = Pool(3)
|
|
22
|
-
for work in range(100):
|
|
23
|
-
error_cb = partial(task_error, pool, work)
|
|
24
|
-
result = pool.apply_async(square, args=(work, ), error_callback=error_cb)
|
|
25
|
-
results[work] = result
|
|
26
|
-
# 检查任务结果
|
|
27
|
-
pool.close()
|
|
28
|
-
pool.join()
|
|
29
|
-
# print("xxxxxxxxxxxxxxxxxx")
|
|
30
|
-
# time.sleep(100)
|
|
31
|
-
for number, result in results.items():
|
|
32
|
-
try:
|
|
33
|
-
if not result.ready():
|
|
34
|
-
succ = False
|
|
35
|
-
else:
|
|
36
|
-
result.get()
|
|
37
|
-
except Exception as e:
|
|
38
|
-
print(f"任务{number}执行异常: {str(e)}")
|
|
39
|
-
succ = False
|
|
40
|
-
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|