lbkit 0.9.2__tar.gz → 0.9.4__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 (70) hide show
  1. {lbkit-0.9.2 → lbkit-0.9.4}/AUTHORS +1 -1
  2. {lbkit-0.9.2/lbkit.egg-info → lbkit-0.9.4}/PKG-INFO +1 -1
  3. lbkit-0.9.4/lbkit/__init__.py +2 -0
  4. lbkit-0.9.4/lbkit/build_conan_parallel.py +61 -0
  5. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/idf_interface.py +2 -0
  6. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/template/public.c.mako +32 -5
  7. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/template/public.h.mako +8 -0
  8. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/component/build.py +18 -15
  9. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/component/test.py +4 -3
  10. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/log.py +29 -19
  11. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/config.py +0 -2
  12. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/task_build_manifest.py +27 -40
  13. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/task_build_prepare.py +4 -2
  14. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tools.py +54 -28
  15. {lbkit-0.9.2 → lbkit-0.9.4/lbkit.egg-info}/PKG-INFO +1 -1
  16. lbkit-0.9.2/lbkit/__init__.py +0 -2
  17. lbkit-0.9.2/lbkit/build_conan_parallel.py +0 -206
  18. {lbkit-0.9.2 → lbkit-0.9.4}/LICENSE +0 -0
  19. {lbkit-0.9.2 → lbkit-0.9.4}/MANIFEST.in +0 -0
  20. {lbkit-0.9.2 → lbkit-0.9.4}/README.md +0 -0
  21. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/ci_robot/__init__.py +0 -0
  22. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/ci_robot/gitee.py +0 -0
  23. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/cli.py +0 -0
  24. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/__init__.py +0 -0
  25. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/codegen.py +0 -0
  26. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/ctype_defination.py +0 -0
  27. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/renderer.py +0 -0
  28. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/template/client.c.mako +0 -0
  29. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/template/client.h.mako +0 -0
  30. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/template/interface.c.mako +0 -0
  31. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/template/interface.introspect.xml.mako +0 -0
  32. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/template/server.c.mako +0 -0
  33. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/codegen/template/server.h.mako +0 -0
  34. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/component/__init__.py +0 -0
  35. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/component/arg_parser.py +0 -0
  36. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/component/template/conanbase.mako +0 -0
  37. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/component/template/deploy.mako +0 -0
  38. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/errors.py +0 -0
  39. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/helper.py +0 -0
  40. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/lbkit.py +0 -0
  41. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/misc.py +0 -0
  42. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/__init__.py +0 -0
  43. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/executor.py +0 -0
  44. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/image_maker/__init__.py +0 -0
  45. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/image_maker/make_image.py +0 -0
  46. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/image_maker/make_qemu_image.py +0 -0
  47. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/image_maker/make_rockchip_image.py +0 -0
  48. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/task.py +0 -0
  49. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/task_build_image.py +0 -0
  50. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/task_build_rootfs.py +0 -0
  51. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/task_download.py +0 -0
  52. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/template/conanfile.py.mako +0 -0
  53. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/tasks/template/rootfs.py.mako +0 -0
  54. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/ukr/__init__.py +0 -0
  55. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/ukr/build.py +0 -0
  56. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/utils/__init__.py +0 -0
  57. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/utils/env_detector.py +0 -0
  58. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/utils/images/__init__.py +0 -0
  59. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit/utils/images/emmc.py +0 -0
  60. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit.egg-info/SOURCES.txt +0 -0
  61. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit.egg-info/dependency_links.txt +0 -0
  62. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit.egg-info/entry_points.txt +0 -0
  63. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit.egg-info/requires.txt +0 -0
  64. {lbkit-0.9.2 → lbkit-0.9.4}/lbkit.egg-info/top_level.txt +0 -0
  65. {lbkit-0.9.2 → lbkit-0.9.4}/setup.cfg +0 -0
  66. {lbkit-0.9.2 → lbkit-0.9.4}/setup.py +0 -0
  67. {lbkit-0.9.2 → lbkit-0.9.4}/test/__init__.py +0 -0
  68. {lbkit-0.9.2 → lbkit-0.9.4}/test/test_codegen.py +0 -0
  69. {lbkit-0.9.2 → lbkit-0.9.4}/test/test_config.py +0 -0
  70. {lbkit-0.9.2 → lbkit-0.9.4}/test/test_helper.py +0 -0
@@ -1,4 +1,4 @@
1
- Below are just a few of the people who have contributed to litebmc/toolkit.
1
+ Below are just a few of the people who have contributed to litebmc/lbkit.
2
2
 
3
3
  ----------
4
4
  xuhaijun <xuhj@litebmc.com>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lbkit
