lbkit 0.9.3__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.
- {lbkit-0.9.3 → lbkit-0.9.4}/AUTHORS +1 -1
- {lbkit-0.9.3/lbkit.egg-info → lbkit-0.9.4}/PKG-INFO +1 -1
- lbkit-0.9.4/lbkit/__init__.py +2 -0
- lbkit-0.9.4/lbkit/build_conan_parallel.py +61 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/component/build.py +18 -15
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/component/test.py +3 -3
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/log.py +29 -19
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/config.py +0 -2
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/task_build_manifest.py +27 -40
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/task_build_prepare.py +4 -2
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tools.py +54 -28
- {lbkit-0.9.3 → lbkit-0.9.4/lbkit.egg-info}/PKG-INFO +1 -1
- lbkit-0.9.3/lbkit/__init__.py +0 -2
- lbkit-0.9.3/lbkit/build_conan_parallel.py +0 -206
- {lbkit-0.9.3 → lbkit-0.9.4}/LICENSE +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/MANIFEST.in +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/README.md +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/ci_robot/__init__.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/ci_robot/gitee.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/cli.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/__init__.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/codegen.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/ctype_defination.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/idf_interface.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/renderer.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/template/client.c.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/template/client.h.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/template/interface.c.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/template/interface.introspect.xml.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/template/public.c.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/template/public.h.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/template/server.c.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/codegen/template/server.h.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/component/__init__.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/component/arg_parser.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/component/template/conanbase.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/component/template/deploy.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/errors.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/helper.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/lbkit.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/misc.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/__init__.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/executor.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/image_maker/__init__.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/image_maker/make_image.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/image_maker/make_qemu_image.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/image_maker/make_rockchip_image.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/task.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/task_build_image.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/task_build_rootfs.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/task_download.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/template/conanfile.py.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/tasks/template/rootfs.py.mako +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/ukr/__init__.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/ukr/build.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/utils/__init__.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/utils/env_detector.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/utils/images/__init__.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit/utils/images/emmc.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit.egg-info/SOURCES.txt +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit.egg-info/dependency_links.txt +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit.egg-info/entry_points.txt +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit.egg-info/requires.txt +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/lbkit.egg-info/top_level.txt +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/setup.cfg +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/setup.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/test/__init__.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/test/test_codegen.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/test/test_config.py +0 -0
- {lbkit-0.9.3 → lbkit-0.9.4}/test/test_helper.py +0 -0
|
@@ -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)
|
|
@@ -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
|
|
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
|
|
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)
|
|
@@ -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.
|
|
14
|
+
self._set_handler(logging.INFO, formatter)
|
|
16
15
|
else:
|
|
17
|
-
formatter = logging.Formatter('%(asctime)s %(
|
|
16
|
+
formatter = logging.Formatter('%(asctime)s %(message)s')
|
|
18
17
|
if self.logenv == "info":
|
|
19
|
-
self.
|
|
18
|
+
self._set_handler(logging.INFO, formatter)
|
|
20
19
|
elif self.logenv == "warn":
|
|
21
|
-
self.
|
|
22
|
-
elif self.logenv
|
|
23
|
-
self.
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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(
|
|
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,
|
|
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=
|
|
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
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
-
|
|
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):
|
lbkit-0.9.3/lbkit/__init__.py
DELETED
|
@@ -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
|
|
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
|