3
- Version: 0.9.2
3
+ Version: 0.9.4
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.9.4'
@@ -0,0 +1,61 @@
1
+ """任务基础类"""
2
+ import os
3
+ import json
4
+ import tempfile
5
+ from lbkit.log import Logger
6
+ from concurrent.futures import ThreadPoolExecutor, Future, as_completed
7
+ from lbkit import misc
8
+ from lbkit.tools import Tools
9
+ from lbkit.misc import Color
10
+
11
+ tools = Tools("comp_build")
12
+ log = Logger("comp_build")
13
+
14
+
15
+ class BuildConanParallel(object):
16
+ def __init__(self, orderinfo, remote):
17
+ self.orderinfo = orderinfo
18
+ self.remote = remote
19
+
20
+ def _build_package(self, logfile, build_args, public_args):
21
+ cmd = f"conan install {build_args} {public_args}"
22
+ log.info("{}build start: '{}".format(Color.GREEN, Color.RESET_ALL) + cmd + ' log file: ' + logfile)
23
+ tools.exec(cmd, ignore_error=True, log_name=logfile, echo_cmd=False)
24
+ log.info("{}build finished: '{}".format(Color.GREEN, Color.RESET_ALL) + cmd + ' log file: ' + logfile)
25
+
26
+ def _build(self):
27
+ with open(self.orderinfo, "r") as fp:
28
+ order_info = json.load(fp)
29
+ public_args = order_info.get("profiles", {}).get("self", {}).get("args", "")
30
+ for orders in order_info.get("order", []):
31
+ threadPool = ThreadPoolExecutor(max_workers=(os.cpu_count() + 1) // 2)
32
+ results: list[Future] = []
33
+ for order in orders:
34
+ ref = order.get("ref")
35
+ pkgname = ref.split("/")[0]
36
+ logfile = f"{misc.LOG_DIR}/conan_{pkgname}.log"
37
+ packages = order.get("packages", [])
38
+ for package in packages:
39
+ for pkg in package:
40
+ binary = pkg.get("binary", "")
41
+ if binary == 'Download':
42
+ pkgid = pkg.get("package_id")
43
+ cmd = f'conan download {ref}:{pkgid}'
44
+ if self.remote:
45
+ cmd += f' -r {self.remote}'
46
+ tools.exec(cmd)
47
+ continue
48
+ if binary != "Build":
49
+ continue
50
+ build_args = pkg.get("build_args", "")
51
+ result = threadPool.submit(self._build_package, logfile, build_args, public_args)
52
+ results.append(result)
53
+ for result in as_completed(results):
54
+ result.result()
55
+
56
+ def build(self):
57
+ cwd = os.getcwd()
58
+ tmp = tempfile.TemporaryDirectory()
59
+ os.chdir(tmp.name)
60
+ self._build()
61
+ os.chdir(cwd)
@@ -987,6 +987,8 @@ class IdfPluginAction(IdfMethod):
987
987
  self.policy = method_data.get("policy", "continue_always")
988
988
  super().__init__(intf, method_data)
989
989
  self.is_plugin = True
990
+ if self.policy == "continue_always" and method_data.get("returns", []):
991
+ raise Exception(f"The policy of plugin {self.name} is 'continue_always', does't supported 'returns' property, interface {self.intf.name} failed to generate code")
990
992
 
991
993
 
992
994
  class IdfEnumeration(IdfBase):
@@ -1218,7 +1218,7 @@ int ${class_name}_${action.name}_register(const gchar *req_signature, const gcha
1218
1218
  g_mutex_lock(&_${class_name}_${action.name}_lock);
1219
1219
  for (GSList *item = _${class_name}_${action.name}_actions; item; item = item->next) {
1220
1220
  _${class_name}_${action.name}_PluginAction *old_handler = (_${class_name}_${action.name}_PluginAction *)item->data;
1221
- if (action == old_handler->action || user_data == old_handler->user_data) {
1221
+ if (action == old_handler->action && user_data == old_handler->user_data) {
1222
1222
  g_mutex_unlock(&_${class_name}_${action.name}_lock);
1223
1223
  return 0;
1224
1224
  }
@@ -1256,9 +1256,18 @@ void ${class_name}_${action.name}_unregister(${class_name}_${action.name}_action
1256
1256
  <% REQ_PARA = f', const {class_name}_{action.name}_Req *req' %>\
1257
1257
  <% REQ_NAME = f', req' %>\
1258
1258
  % endif
1259
+ % if action.policy == "continue_always":
1260
+ void ${class_name}_${action.name}_run(${class_name} obj${REQ_PARA}${RSP_PARA})
1261
+ % else:
1259
1262
  int ${class_name}_${action.name}_run(${class_name} obj${REQ_PARA}${RSP_PARA})
1263
+ % endif
1260
1264
  {
1265
+ % if action.policy == "return_any_success":
1266
+ gint result = -1;
1267
+ % elif action.policy == "return_any_fail":
1261
1268
  gint result = 0;
1269
+ % endif
1270
+
1262
1271
  g_mutex_lock(&_${class_name}_${action.name}_lock);
1263
1272
  for (GSList *item = _${class_name}_${action.name}_actions; item; item = item->next) {
1264
1273
  _${class_name}_${action.name}_PluginAction *handler = (_${class_name}_${action.name}_PluginAction *)item->data;
@@ -1266,24 +1275,42 @@ int ${class_name}_${action.name}_run(${class_name} obj${REQ_PARA}${RSP_PARA})
1266
1275
  if (rsp && *rsp) {
1267
1276
  ${class_name}_${action.name}_Rsp_free(rsp);
1268
1277
  }
1278
+ % if action.policy == "continue_always":
1279
+ handler->action(obj${REQ_NAME}, rsp, handler->user_data);
1280
+ % else:
1269
1281
  gint ret = handler->action(obj${REQ_NAME}, rsp, handler->user_data);
1282
+ % endif
1270
1283
  % else:
1284
+ % if action.policy == "continue_always":
1285
+ handler->action(obj${REQ_NAME}, handler->user_data);
1286
+ % else:
1271
1287
  gint ret = handler->action(obj${REQ_NAME}, handler->user_data);
1288
+ % endif
1272
1289
  % endif
1273
- if (ret != 0)
1274
- result = ret;
1275
1290
  % if action.policy == "return_any_success":
1276
1291
  /* return when any action success(ret == 0) */
1277
- if (ret == 0)
1292
+ if (ret == 0) {
1293
+ result = 0;
1278
1294
  break;
1295
+ }
1279
1296
  % elif action.policy == "return_any_fail":
1280
1297
  /* return when any action failed(ret != 0) */
1281
- if (ret != 0)
1298
+ if (ret != 0) {
1299
+ result = ret;
1282
1300
  break;
1301
+ }
1283
1302
  % endif
1284
1303
  }
1304
+ % if len(action.returns.parameters) > 0:
1305
+ if (result != 0 && *rsp) {
1306
+ ${class_name}_${action.name}_Rsp_free(rsp);
1307
+ *rsp = NULL;
1308
+ }
1309
+ % endif
1285
1310
  g_mutex_unlock(&_${class_name}_${action.name}_lock);
1311
+ % if action.policy != "continue_always":
1286
1312
  return result;
1313
+ % endif
1287
1314
  }
1288
1315
 
1289
1316
  % endfor
@@ -320,13 +320,21 @@ typedef struct {
320
320
  % if len(action.parameters.parameters) > 0:
321
321
  <% REQ_PARA = f', const {class_name}_{action.name}_Req *req' %>\
322
322
  % endif
323
+ % if action.policy == "continue_always":
324
+ typedef void (*${class_name}_${action.name}_action)(${class_name} obj${REQ_PARA}${RSP_PARA}, gpointer user_data);
325
+ % else:
323
326
  typedef int (*${class_name}_${action.name}_action)(${class_name} obj${REQ_PARA}${RSP_PARA}, gpointer user_data);
327
+ % endif
324
328
 
325
329
  /* Register a new plugin action, can't register repeated with same action and user_data */
326
330
  int ${class_name}_${action.name}_register(const gchar *req_signature, const gchar *rsp_signature,
327
331
  ${class_name}_${action.name}_action action, gpointer user_data);
328
332
  void ${class_name}_${action.name}_unregister(${class_name}_${action.name}_action action);
333
+ % if action.policy == "continue_always":
334
+ void ${class_name}_${action.name}_run(${class_name} obj${REQ_PARA}${RSP_PARA});
335
+ % else:
329
336
  int ${class_name}_${action.name}_run(${class_name} obj${REQ_PARA}${RSP_PARA});
337
+ % endif
330
338
 
331
339
  % endfor
332
340
  % endif
@@ -4,6 +4,7 @@ import yaml
4
4
  import shutil
5
5
  import re
6
6
  import json
7
+ import sys
7
8
  from multiprocessing import Pool
8
9
  import traceback
9
10
  from argparse import ArgumentParser
@@ -206,23 +207,25 @@ class BuildComponent():
206
207
  export_cmd = "conan export . "
207
208
  if self.user != "litebmc" or self.channel != "release":
208
209
  export_cmd += f"--user={self.user} --channel={self.channel}"
209
- tools.run(export_cmd, capture_output=False)
210
- # import sys
211
- # sys.exit(-1)
210
+ tools.run(export_cmd, capture_output=False, stdout=sys.stdout, stderr=sys.stdout)
212
211
 
213
212
  lockfile = os.path.join(self.cwd, ".temp", "conan.lock")
213
+ orderfile = os.path.join(self.cwd, ".temp", "graph.order")
214
+ graph_cmd = f"conan graph build-order . {self.base_cmd} --order-by=recipe -f json --out-file={orderfile}"
215
+ if self.from_source:
216
+ graph_cmd += " --build='*'"
217
+ else:
218
+ graph_cmd += " --build=missing"
219
+ graph_cmd += f" --lockfile-out={lockfile}"
220
+ tools.run(graph_cmd)
221
+ bcp = BuildConanParallel(orderfile, self.options.remote)
222
+ bcp.build()
223
+ cmd = f"conan create {self.base_cmd} --build='{self.name}/*'"
224
+ tools.run(cmd, capture_output=False)
225
+
214
226
  graphfile = os.path.join(self.cwd, ".temp", "graph.info")
215
- lock_cmd = f"conan lock create . {self.base_cmd} --lockfile-out={lockfile}"
216
- tools.run(lock_cmd, capture_output=False)
217
227
  graph_cmd = f"conan graph info . {self.base_cmd} -f json --lockfile={lockfile}"
218
228
  tools.pipe([graph_cmd], out_file=graphfile)
219
- log.success(f"start build dependency packages of {self.package}")
220
- bcp = BuildConanParallel(graphfile, lockfile, self.base_cmd, self.from_source)
221
- bcp.build()
222
-
223
- cmd = f"conan create . {self.base_cmd} --build=missing"
224
- log.info(f"start build {self.package}: {cmd}")
225
- tools.run(cmd, capture_output=False)
226
229
 
227
230
  log.success(f"start deploy {self.package} and is's dependency packages")
228
231
  self.deploy(graphfile)
@@ -315,7 +318,7 @@ class BuildComponent():
315
318
  @property
316
319
  def package_id(self):
317
320
  cmd = f"conan graph info . {self.base_cmd} --filter=package_id"
318
- res = tools.run(cmd)
321
+ res = tools.run(cmd, capture_output=True)
319
322
  match = re.search(r"package_id: ([a-f0-9]{40})", res.stdout)
320
323
  if match is None:
321
324
  raise errors.LiteBmcException(f"Get package if of {self.package} failed")
@@ -324,13 +327,13 @@ class BuildComponent():
324
327
  @staticmethod
325
328
  def package_folder(self):
326
329
  cmd = f"conan cache path {self.package}#latest:{self.package_id}"
327
- res = tools.run(cmd)
330
+ res = tools.run(cmd, capture_output=True)
328
331
  return res.stdout.strip()
329
332
 
330
333
  @property
331
334
  def build_folder(self):
332
335
  cmd = f"conan cache path {self.package}#latest:{self.package_id} --folder=build"
333
- res = tools.run(cmd)
336
+ res = tools.run(cmd, capture_output=True)
334
337
  return res.stdout.strip()
335
338
 
336
339
  def test(self):
@@ -52,12 +52,12 @@ class TestComponent():
52
52
  shutil.rmtree(coverage_dir, ignore_errors=True)
53
53
  os.makedirs(coverage_dir)
54
54
  # 覆盖率数据路径由conanbase.make在build配置cflags时定义
55
- cmd = f"lcov -c -q -d {build_folder} -o {coverage_dir}/cover.info"
55
+ cmd = f"lcov --compat-libtool -c -q -d {build_folder} -o {coverage_dir}/cover.info"
56
56
  tool.exec(cmd)
57
57
  for dir in test_src_folder:
58
- cmd = f"lcov -r {coverage_dir}/cover.info \"{build_folder}/{dir}/*\" -o {coverage_dir}/cover.info"
58
+ cmd = f"lcov --compat-libtool -r {coverage_dir}/cover.info \"{build_folder}/{dir}/*\" -o {coverage_dir}/cover.info"
59
59
  tool.exec(cmd)
60
- cmd = f"lcov -r {coverage_dir}/cover.info \"*/include/*\" -o {coverage_dir}/cover.info"
60
+ cmd = f"lcov --compat-libtool -r {coverage_dir}/cover.info \"*/include/*\" -o {coverage_dir}/cover.info"
61
61
  tool.exec(cmd)
62
62
  cmd = f"genhtml -o {coverage_dir}/html --legend {coverage_dir}/cover.info"
63
63
  tool.exec(cmd)
@@ -193,6 +193,7 @@ class TestComponent():
193
193
  with open(term_flag, "w") as fp:
194
194
  pass
195
195
  time.sleep(1)
196
+ os.unlink(term_flag)
196
197
  # 设置ROOTFS_DIR环境变量,为DT测试提供相对路径
197
198
  self._collect_coverage_data(build.build_folder, test_src_folder)
198
199
  log.success("Test finished")
@@ -1,41 +1,47 @@
1
1
  import logging
2
2
  import inspect
3
3
  import os
4
+ import sys
4
5
  from lbkit.misc import Color
5
6
 
6
7
  class Logger(logging.getLoggerClass()):
7
8
 
8
9
  def __init__(self, *args, **kwargs):
9
10
  super(Logger, self).__init__(*args, **kwargs)
10
- self.setLevel(logging.INFO)
11
- handler = logging.StreamHandler()
12
11
  self.logenv = os.environ.get("LOG")
13
12
  if self.logenv is None:
14
13
  formatter = logging.Formatter('%(message)s')
15
- self.setLevel(logging.INFO)
14
+ self._set_handler(logging.INFO, formatter)
16
15
  else:
17
- formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
16
+ formatter = logging.Formatter('%(asctime)s %(message)s')
18
17
  if self.logenv == "info":
19
- self.setLevel(logging.INFO)
18
+ self._set_handler(logging.INFO, formatter)
20
19
  elif self.logenv == "warn":
21
- self.setLevel(logging.WARNING)
22
- elif self.logenv == "error":
23
- self.setLevel(logging.ERROR)
24
- else:
25
- self.setLevel(logging.DEBUG)
20
+ self._set_handler(logging.WARNING, formatter)
21
+ elif self.logenv != "error":
22
+ self._set_handler(logging.DEBUG, formatter)
23
+ self._set_handler(logging.ERROR, formatter)
24
+
25
+ def _set_handler(self, level, formatter):
26
+ if level == logging.ERROR:
27
+ handler = logging.StreamHandler()
28
+ else:
29
+ handler = logging.StreamHandler(sys.stdout)
30
+ handler.addFilter(lambda record: record.levelno < logging.ERROR)
31
+ handler.setLevel(level)
26
32
  handler.setFormatter(formatter)
27
- self.handlers = []
28
33
  self.addHandler(handler)
29
34
 
35
+
30
36
  def error(self, msg, *args, **kwargs):
31
37
  if self.logenv:
32
38
  uptrace = kwargs.get("uptrace", 0)
33
39
  uptrace += 1
34
40
  stack = inspect.stack()[uptrace]
35
41
  filename = os.path.basename(stack.filename)
36
- msg = f"[{filename}:{stack.lineno}] " + Color.RED + msg + Color.RESET_ALL
42
+ msg = "ERROR: " + f"[{filename}:{stack.lineno}] " + Color.RED + msg + Color.RESET_ALL
37
43
  else:
38
- msg = Color.RED + msg + Color.RESET_ALL
44
+ msg = "ERROR: " + Color.RED + msg + Color.RESET_ALL
39
45
  kwargs.pop("uptrace", None)
40
46
  super(Logger, self).error(msg, *args, **kwargs)
41
47
 
@@ -45,7 +51,9 @@ class Logger(logging.getLoggerClass()):
45
51
  uptrace += 1
46
52
  stack = inspect.stack()[uptrace]
47
53
  filename = os.path.basename(stack.filename)
48
- msg = f"[{filename}:{stack.lineno}] " + msg
54
+ msg = "DEBUG: " + f"[{filename}:{stack.lineno}] " + msg
55
+ else:
56
+ msg = "DEBUG: " + msg
49
57
  kwargs.pop("uptrace", None)
50
58
  super(Logger, self).debug(msg, *args, **kwargs)
51
59
 
@@ -55,7 +63,9 @@ class Logger(logging.getLoggerClass()):
55
63
  uptrace += 1
56
64
  stack = inspect.stack()[uptrace]
57
65
  filename = os.path.basename(stack.filename)
58
- msg = f"[{filename}:{stack.lineno}] " + msg
66
+ msg = "INFO: " + f"[{filename}:{stack.lineno}] " + msg
67
+ else:
68
+ msg = "INFO: " + msg
59
69
  kwargs.pop("uptrace", None)
60
70
  super(Logger, self).info(msg, *args, **kwargs)
61
71
 
@@ -65,9 +75,9 @@ class Logger(logging.getLoggerClass()):
65
75
  uptrace += 1
66
76
  stack = inspect.stack()[uptrace]
67
77
  filename = os.path.basename(stack.filename)
68
- msg = f"[{filename}:{stack.lineno}] " + Color.YELLOW + msg + Color.RESET_ALL
78
+ msg = "WARN: " + f"[{filename}:{stack.lineno}] " + Color.YELLOW + msg + Color.RESET_ALL
69
79
  else:
70
- msg = Color.YELLOW + msg + Color.RESET_ALL
80
+ msg = "WARN: " + Color.YELLOW + msg + Color.RESET_ALL
71
81
  kwargs.pop("uptrace", None)
72
82
  super(Logger, self).warning(msg, *args, **kwargs)
73
83
 
@@ -77,9 +87,9 @@ class Logger(logging.getLoggerClass()):
77
87
  uptrace += 1
78
88
  stack = inspect.stack()[uptrace]
79
89
  filename = os.path.basename(stack.filename)
80
- msg = f"[{filename}:{stack.lineno}] " + Color.GREEN + msg + Color.RESET_ALL
90
+ msg = "SUCC: " + f"[{filename}:{stack.lineno}] " + Color.GREEN + msg + Color.RESET_ALL
81
91
  else:
82
- msg = Color.GREEN + msg + Color.RESET_ALL
92
+ msg = "SUCC: " + Color.GREEN + msg + Color.RESET_ALL
83
93
  kwargs.pop("uptrace", None)
84
94
  super(Logger, self).info(msg, *args, **kwargs)
85
95
 
@@ -37,7 +37,6 @@ class Config(object):
37
37
  self.profile_host = None
38
38
 
39
39
  # conan.lock options
40
- self.using_lockfile = args.lockfile
41
40
  self.update_lockfile = args.update_lockfile
42
41
  self.target = args.target
43
42
  self.product = args.product
@@ -86,7 +85,6 @@ class Config(object):
86
85
  parser.add_argument("-pr:b", "--profile_build", help="Apply the specified profile to the build machine", default="default")
87
86
  parser.add_argument("-bt", "--build_type", type=str, choices=['debug', 'release', 'minsize'], help="Set the build type", default="debug")
88
87
  parser.add_argument("-r", "--remote", help="specified conan server", default="litebmc")
89
- parser.add_argument("-l", "--lockfile", help="using conan.lock", action="store_true")
90
88
  parser.add_argument("-ul", "--update_lockfile", help="update conan.lock", action="store_true")
91
89
  parser.add_argument("-p", "--product", help="product name, default product is `default`", default="default")
92
90
  targets = Config.target_list()
@@ -43,24 +43,20 @@ class TaskClass(Task):
43
43
 
44
44
  def deploy(self, graph_file):
45
45
  with open(graph_file, "r") as fp:
46
- graph = json.load(fp)
47
- nodes = graph.get("graph", {}).get("nodes", {})
48
- for id, info in nodes.items():
49
- ref = info.get("ref")
50
- id = info.get("package_id")
51
- context = info.get("context")
52
- if context != "host" or ref.startswith("litebmc/"):
53
- continue
54
- cmd = f"conan cache path {ref}:{id}"
55
- package_folder = self.tools.run(cmd).stdout.strip()
56
- self.config.conan_install.append(package_folder)
57
-
58
- def download_recipe(self, pkg):
59
- cmd = f"conan cache path {pkg}"
60
- ret = self.exec_easy(cmd, ignore_error=True)
61
- if ret is None or ret.returncode != 0:
62
- cmd = f"conan download {pkg} -r {self.config.remote} --only-recipe"
63
- self.exec(cmd, ignore_error=True)
46
+ order_info = json.load(fp)
47
+ for orders in order_info.get("order", []):
48
+ for order in orders:
49
+ ref = order.get("ref")
50
+ packages = order.get("packages", [])
51
+ for package in packages:
52
+ for pkg in package:
53
+ binary = pkg.get("context", "")
54
+ if binary != "host":
55
+ continue
56
+ id = pkg.get("package_id", "")
57
+ cmd = f"conan cache path {ref}:{id}"
58
+ package_folder = self.tools.run(cmd).stdout.strip()
59
+ self.config.conan_install.append(package_folder)
64
60
 
65
61
  def build_rootfs(self):
66
62
  """构建产品rootfs包"""
@@ -101,33 +97,24 @@ class TaskClass(Task):
101
97
  fp.close()
102
98
 
103
99
  base_cmd = f"{self.common_args} {self.conan_settings}"
104
- threadPool = ThreadPoolExecutor(max_workers=16)
105
- if self.config.using_lockfile:
106
- lockfile = os.path.join(self.config.code_path, "conan.lock")
100
+ lockfile = os.path.join(self.config.code_path, "conan.lock")
101
+ orderfile = os.path.join(self.config.temp_path, "build-order.json")
102
+ graph_cmd = f"conan graph build-order . {base_cmd} --order-by=recipe -f json --out-file={orderfile}"
103
+ if self.config.from_source:
104
+ graph_cmd += " --build='*'"
107
105
  else:
108
- lockfile = os.path.join(self.config.temp_path, "conan.lock")
109
- # 创建新的conan.lock文件
110
- if not self.config.using_lockfile or self.config.update_lockfile:
111
- lock_cmd = f"conan lock create . {base_cmd} --lockfile-out={lockfile}"
112
- self.exec(lock_cmd, verbose=True)
113
- if not os.path.isfile(lockfile):
114
- raise FileNotFoundError("lockfile ./conan.lock was not found")
115
- with open(lockfile, "r") as fp:
116
- lock = json.load(fp)
117
- for key in ["requires", "build_requires", "python_requires", "config_requires"]:
118
- requires = lock.get(key, [])
119
- for require in requires:
120
- threadPool.submit(self.download_recipe, require)
121
- threadPool.shutdown(wait=True)
122
- graph_cmd = f"conan graph info . {base_cmd} -f json --lockfile={lockfile}"
123
- graphfile = os.path.join(self.config.temp_path, "graph.info")
124
- self.pipe([graph_cmd], out_file=graphfile)
125
- bcp = BuildConanParallel(graphfile, lockfile, self.common_args, self.config.from_source)
106
+ graph_cmd += " --build=missing"
107
+ if self.config.update_lockfile or not os.path.isfile(lockfile):
108
+ graph_cmd += f" --lockfile-out={lockfile}"
109
+ else:
110
+ graph_cmd += f" --lockfile={lockfile} --lockfile-partial"
111
+ self.exec(graph_cmd, verbose=True)
112
+ bcp = BuildConanParallel(orderfile, self.config.remote)
126
113
  bcp.build()
127
114
 
128
115
  self.exec(f"sed -i 's@rootfs_df190c/0.0.1#.*\"@rootfs_df190c/0.0.1\"@g' {lockfile}")
129
116
  # 部署应用到self.config.conan_install
130
- self.deploy(graphfile)
117
+ self.deploy(orderfile)
131
118
 
132
119
  def run(self):
133
120
  """任务入口"""
@@ -74,12 +74,14 @@ class TaskClass(Task):
74
74
  src_fd = open(profile, "r")
75
75
  template = Template(src_fd.read())
76
76
  src_fd.close()
77
- content = template.safe_substitute(compiler_path=self.compiler_path, sysroot_path=self.sysroot_path)
77
+ content = template.safe_substitute(compiler_path=self.compiler_path,
78
+ sysroot_path=self.sysroot_path,
79
+ code_path=self.config.code_path)
78
80
  dst_fp.write(content)
79
81
 
80
82
  with open(dst_profile, "r") as fp:
81
83
  profile_data = jinja2.Template(fp.read()).render()
82
- parser = configparser.ConfigParser()
84
+ parser = configparser.ConfigParser(allow_no_value=True, delimiters=('=',))
83
85
  parser.read_string(profile_data)
84
86
  strip = "strip"
85
87
  if parser.has_option("buildenv", "STRIP"):
@@ -22,6 +22,33 @@ class Tools(object):
22
22
  self.log_name = os.path.join(self.log_dir, f"{log_name}.log")
23
23
  self.log: Logger = Logger(log_name)
24
24
 
25
+ class _Tee:
26
+ """同时写入文件和终端的类"""
27
+ def __init__(self, file_obj, verbose: bool):
28
+ self.file = file_obj
29
+ self.verbose = verbose
30
+ self.memory = ""
31
+ self.stdout = sys.stdout
32
+ self.stderr = sys.stderr
33
+
34
+ def writef(self, data):
35
+ self.file.write(data)
36
+
37
+ def write(self, data):
38
+ # 同时写入文件和终端
39
+ self.memory += data
40
+ self.file.write(data)
41
+ if self.verbose:
42
+ self.stdout.write(data)
43
+
44
+ def fileno(self):
45
+ return self.file.fileno()
46
+
47
+ def flush(self):
48
+ self.file.flush()
49
+ if self.verbose:
50
+ self.stdout.flush()
51
+
25
52
  @staticmethod
26
53
  def _real_command(shell: str):
27
54
  if not isinstance(shell, str):
@@ -44,50 +71,46 @@ class Tools(object):
44
71
  return cmd
45
72
 
46
73
  def exec(self, cmd: str, verbose=False, ignore_error = False, sensitive=False, log_prefix="", **kwargs):
47
- """执行命令"""
74
+ """执行命令,输出同时写入日志文件和终端"""
48
75
  stack = inspect.stack()[1]
49
76
  file = os.path.basename(stack.filename)
50
77
  line = stack.lineno
51
78
  echo_cmd = kwargs.get("echo_cmd", True)
52
79
  log_name = kwargs.get("log_name", self.log_name)
53
80
  uptrace = kwargs.get("uptrace", 0) + 1
54
-
55
81
  show_cmd = "***" if sensitive else cmd
56
82
  if log_prefix:
57
83
  show_cmd = "(" + log_prefix + ") " + show_cmd
58
84
 
59
- fd = os.fdopen(os.open(log_name, os.O_APPEND | os.O_CREAT | os.O_RDWR), "a+")
60
- if echo_cmd:
61
- self.log.info("{}>>>>{} {}".format(Color.GREEN, Color.RESET_ALL, show_cmd), uptrace=uptrace)
62
- last_pos = fd.tell()
63
- fd.write(">>>> {}\n".format(show_cmd))
64
85
  if os.environ.get("VERBOSE", False):
65
86
  verbose = True
87
+ fd = os.fdopen(os.open(log_name, os.O_APPEND | os.O_CREAT | os.O_RDWR), "a+")
88
+ # 使用 _Tee 类同时写入文件和终端
89
+ msg = "{}>>>>{} {}\n".format(Color.GREEN, Color.RESET_ALL, show_cmd)
90
+ tee = self._Tee(fd, verbose)
91
+ if echo_cmd:
92
+ sys.stdout.write(msg)
93
+ tee.writef(msg)
66
94
  real_cmd = self._real_command(cmd)
67
- result = subprocess.Popen(real_cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
95
+ result = subprocess.Popen(real_cmd, stdout=tee, stderr=tee, universal_newlines=True)
96
+ tee.flush()
97
+ fd.close()
68
98
  if result is None:
69
99
  raise errors.RunCommandException(f"Run command {show_cmd} failed")
70
- for msg in result.stdout:
71
- fd.write(msg)
72
- if verbose:
73
- sys.stdout.write(msg)
74
- fd.close()
75
100
  result.communicate()
76
- if result is None or result.returncode != 0:
77
- if not ignore_error:
78
- msg = f"{file}:{line} > Run command ({show_cmd}) failed, log file is {log_name}"
101
+ if result.returncode == 0:
102
+ return result
103
+ if not ignore_error:
104
+ msg = f"{file}:{line} > Run command ({show_cmd}) failed, log file is {log_name}"
105
+ if not verbose:
79
106
  self.log.warn(f">>>>>>>>>> '{show_cmd}' LOG START", uptrace=uptrace)
80
- fd = open(log_name, "r")
81
- fd.seek(last_pos, 0)
82
- logs = fd.readlines()
83
- for log in logs:
84
- print(log, end="")
85
- self.log.warn(f"<<<<<<<<<< '{show_cmd}' LOG END", uptrace=uptrace)
107
+ sys.stdout.write(tee.memory)
86
108
  sys.stdout.flush()
87
- raise errors.RunCommandException(msg)
88
- else:
89
- msg = f"{file}:{line} > Run command ({show_cmd}) failed but ignore error"
90
- self.log.warn(msg, uptrace=uptrace)
109
+ self.log.warn(f"<<<<<<<<<< '{show_cmd}' LOG END", uptrace=uptrace)
110
+ raise errors.RunCommandException(msg)
111
+ else:
112
+ msg = f"{file}:{line} > Run command ({show_cmd}) failed but ignore error"
113
+ self.log.warn(msg, uptrace=uptrace)
91
114
  return result
92
115
 
93
116
  def pipe(self, cmds: list[str], ignore_error=False, out_file = None, **kwargs):
@@ -127,13 +150,16 @@ class Tools(object):
127
150
  stdin.close()
128
151
  return output
129
152
 
130
- def run(self, cmd, ignore_error=False, **kwargs):
153
+ def run(self, cmd, ignore_error=False, stdout=None, stderr=None, **kwargs):
131
154
  uptrace = kwargs.get("uptrace", 0) + 1
132
155
  capture_output = kwargs.get("capture_output", True)
133
156
  self.log.debug("{}>>>>{} {}".format(Color.GREEN, Color.RESET_ALL, cmd), uptrace=uptrace)
134
157
  real_cmd = self._real_command(cmd)
135
158
  check = False if ignore_error else True
136
- return subprocess.run(real_cmd, capture_output=capture_output, check=check, encoding="utf-8")
159
+ if stdout or stderr:
160
+ return subprocess.run(real_cmd, check=check, stdout=stdout, stderr=stderr, encoding="utf-8")
161
+ else:
162
+ return subprocess.run(real_cmd, capture_output=capture_output, check=check, encoding="utf-8")
137
163
 
138
164
  @staticmethod
139
165
  def file_digest_sha256(filename):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lbkit
3
- Version: 0.9.2
3
+ Version: 0.9.4
4
4
  Summary: Tools provided by litebmc.com
5
5
  Home-page: https://www.litebmc.com
6
6
  Author: xuhj@litebmc.com
@@ -1,2 +0,0 @@
1
-
2
- __version__ = '0.9.2'
@@ -1,206 +0,0 @@
1
- """任务基础类"""
2
- import os
3
- import json
4
- import tempfile
5
- from queue import Queue
6
- from threading import Thread
7
- from lbkit.log import Logger
8
- from lbkit.errors import LiteBmcException
9
- from lbkit import misc
10
- from lbkit.tools import Tools
11
-
12
- tools = Tools("comp_build")
13
- log = Logger("comp_build")
14
-
15
- class ConanPackage():
16
- def __init__(self, node):
17
- self.node = node
18
- self.deps: dict[str, ConanPackage] = {}
19
- self.build_deps: dict[str, ConanPackage] = {}
20
- self.ref = node.get("ref")
21
- self.pkg = self.ref.split("#")[0]
22
- self.name = self.pkg.split("/")[0]
23
- self.context = node.get("context")
24
- self.building = False
25
- self.is_host = self.context == "host"
26
- binary = node.get("binary")
27
- self.binary_exist = (binary in ["Cache"])
28
-
29
- def append_dep(self, dep):
30
- self.deps[dep.pkg] = dep
31
-
32
- def append_build_dep(self, dep):
33
- self.build_deps[dep.pkg] = dep
34
-
35
- # 间接构建依赖更新为直接依赖
36
- def update_indirect_build_dep(self):
37
- deps = {}
38
- for _, dep in self.build_deps.items():
39
- sub_deps = dep.update_indirect_build_dep()
40
- deps.update(sub_deps)
41
- self.build_deps.update(deps)
42
- return self.build_deps
43
-
44
- @property
45
- def options(self):
46
- return self.node.get("options", {})
47
-
48
- @property
49
- def settings(self):
50
- return self.node.get("settings", {})
51
-
52
- @property
53
- def default_options(self):
54
- return self.node.get("default_options", {})
55
-
56
-
57
- class BuildConanParallel(object):
58
- def __init__(self, graphinfo, lockfile, cmd, force_build):
59
- self.queue = Queue()
60
- self.cmd = ""
61
- chunks = cmd.split()
62
- skip = False
63
- for chunk in chunks:
64
- if skip:
65
- skip = False
66
- continue
67
- if chunk in ["--user", "--channel", "--version", "--name"]:
68
- skip = True
69
- continue
70
- self.cmd += f"{chunk} "
71
- self.graphinfo = os.path.realpath(graphinfo)
72
- self.lockfile = os.path.realpath(lockfile)
73
- if not os.path.isfile(graphinfo):
74
- raise LiteBmcException(f"graph file {graphinfo} not exist")
75
- self.exception = None
76
- self.force_build = force_build
77
-
78
- def build_dep(self, cp: ConanPackage, options):
79
- for name, value in cp.settings.items():
80
- options += f" -s {name}={value}"
81
-
82
- cmd = f"conan install --requires={cp.ref} {self.cmd} {options}"
83
- if self.force_build:
84
- cmd += f" --build=\"{cp.name}/*\""
85
- cmd += " --build=missing"
86
- cmd += f" --lockfile={self.lockfile}"
87
- try:
88
- logfile = f"{misc.LOG_DIR}/conan_{cp.name}.log"
89
- log.info(f">>>> build {cp.ref} start, logfile: {logfile}")
90
- log.debug(f">>>> {cmd}")
91
- tools.exec(cmd, echo_cmd=False, log_name=logfile)
92
- log.success(f"<<<< build {cp.ref} finished")
93
- except Exception as e:
94
- self.exception = e
95
- self.queue.put(cp)
96
-
97
- def _build(self):
98
- with open(self.graphinfo, "r") as fp:
99
- grapth = json.load(fp)
100
- nodes = grapth.get("graph", {}).get("nodes", {})
101
- packages: dict[str, ConanPackage] = {}
102
-
103
- build_works = {}
104
- for id, node in nodes.items():
105
- if id == "0":
106
- continue
107
- cp = ConanPackage(node)
108
- packages[cp.pkg] = cp
109
- build_works[cp.name] = False
110
- for id, node in nodes.items():
111
- if id == "0":
112
- continue
113
- ref = node.get("ref")
114
- if ref.startswith("litebmc/"):
115
- continue
116
- cp = packages[ref.split("#")[0]]
117
- deps = node.get("dependencies", {})
118
- for _, dep in deps.items():
119
- dep_ref = dep.get("ref")
120
- direct = dep.get("direct")
121
- if not direct:
122
- continue
123
- dep_cp = packages[dep_ref]
124
- if dep_cp.is_host:
125
- cp.append_dep(dep_cp)
126
- else:
127
- cp.append_build_dep(dep_cp)
128
- options = ""
129
- for _, cp in packages.items():
130
- for name, value in cp.options.items():
131
- if value is None:
132
- continue
133
- def_val = cp.default_options.get(name)
134
- conan_name = cp.pkg.split("@")[0]
135
- if isinstance(def_val, bool):
136
- if (def_val and "False" == value) or (not def_val and "True" == value):
137
- options += f" -o {conan_name}:{name}={value}"
138
- elif def_val != value:
139
- options += f" -o {conan_name}:{name}={value}"
140
- # 工具间接依赖变更为直接依赖
141
- for name, pkg in packages.items():
142
- pkg.update_indirect_build_dep()
143
- wait_finished = 0
144
- while True:
145
- for _, cp in packages.items():
146
- if wait_finished >= 4:
147
- continue
148
- if cp.ref.startswith("litebmc/"):
149
- continue
150
- # 如果是构建工具,不参与构建
151
- if not cp.is_host:
152
- continue
153
- # 如果还有依赖未构建完成,不参与构建
154
- if len(cp.deps) != 0:
155
- continue
156
- # 如果正在构建
157
- if cp.building:
158
- continue
159
- # 相同名称的组件正在构建时不启动新的构建
160
- if build_works[cp.name]:
161
- continue
162
- if not cp.binary_exist or self.force_build:
163
- # 当依赖的构建工具存在正在构建的组件时不能构建
164
- need_build = True
165
- for _, dep in cp.build_deps.items():
166
- # 正在构建且未构建出制品时
167
- if not dep.binary_exist and dep.building:
168
- need_build = False
169
- break
170
- if not need_build:
171
- continue
172
- cp.building = True
173
- # 启动构建前将其依赖的构建工具置为正在构建
174
- for _, dep in cp.build_deps.items():
175
- dep.building = True
176
- build_works[cp.name] = True
177
- wait_finished += 1
178
- thread = Thread(target=self.build_dep, args=(cp, options,))
179
- thread.start()
180
- else:
181
- cp.building = True
182
- build_works[cp.name] = True
183
- wait_finished += 1
184
- self.queue.put(cp)
185
- if not wait_finished:
186
- return
187
- cp = self.queue.get()
188
- if self.exception:
189
- raise self.exception
190
- build_works[cp.name] = False
191
- wait_finished -= 1
192
- for _, sub_cp in packages.items():
193
- if sub_cp.deps.get(cp.pkg):
194
- sub_cp.deps.pop(cp.pkg)
195
- # 构建完成后,组件的构建依赖工具一定构建完成且制品存在
196
- for _, dep in cp.build_deps.items():
197
- dep.building = False
198
- dep.binary_exist = True
199
-
200
-
201
- def build(self):
202
- cwd = os.getcwd()
203
- tmp = tempfile.TemporaryDirectory()
204
- os.chdir(tmp.name)
205
- self._build()
206
- os.chdir(cwd)
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