lbkit 0.9.11__tar.gz → 0.9.13__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.11 → lbkit-0.9.13}/MANIFEST.in +1 -0
- {lbkit-0.9.11/lbkit.egg-info → lbkit-0.9.13}/PKG-INFO +1 -1
- lbkit-0.9.13/lbkit/__commit__.py +1 -0
- lbkit-0.9.13/lbkit/__init__.py +2 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/cli.py +21 -1
- lbkit-0.9.13/lbkit/codegen/.clang-format +70 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/codegen.py +76 -24
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/template/client.c.mako +0 -2
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/template/public.c.mako +3 -8
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/template/server.c.mako +0 -4
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/component/arg_parser.py +2 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/component/build.py +5 -2
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/component/template/conanbase.mako +18 -10
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/component/template/deploy.mako +4 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/component/test.py +6 -2
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/task_build_rootfs.py +0 -2
- lbkit-0.9.13/lbkit/upgrade.py +326 -0
- {lbkit-0.9.11 → lbkit-0.9.13/lbkit.egg-info}/PKG-INFO +1 -1
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit.egg-info/SOURCES.txt +2 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/test/test_codegen.py +99 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/test/test_permission.py +0 -8
- lbkit-0.9.11/lbkit/__commit__.py +0 -1
- lbkit-0.9.11/lbkit/__init__.py +0 -2
- {lbkit-0.9.11 → lbkit-0.9.13}/AUTHORS +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/README.md +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/build_conan_parallel.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/ci_robot/__init__.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/__init__.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/ctype_defination.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/idf_interface.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/renderer.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/template/client.h.mako +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/template/interface.introspect.xml.mako +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/template/public.h.mako +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/codegen/template/server.h.mako +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/component/__init__.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/download_cache.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/errors.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/helper.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/lbkit.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/log.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/misc.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/skill/__init__.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/skill/constants.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/skill/manager.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/__init__.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/config.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/executor.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/image_maker/__init__.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/image_maker/make_image.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/image_maker/make_qemu_image.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/image_maker/make_rockchip_image.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/task.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/task_build_image.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/task_build_manifest.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/task_build_prepare.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/task_download.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/template/conanfile.py.mako +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tasks/template/rootfs.py.mako +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/tools.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/ukr/__init__.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/ukr/build.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/utils/__init__.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/utils/env_detector.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/utils/fakeroot.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/utils/images/__init__.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit/utils/images/emmc.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit.egg-info/dependency_links.txt +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit.egg-info/entry_points.txt +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit.egg-info/requires.txt +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/lbkit.egg-info/top_level.txt +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/setup.cfg +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/setup.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/test/__init__.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/test/test_config.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/test/test_helper.py +0 -0
- {lbkit-0.9.11 → lbkit-0.9.13}/test/test_skill.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__commit__ = '014a769'
|
|
@@ -27,10 +27,22 @@ from lbkit import errors
|
|
|
27
27
|
from lbkit.ukr.build import UKRBuild
|
|
28
28
|
from lbkit.utils.env_detector import EnvDetector
|
|
29
29
|
from lbkit.skill.manager import sync as skill_sync
|
|
30
|
+
from lbkit.upgrade import upgrade as do_upgrade
|
|
30
31
|
|
|
31
32
|
log = Logger()
|
|
32
33
|
|
|
33
34
|
|
|
35
|
+
def ask_user_yes_no(prompt):
|
|
36
|
+
"""向用户询问是/否问题"""
|
|
37
|
+
while True:
|
|
38
|
+
answer = input(f"{prompt} [y/N]: ").strip().lower()
|
|
39
|
+
if answer in ("y", "yes"):
|
|
40
|
+
return True
|
|
41
|
+
if answer in ("n", "no", ""):
|
|
42
|
+
return False
|
|
43
|
+
print("请输入 y 或 n")
|
|
44
|
+
|
|
45
|
+
|
|
34
46
|
class Command(object):
|
|
35
47
|
"""A single command of the lbkit application, with all the first level commands. Manages the
|
|
36
48
|
parsing of parameters and delegates functionality in collaborators. It can also show the
|
|
@@ -115,6 +127,14 @@ class Command(object):
|
|
|
115
127
|
"""
|
|
116
128
|
skill_sync()
|
|
117
129
|
|
|
130
|
+
def upgrade(self, *args):
|
|
131
|
+
"""
|
|
132
|
+
自升级lbkit.
|
|
133
|
+
|
|
134
|
+
通过apt/dnf升级系统包并安装配套的PyPI包
|
|
135
|
+
"""
|
|
136
|
+
do_upgrade(*args)
|
|
137
|
+
|
|
118
138
|
def _show_help(self):
|
|
119
139
|
"""
|
|
120
140
|
Prints a summary of all commands.
|
|
@@ -123,7 +143,7 @@ class Command(object):
|
|
|
123
143
|
("Build Component commands", ["build", "test"]),
|
|
124
144
|
("Build Product commands", ["build"]),
|
|
125
145
|
("AI Skill commands", ["skill"]),
|
|
126
|
-
("Misc commands", ["help"]),
|
|
146
|
+
("Misc commands", ["upgrade", "help"]),
|
|
127
147
|
]
|
|
128
148
|
|
|
129
149
|
def check_all_commands_listed():
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
# LiteBMC 代码生成默认格式化配置
|
|
3
|
+
# 参考 Linux 内核风格, 适配 GLib/C 项目
|
|
4
|
+
# BasedOnStyle: LLVM
|
|
5
|
+
|
|
6
|
+
AccessModifierOffset: -4
|
|
7
|
+
AlignAfterOpenBracket: Align
|
|
8
|
+
AlignConsecutiveAssignments: false
|
|
9
|
+
AlignConsecutiveDeclarations: false
|
|
10
|
+
AlignEscapedNewlines: Left
|
|
11
|
+
AlignOperands: true
|
|
12
|
+
AlignTrailingComments: false
|
|
13
|
+
AllowAllParametersOfDeclarationOnNextLine: false
|
|
14
|
+
AllowShortBlocksOnASingleLine: false
|
|
15
|
+
AllowShortCaseLabelsOnASingleLine: false
|
|
16
|
+
AllowShortFunctionsOnASingleLine: None
|
|
17
|
+
AllowShortIfStatementsOnASingleLine: false
|
|
18
|
+
AllowShortLoopsOnASingleLine: false
|
|
19
|
+
AlwaysBreakAfterDefinitionReturnType: None
|
|
20
|
+
AlwaysBreakAfterReturnType: None
|
|
21
|
+
AlwaysBreakBeforeMultilineStrings: false
|
|
22
|
+
BinPackArguments: true
|
|
23
|
+
BinPackParameters: true
|
|
24
|
+
BraceWrapping:
|
|
25
|
+
AfterControlStatement: false
|
|
26
|
+
AfterEnum: false
|
|
27
|
+
AfterFunction: true
|
|
28
|
+
AfterStruct: false
|
|
29
|
+
BeforeCatch: false
|
|
30
|
+
BeforeElse: false
|
|
31
|
+
IndentBraces: false
|
|
32
|
+
BreakBeforeBinaryOperators: None
|
|
33
|
+
BreakBeforeBraces: Custom
|
|
34
|
+
BreakBeforeTernaryOperators: false
|
|
35
|
+
BreakStringLiterals: false
|
|
36
|
+
ColumnLimit: 120
|
|
37
|
+
CommentPragmas: '^ IWYU pragma:'
|
|
38
|
+
ConstructorInitializerIndentWidth: 4
|
|
39
|
+
ContinuationIndentWidth: 4
|
|
40
|
+
Cpp11BracedListStyle: false
|
|
41
|
+
DerivePointerAlignment: false
|
|
42
|
+
FixNamespaceComments: false
|
|
43
|
+
IndentCaseLabels: false
|
|
44
|
+
IndentGotoLabels: false
|
|
45
|
+
IndentPPDirectives: None
|
|
46
|
+
IndentWidth: 4
|
|
47
|
+
IndentWrappedFunctionNames: false
|
|
48
|
+
KeepEmptyLinesAtTheStartOfBlocks: false
|
|
49
|
+
MaxEmptyLinesToKeep: 1
|
|
50
|
+
PointerAlignment: Right
|
|
51
|
+
ReflowComments: false
|
|
52
|
+
SortIncludes: false
|
|
53
|
+
SpaceAfterCStyleCast: false
|
|
54
|
+
SpaceBeforeAssignmentOperators: true
|
|
55
|
+
SpaceBeforeParens: ControlStatementsExceptForEachMacros
|
|
56
|
+
SpacesBeforeTrailingComments: 1
|
|
57
|
+
SpacesInAngles: false
|
|
58
|
+
SpacesInContainerLiterals: false
|
|
59
|
+
SpacesInCStyleCastParentheses: false
|
|
60
|
+
SpacesInParentheses: false
|
|
61
|
+
SpacesInSquareBrackets: false
|
|
62
|
+
TabWidth: 4
|
|
63
|
+
UseTab: Never
|
|
64
|
+
PenaltyBreakAssignment: 10
|
|
65
|
+
PenaltyBreakBeforeFirstCallParameter: 30
|
|
66
|
+
PenaltyBreakComment: 10
|
|
67
|
+
PenaltyBreakFirstLessLess: 0
|
|
68
|
+
PenaltyBreakString: 10
|
|
69
|
+
PenaltyExcessCharacter: 100
|
|
70
|
+
PenaltyReturnTypeOnItsOwnLine: 60
|
|
@@ -13,6 +13,8 @@ import re
|
|
|
13
13
|
import json
|
|
14
14
|
import yaml
|
|
15
15
|
import argparse
|
|
16
|
+
import shutil
|
|
17
|
+
import subprocess
|
|
16
18
|
from concurrent.futures import ProcessPoolExecutor, as_completed
|
|
17
19
|
from lbkit.codegen.idf_interface import IdfInterface
|
|
18
20
|
|
|
@@ -68,45 +70,93 @@ class Version():
|
|
|
68
70
|
return True
|
|
69
71
|
return False
|
|
70
72
|
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
+
# 历史自动生成版本号,用于管理代码生成版本与lb_base的兼容性
|
|
74
|
+
# 新增兼容性变更时添加条目,格式:"版本号": CodeGenHistory("lb_base依赖范围", "变更描述", "compatible_required值")
|
|
73
75
|
history_versions = {
|
|
74
|
-
"5.3": CodeGenHistory("lb_base/[>=0.8.5 <0.9.0]", "支持32位操作系统", "8004"),
|
|
75
76
|
"5.4": CodeGenHistory("lb_base/[>=0.9.0 <0.10.0]", "LBInterface增加alias", "9000"),
|
|
76
77
|
}
|
|
78
|
+
|
|
79
|
+
# 最新的版本号
|
|
77
80
|
__version__=Version("5.4")
|
|
78
81
|
|
|
79
82
|
|
|
80
83
|
def version_check(ver_str: str):
|
|
81
84
|
if not re.match("^([0-9]|([1-9][0-9]*))\\.([0-9]|([1-9][0-9]*))$", ver_str):
|
|
82
85
|
raise Exception(f"Version string {ver_str} not match with regex ^([0-9]|([1-9][0-9]*))\\.([0-9]|([1-9][0-9]*))$")
|
|
83
|
-
if
|
|
86
|
+
if history_versions.get(ver_str):
|
|
87
|
+
return ver_str
|
|
88
|
+
if not history_versions:
|
|
84
89
|
return ver_str
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
raise Exception(f"Can't found the valid version for {ver_str}")
|
|
90
|
-
|
|
91
|
-
def codegen_version_max():
|
|
92
|
-
max_v = __version__
|
|
93
|
-
for ver_str, _ in history_versions.items():
|
|
94
|
-
next_ver = Version(ver_str)
|
|
95
|
-
if next_ver.bt(max_v.str):
|
|
96
|
-
max_v = next_ver
|
|
97
|
-
return max_v.str
|
|
90
|
+
log.error(f"Unkonw codegen version {ver_str}, supported versions:")
|
|
91
|
+
for ver, msg in history_versions.items():
|
|
92
|
+
log.error(f" {ver}: {msg}")
|
|
93
|
+
raise Exception(f"Can't found the valid version for {ver_str}")
|
|
98
94
|
|
|
99
95
|
def codegen_version_arg(parser: argparse.ArgumentParser, default=__version__.str, short_arg="-cv", full_arg="--codegen_version"):
|
|
100
|
-
|
|
101
|
-
help=f'''must less than or equal to {codegen_version_max()}, default: {default}
|
|
102
|
-
|
|
103
|
-
codegen versions:
|
|
104
|
-
'''
|
|
96
|
+
help=f'''default: {default}'''
|
|
105
97
|
for ver, detail in history_versions.items():
|
|
106
|
-
help += f"- {ver}: compatible with {detail.lb_base}, {detail.description}
|
|
98
|
+
help += f"\n - {ver}: compatible with {detail.lb_base}, {detail.description}"
|
|
107
99
|
parser.add_argument(short_arg, full_arg, help=help, type=str, default=__version__.str)
|
|
108
100
|
|
|
109
101
|
|
|
102
|
+
def _find_clang_format_config(idf_file):
|
|
103
|
+
"""查找 .clang-format 配置文件,优先级:IDF目录 > 组件目录 > toolkit默认"""
|
|
104
|
+
# 优先级1:IDF文件所在目录(接口仓库级配置)
|
|
105
|
+
idf_dir = os.path.dirname(os.path.realpath(idf_file))
|
|
106
|
+
candidate = os.path.join(idf_dir, ".clang-format")
|
|
107
|
+
if os.path.isfile(candidate):
|
|
108
|
+
return candidate
|
|
109
|
+
# 优先级2:组件源码目录(组件仓库级配置)
|
|
110
|
+
candidate = os.path.join(os.getcwd(), ".clang-format")
|
|
111
|
+
if os.path.isfile(candidate):
|
|
112
|
+
return candidate
|
|
113
|
+
# 优先级3:toolkit自带的默认配置
|
|
114
|
+
candidate = os.path.join(lb_cwd, ".clang-format")
|
|
115
|
+
if os.path.isfile(candidate):
|
|
116
|
+
return candidate
|
|
117
|
+
return None
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _format_generated_files(idf_file, directory, interface_alias):
|
|
121
|
+
"""使用 clang-format 格式化生成的 .c 和 .h 文件"""
|
|
122
|
+
clang_format_bin = shutil.which("clang-format")
|
|
123
|
+
if not clang_format_bin:
|
|
124
|
+
log.warn("clang-format not found, skipping auto-formatting of generated files")
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
style_file = _find_clang_format_config(idf_file)
|
|
128
|
+
if not style_file:
|
|
129
|
+
log.warn("No .clang-format config found, skipping auto-formatting")
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
files_to_format = []
|
|
133
|
+
for ct in ["server", "client", "public"]:
|
|
134
|
+
ct_dir = os.path.join(directory, ct)
|
|
135
|
+
if not os.path.isdir(ct_dir):
|
|
136
|
+
continue
|
|
137
|
+
for ext in [".c", ".h"]:
|
|
138
|
+
fpath = os.path.join(ct_dir, interface_alias + ext)
|
|
139
|
+
if os.path.isfile(fpath):
|
|
140
|
+
files_to_format.append(fpath)
|
|
141
|
+
|
|
142
|
+
if not files_to_format:
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
log.debug(f"Formatting {len(files_to_format)} generated files with clang-format (style: {style_file})")
|
|
146
|
+
for fpath in files_to_format:
|
|
147
|
+
try:
|
|
148
|
+
subprocess.run(
|
|
149
|
+
[clang_format_bin, f"--style=file:{style_file}", "-i", fpath],
|
|
150
|
+
check=True, capture_output=True, timeout=30
|
|
151
|
+
)
|
|
152
|
+
except subprocess.TimeoutExpired:
|
|
153
|
+
log.warn(f"clang-format timed out on {fpath}, skipping")
|
|
154
|
+
except subprocess.CalledProcessError as e:
|
|
155
|
+
log.warn(f"clang-format failed on {fpath}: {e.stderr.strip() if e.stderr else 'unknown error'}")
|
|
156
|
+
except Exception as e:
|
|
157
|
+
log.warn(f"clang-format error on {fpath}: {str(e)}")
|
|
158
|
+
|
|
159
|
+
|
|
110
160
|
def _gen_interface(idf_file, directory=".", code_type="all", codegen_version_str="5.4", log_level="NOTSET"):
|
|
111
161
|
"""模块级函数,供多进程调用"""
|
|
112
162
|
directory = os.path.realpath(directory)
|
|
@@ -143,6 +193,8 @@ def _gen_interface(idf_file, directory=".", code_type="all", codegen_version_str
|
|
|
143
193
|
with open(json_file, "w", encoding="utf-8") as fp:
|
|
144
194
|
yaml.dump(data, fp, encoding='utf-8', allow_unicode=True)
|
|
145
195
|
|
|
196
|
+
_format_generated_files(idf_file, directory, interface.alias)
|
|
197
|
+
|
|
146
198
|
|
|
147
199
|
class CodeGen(object):
|
|
148
200
|
def __init__(self, args):
|
|
@@ -221,7 +273,7 @@ class CodeGen(object):
|
|
|
221
273
|
raise ArgException(f"argument error, arguments -c/--cdf_file and -i/--idf_file are not set")
|
|
222
274
|
if not os.path.isfile(intf_file):
|
|
223
275
|
raise ArgException(f"argument -i/--idf_file: {args.idf_file} not exist")
|
|
224
|
-
if self.codegen_version.bt(
|
|
276
|
+
if self.codegen_version.bt(__version__.str):
|
|
225
277
|
raise ArgException(f"argument -cv/--codegen_version: validate failed, must less than or equal to {__version__.str}")
|
|
226
278
|
out_dir = os.path.join(os.getcwd(), args.directory)
|
|
227
279
|
if not intf_file.endswith(".yaml"):
|
|
@@ -91,10 +91,8 @@ static LBInterface _${class_name}_interface = {
|
|
|
91
91
|
.name = "${intf.name}",
|
|
92
92
|
.properties = (LBProperty *)&${properties},
|
|
93
93
|
.interface = NULL, /* load from usr/share/dbus-1/interfaces/${intf.name}.xml by lb_init */
|
|
94
|
-
#ifdef LB_CODEGEN_BE_5_4
|
|
95
94
|
.alias = "${class_name}",
|
|
96
95
|
.object_template = "${intf.object_path}",
|
|
97
|
-
#endif
|
|
98
96
|
};
|
|
99
97
|
|
|
100
98
|
static LBBase *_get_real_object(LBO *obj)
|
|
@@ -434,7 +434,7 @@ ${name} ${name}_decode(GVariant *in)
|
|
|
434
434
|
}
|
|
435
435
|
|
|
436
436
|
const gchar *in_val = g_variant_get_string(in, NULL);
|
|
437
|
-
for (int i = 0; i
|
|
437
|
+
for (int i = 0; i < (int)G_N_ELEMENTS(_${name}StrMap); i++) {
|
|
438
438
|
if (g_strcmp0(in_val, _${name}StrMap[i]) == 0) {
|
|
439
439
|
return (${name})i;
|
|
440
440
|
}
|
|
@@ -494,7 +494,7 @@ gboolean ${name}_validate_odf(yaml_document_t *doc, yaml_node_t *node,
|
|
|
494
494
|
return FALSE;
|
|
495
495
|
}
|
|
496
496
|
const gchar *value = (const gchar *)node->data.scalar.value;
|
|
497
|
-
for (int i = 0; i
|
|
497
|
+
for (int i = 0; i < ${len(enum.values.parameters)}; i++) {
|
|
498
498
|
if (g_strcmp0(value, ${name}_as_string(i)) == 0) {
|
|
499
499
|
return TRUE;
|
|
500
500
|
}
|
|
@@ -502,7 +502,6 @@ gboolean ${name}_validate_odf(yaml_document_t *doc, yaml_node_t *node,
|
|
|
502
502
|
*error_list = g_slist_append(*error_list, g_error_new(ODF_ERROR, ODF_ERROR_PROP_VALIDATE_TYPE_ERROR,
|
|
503
503
|
"the value of property %s is invalid, get %s", prop->str, value));
|
|
504
504
|
return FALSE;
|
|
505
|
-
return FALSE;
|
|
506
505
|
}
|
|
507
506
|
|
|
508
507
|
gboolean ${name}_validate_odf_v(yaml_document_t *doc, yaml_node_t *node,
|
|
@@ -1045,7 +1044,6 @@ static LBBase *_get_real_object(LBO *obj)
|
|
|
1045
1044
|
% endif
|
|
1046
1045
|
<% prop_idx = 0 %>\
|
|
1047
1046
|
% for prop in intf.properties:
|
|
1048
|
-
#ifdef LB_CODEGEN_BE_5_2
|
|
1049
1047
|
% if (prop.signature in ["b", "y", "n", "q", "i", "u", "x", "t", "d", "o", "s", "g", "ab", "ay", "an", "aq", "ai", "au", "ax", "at", "ad", "as", "ao", "ag"] and prop.val_validate()):
|
|
1050
1048
|
static gboolean ${class_name}_check_${prop.name}_variant(LBO *obj, GVariant *value, GError **error)
|
|
1051
1049
|
{
|
|
@@ -1080,7 +1078,6 @@ static gboolean ${class_name}_check_${prop.name}_variant(LBO *obj, GVariant *val
|
|
|
1080
1078
|
}
|
|
1081
1079
|
|
|
1082
1080
|
% endif
|
|
1083
|
-
#endif
|
|
1084
1081
|
static void ${class_name}_set_${prop.name}_variant(LBO *obj, GVariant *value)
|
|
1085
1082
|
{
|
|
1086
1083
|
g_assert(value && obj);
|
|
@@ -1126,7 +1123,6 @@ static ${class_name}_Properties _${class_name}_properties =
|
|
|
1126
1123
|
.flags = ${prop.desc_flags},
|
|
1127
1124
|
.set = ${class_name}_set_${prop.name}_variant,
|
|
1128
1125
|
.get = _${class_name}_get_${prop.name}_variant,
|
|
1129
|
-
#ifdef LB_CODEGEN_BE_5_2
|
|
1130
1126
|
% if (prop.signature in ["b", "y", "n", "q", "i", "u", "x", "t", "d", "o", "s", "g", "ab", "ay", "an", "aq", "ai", "au", "ax", "at", "ad", "as", "ao", "ag"] and prop.val_validate()):
|
|
1131
1127
|
.check = ${class_name}_check_${prop.name}_variant,
|
|
1132
1128
|
% else:
|
|
@@ -1152,7 +1148,6 @@ static ${class_name}_Properties _${class_name}_properties =
|
|
|
1152
1148
|
% endif
|
|
1153
1149
|
% endif
|
|
1154
1150
|
% endif
|
|
1155
|
-
#endif
|
|
1156
1151
|
},
|
|
1157
1152
|
<% id = id + 1 %>\
|
|
1158
1153
|
<% prop_idx = prop_idx + 1 %>\
|
|
@@ -1706,7 +1701,7 @@ ${name} ${name}_load_from_odf(yaml_document_t *doc, yaml_node_t *node)
|
|
|
1706
1701
|
return _${name}_Invalid;
|
|
1707
1702
|
}
|
|
1708
1703
|
|
|
1709
|
-
for (int i = 0; i
|
|
1704
|
+
for (int i = 0; i < (int)G_N_ELEMENTS(_${name}StrMap); i++) {
|
|
1710
1705
|
if (g_strcmp0((const gchar *)node->data.scalar.value, ${name}_as_string(i)) == 0) {
|
|
1711
1706
|
return (${name})i;
|
|
1712
1707
|
}
|
|
@@ -93,17 +93,13 @@ static LBInterface _${class_name}_interface = {
|
|
|
93
93
|
.name = "${intf.name}",
|
|
94
94
|
.properties = (LBProperty *)&${properties},
|
|
95
95
|
.interface = NULL, /* load from usr/share/dbus-1/interfaces/${intf.name} by lb_init */
|
|
96
|
-
#ifdef LB_CODEGEN_BE_5_2
|
|
97
96
|
% if intf.plugin.install_dir:
|
|
98
97
|
.plugin_dir = "${intf.plugin.install_dir}",
|
|
99
98
|
% else:
|
|
100
99
|
.plugin_dir = "/opt/litebmc/plugins/${class_name}",
|
|
101
100
|
% endif
|
|
102
|
-
#endif
|
|
103
|
-
#ifdef LB_CODEGEN_BE_5_4
|
|
104
101
|
.alias = "${class_name}",
|
|
105
102
|
.object_template = "${intf.object_path}",
|
|
106
|
-
#endif
|
|
107
103
|
};
|
|
108
104
|
|
|
109
105
|
<% prop_idx = 0 %>\
|
|
@@ -37,6 +37,8 @@ class ArgParser():
|
|
|
37
37
|
help=argparse.SUPPRESS)
|
|
38
38
|
parser.add_argument("--cov", action="store_true",
|
|
39
39
|
help=argparse.SUPPRESS)
|
|
40
|
+
parser.add_argument("--asan", action="store_true",
|
|
41
|
+
help=argparse.SUPPRESS)
|
|
40
42
|
parser.add_argument("--test", action="store_true",
|
|
41
43
|
help=argparse.SUPPRESS)
|
|
42
44
|
parser.add_argument(
|
|
@@ -100,12 +100,15 @@ class BuildComponent():
|
|
|
100
100
|
self.base_cmd += f" -pr:b {self.profile_build}"
|
|
101
101
|
if self.options.cov:
|
|
102
102
|
self.base_cmd += f" -o {self.name}/*:gcov=True"
|
|
103
|
+
if self.options.asan:
|
|
104
|
+
self.base_cmd += f" -o */*:asan=True"
|
|
103
105
|
if self.options.test:
|
|
104
106
|
self.base_cmd += f" -o {self.name}/*:test=True"
|
|
105
107
|
for pkg_option in self.options.pkg_options:
|
|
106
108
|
self.base_cmd += " -o " + pkg_option
|
|
107
109
|
self.base_cmd += f" -o */*:codegen_version={self.options.codegen_version}"
|
|
108
|
-
|
|
110
|
+
if self.codegen_version.info:
|
|
111
|
+
self.base_cmd += f" -o */*:compatible_required={self.codegen_version.info.lb_base_compatible_required}"
|
|
109
112
|
|
|
110
113
|
@staticmethod
|
|
111
114
|
def _check_conanfile_if_tracked():
|
|
@@ -277,7 +280,7 @@ class BuildComponent():
|
|
|
277
280
|
tools.exec(graph_cmd, verbose=True)
|
|
278
281
|
bcp = BuildConanParallel(self.orderfile, self.lockfile, self.options.remote)
|
|
279
282
|
bcp.build()
|
|
280
|
-
cmd = f"conan create {self.base_cmd} --build='{self.name}/*'"
|
|
283
|
+
cmd = f"conan create . {self.base_cmd} --build='{self.name}/*'"
|
|
281
284
|
tools.exec(cmd, verbose=True)
|
|
282
285
|
|
|
283
286
|
graph_cmd = f"conan install . {self.base_cmd} --lockfile={self.lockfile} -f json"
|
|
@@ -49,6 +49,7 @@ class LiteBmcConan(ConanFile):
|
|
|
49
49
|
"shared": [False, True],
|
|
50
50
|
% endif
|
|
51
51
|
"gcov": [False, True],
|
|
52
|
+
"asan": [False, True],
|
|
52
53
|
"test": [False, True],
|
|
53
54
|
% if len(pkg.get("options", [])) > 0:
|
|
54
55
|
% for op, ctx in pkg["options"].items():
|
|
@@ -61,6 +62,7 @@ class LiteBmcConan(ConanFile):
|
|
|
61
62
|
"shared": True,
|
|
62
63
|
% endif
|
|
63
64
|
"gcov": False,
|
|
65
|
+
"asan": False,
|
|
64
66
|
"test": False,
|
|
65
67
|
% if len(pkg.get("options", [])) > 0:
|
|
66
68
|
% for op, ctx in pkg["options"].items():
|
|
@@ -184,6 +186,10 @@ class LiteBmcConan(ConanFile):
|
|
|
184
186
|
if self.options.gcov:
|
|
185
187
|
flags.append("-fprofile-arcs")
|
|
186
188
|
flags.append("-ftest-coverage")
|
|
189
|
+
if self.options.asan:
|
|
190
|
+
flags.append("-fsanitize=address")
|
|
191
|
+
flags.append("-fno-omit-frame-pointer")
|
|
192
|
+
flags.append("-g")
|
|
187
193
|
if self.settings.build_type == "Release" and self.settings.arch == "armv8":
|
|
188
194
|
flags.append("-D_FORTIFY_SOURCE=2")
|
|
189
195
|
return flags
|
|
@@ -200,27 +206,27 @@ class LiteBmcConan(ConanFile):
|
|
|
200
206
|
tc.variables["CMAKE_PROJECT_VERSION"] = self.version
|
|
201
207
|
tc.variables["CMAKE_BUILD_TYPE"] = self.settings.build_type
|
|
202
208
|
% if pkg_type == "static-library":
|
|
203
|
-
tc.variables["BUILD_SHARED_LIBS] =
|
|
209
|
+
tc.variables["BUILD_SHARED_LIBS"] = 0
|
|
204
210
|
% elif pkg_type == "shared-libraries":
|
|
205
|
-
tc.variables["BUILD_SHARED_LIBS"] =
|
|
211
|
+
tc.variables["BUILD_SHARED_LIBS"] = 1
|
|
206
212
|
% elif pkg_type == "library":
|
|
207
213
|
if self.options.shared == False:
|
|
208
|
-
tc.variables["BUILD_SHARED_LIBS"] =
|
|
214
|
+
tc.variables["BUILD_SHARED_LIBS"] = 0
|
|
209
215
|
else:
|
|
210
|
-
tc.variables["BUILD_SHARED_LIBS"] =
|
|
216
|
+
tc.variables["BUILD_SHARED_LIBS"] = 1
|
|
211
217
|
% endif
|
|
212
218
|
if self.options.test == True:
|
|
213
|
-
tc.variables["BUILD_TEST"] =
|
|
219
|
+
tc.variables["BUILD_TEST"] = 1
|
|
214
220
|
else:
|
|
215
|
-
tc.variables["BUILD_TEST"] =
|
|
221
|
+
tc.variables["BUILD_TEST"] = 0
|
|
216
222
|
|
|
217
223
|
% if len(pkg.get("options", [])) > 0:
|
|
218
224
|
% for op, value in pkg["options"].items():
|
|
219
225
|
% if type(value["default"]) == type(False):
|
|
220
226
|
if self.options.${op}:
|
|
221
|
-
value =
|
|
227
|
+
value = 1
|
|
222
228
|
else:
|
|
223
|
-
value =
|
|
229
|
+
value = 0
|
|
224
230
|
% elif type(value["default"]) == type(""):
|
|
225
231
|
value = str(self.options.${op})
|
|
226
232
|
% elif type(value["default"]) == type(123):
|
|
@@ -234,8 +240,10 @@ class LiteBmcConan(ConanFile):
|
|
|
234
240
|
tc.preprocessor_definitions["LB_${op.upper()}"] = value
|
|
235
241
|
% endfor
|
|
236
242
|
% endif
|
|
237
|
-
|
|
238
|
-
tc.
|
|
243
|
+
default_flags = self._append_default_flags()
|
|
244
|
+
tc.extra_cflags = default_flags
|
|
245
|
+
tc.extra_cxxflags = default_flags
|
|
246
|
+
tc.extra_ldflags = default_flags
|
|
239
247
|
|
|
240
248
|
tc.generate()
|
|
241
249
|
|
|
@@ -17,6 +17,7 @@ class DeployConan(ConanFile):
|
|
|
17
17
|
}
|
|
18
18
|
options = {
|
|
19
19
|
"gcov": [False, True],
|
|
20
|
+
"asan": [False, True],
|
|
20
21
|
"test": [False, True],
|
|
21
22
|
% if len(pkg.get("options", [])) > 0:
|
|
22
23
|
% for op, ctx in pkg["options"].items():
|
|
@@ -29,6 +30,7 @@ class DeployConan(ConanFile):
|
|
|
29
30
|
"shared": True,
|
|
30
31
|
% endif
|
|
31
32
|
"gcov": False,
|
|
33
|
+
"asan": False,
|
|
32
34
|
"test": False,
|
|
33
35
|
% if len(pkg.get("options", [])) > 0:
|
|
34
36
|
% for op, ctx in pkg["options"].items():
|
|
@@ -66,7 +68,9 @@ class DeployConan(ConanFile):
|
|
|
66
68
|
pass
|
|
67
69
|
|
|
68
70
|
def configure(self):
|
|
71
|
+
% if codegen_version.info:
|
|
69
72
|
self.options["lb_base"].compatible_required = "${codegen_version.info.lb_base_compatible_required}"
|
|
73
|
+
% endif
|
|
70
74
|
% if len(pkg.get("requires", {})) > 0:
|
|
71
75
|
% for conan in pkg["requires"].get("compile", []):
|
|
72
76
|
% if conan.get("option") is not None:
|
|
@@ -62,9 +62,9 @@ class TestComponent():
|
|
|
62
62
|
cmd = f"lcov --compat-libtool -c -q -d {build_folder} -o {coverage_dir}/cover.info"
|
|
63
63
|
tool.exec(cmd)
|
|
64
64
|
for dir in test_src_folder:
|
|
65
|
-
cmd = f"lcov --compat-libtool --ignore-errors unused -r {coverage_dir}/cover.info \"{build_folder}/{dir}/*\" -o {coverage_dir}/cover.info"
|
|
65
|
+
cmd = f"lcov --compat-libtool --ignore-errors unused,inconsistent -r {coverage_dir}/cover.info \"{build_folder}/{dir}/*\" -o {coverage_dir}/cover.info"
|
|
66
66
|
tool.exec(cmd)
|
|
67
|
-
cmd = f"lcov --compat-libtool --ignore-errors unused -r {coverage_dir}/cover.info \"*/include/*\" -o {coverage_dir}/cover.info"
|
|
67
|
+
cmd = f"lcov --compat-libtool --ignore-errors unused,inconsistent -r {coverage_dir}/cover.info \"*/include/*\" -o {coverage_dir}/cover.info"
|
|
68
68
|
tool.exec(cmd)
|
|
69
69
|
cmd = f"genhtml -o {coverage_dir}/html --legend {coverage_dir}/cover.info"
|
|
70
70
|
tool.exec(cmd)
|
|
@@ -184,6 +184,10 @@ class TestComponent():
|
|
|
184
184
|
build = BuildComponent(self.build_parser, self.origin_args)
|
|
185
185
|
build.run()
|
|
186
186
|
|
|
187
|
+
if hasattr(build.options, 'asan') and build.options.asan:
|
|
188
|
+
os.environ.setdefault("ASAN_OPTIONS", "halt_on_error=1:detect_leaks=1")
|
|
189
|
+
log.info("ASAN enabled, ASAN_OPTIONS=" + os.environ["ASAN_OPTIONS"])
|
|
190
|
+
|
|
187
191
|
self._make_ld_library_path(build.rootfs_dir)
|
|
188
192
|
self._make_dbus_session(build.rootfs_dir)
|
|
189
193
|
|
|
@@ -65,8 +65,6 @@ class TaskClass(Task):
|
|
|
65
65
|
chunk = line.split()
|
|
66
66
|
if len(chunk) < 5:
|
|
67
67
|
raise errors.PermissionFormatError(f"Permission format with error, line: {line}")
|
|
68
|
-
if not chunk[0].startswith("/"):
|
|
69
|
-
raise errors.PermissionFormatError(f"Permission file error, must begin with \"/\", get: {chunk[0]}")
|
|
70
68
|
if (chunk[2] != "-" and not chunk[2].isnumeric()):
|
|
71
69
|
raise errors.PermissionFormatError(f"Permission mode error, must is numeric or '-', get({chunk[2]})")
|
|
72
70
|
if (chunk[3] != "-" and not chunk[3].isnumeric()) or (chunk[4] != "-" and not chunk[4].isnumeric()):
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# Copyright (c) 2021-2024 litebmc.com
|
|
2
|
+
#
|
|
3
|
+
# This program is free software; you can redistribute it and/or modify it
|
|
4
|
+
# under the terms of the GNU General Public License as published by the
|
|
5
|
+
# Free Software Foundation; either version 3 of the License, or (at your
|
|
6
|
+
# option) any later version.
|
|
7
|
+
"""lbkit 自升级模块"""
|
|
8
|
+
import os
|
|
9
|
+
import subprocess
|
|
10
|
+
import argparse
|
|
11
|
+
import re
|
|
12
|
+
|
|
13
|
+
from packaging.version import Version as PkgVersion
|
|
14
|
+
|
|
15
|
+
from lbkit import __version__ as current_version
|
|
16
|
+
from lbkit.log import Logger
|
|
17
|
+
|
|
18
|
+
log = Logger()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _is_root():
|
|
22
|
+
return os.geteuid() == 0
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_installed_deb_version():
|
|
26
|
+
"""获取已安装的 lbkit deb 包版本"""
|
|
27
|
+
result = subprocess.run(
|
|
28
|
+
["dpkg-query", "-W", "-f=${Version}", "lbkit"],
|
|
29
|
+
capture_output=True, text=True
|
|
30
|
+
)
|
|
31
|
+
if result.returncode != 0:
|
|
32
|
+
return None
|
|
33
|
+
return result.stdout.strip() or None
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _get_installed_rpm_version():
|
|
37
|
+
"""获取已安装的 lbkit rpm 包版本"""
|
|
38
|
+
result = subprocess.run(
|
|
39
|
+
["rpm", "-q", "--queryformat", "%{VERSION}", "lbkit"],
|
|
40
|
+
capture_output=True, text=True
|
|
41
|
+
)
|
|
42
|
+
if result.returncode != 0:
|
|
43
|
+
return None
|
|
44
|
+
return result.stdout.strip() or None
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _get_apt_candidate_version():
|
|
48
|
+
"""通过 apt-cache 获取 lbkit 的候选升级版本"""
|
|
49
|
+
is_root = _is_root()
|
|
50
|
+
sudo = [] if is_root else ["sudo"]
|
|
51
|
+
|
|
52
|
+
# apt update
|
|
53
|
+
log.info("正在更新软件包列表...")
|
|
54
|
+
subprocess.run(sudo + ["apt", "update", "-qq"],
|
|
55
|
+
check=True, capture_output=True)
|
|
56
|
+
|
|
57
|
+
result = subprocess.run(
|
|
58
|
+
["apt-cache", "policy", "lbkit"],
|
|
59
|
+
capture_output=True, text=True
|
|
60
|
+
)
|
|
61
|
+
if result.returncode != 0:
|
|
62
|
+
return None
|
|
63
|
+
for line in result.stdout.splitlines():
|
|
64
|
+
line = line.strip()
|
|
65
|
+
if line.startswith("候选:") or line.startswith("Candidate:"):
|
|
66
|
+
version = line.split(":", 1)[-1].split(": ", 1)[-1].strip()
|
|
67
|
+
if version:
|
|
68
|
+
return version
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _get_dnf_candidate_version():
|
|
73
|
+
"""通过 dnf 获取 lbkit 的候选升级版本"""
|
|
74
|
+
is_root = _is_root()
|
|
75
|
+
sudo = [] if is_root else ["sudo"]
|
|
76
|
+
|
|
77
|
+
# dnf check-update
|
|
78
|
+
log.info("正在更新软件包列表...")
|
|
79
|
+
subprocess.run(sudo + ["dnf", "makecache", "-q"],
|
|
80
|
+
check=True, capture_output=True)
|
|
81
|
+
|
|
82
|
+
result = subprocess.run(
|
|
83
|
+
sudo + ["dnf", "list", "available", "lbkit"],
|
|
84
|
+
capture_output=True, text=True
|
|
85
|
+
)
|
|
86
|
+
if result.returncode != 0:
|
|
87
|
+
return None
|
|
88
|
+
for line in result.stdout.splitlines():
|
|
89
|
+
line = line.strip()
|
|
90
|
+
if line.startswith("lbkit."):
|
|
91
|
+
parts = line.split()
|
|
92
|
+
if len(parts) >= 2:
|
|
93
|
+
# 格式: lbkit.x86_64 0.9.12 repo_name
|
|
94
|
+
return parts[1]
|
|
95
|
+
return None
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _get_pypi_latest_version():
|
|
99
|
+
"""查询 PyPI(或用户配置的镜像源)上 lbkit 的最新版本"""
|
|
100
|
+
try:
|
|
101
|
+
result = subprocess.run(
|
|
102
|
+
[subprocess.sys.executable, "-m", "pip", "index", "versions", "lbkit"],
|
|
103
|
+
capture_output=True, text=True, timeout=15
|
|
104
|
+
)
|
|
105
|
+
if result.returncode != 0:
|
|
106
|
+
log.warn(f"查询 PyPI 版本失败:\n{result.stderr}")
|
|
107
|
+
return None
|
|
108
|
+
# 输出格式: "lbkit (0.9.12)"
|
|
109
|
+
match = re.search(r"lbkit\s*\(([^)]+)\)", result.stdout)
|
|
110
|
+
if match:
|
|
111
|
+
return match.group(1)
|
|
112
|
+
return None
|
|
113
|
+
except Exception as e:
|
|
114
|
+
log.warn(f"查询 PyPI 版本失败: {e}")
|
|
115
|
+
return None
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _install_deb(version):
|
|
119
|
+
"""通过 apt 安装指定版本的 deb 包"""
|
|
120
|
+
is_root = _is_root()
|
|
121
|
+
sudo = [] if is_root else ["sudo"]
|
|
122
|
+
|
|
123
|
+
installed = _get_installed_deb_version()
|
|
124
|
+
if installed == version:
|
|
125
|
+
log.info(f"deb 包已是目标版本 {version}")
|
|
126
|
+
return True
|
|
127
|
+
|
|
128
|
+
log.info(f"正在安装 deb 包: {installed or '未安装'} → {version}")
|
|
129
|
+
result = subprocess.run(
|
|
130
|
+
sudo + ["apt", "install", "-y", "--only-upgrade", f"lbkit={version}"],
|
|
131
|
+
capture_output=True, text=True
|
|
132
|
+
)
|
|
133
|
+
if result.returncode != 0:
|
|
134
|
+
# 指定版本不存在时回退到安装候选版本
|
|
135
|
+
log.warn(f"apt 源中未找到版本 {version},尝试安装候选版本")
|
|
136
|
+
result = subprocess.run(
|
|
137
|
+
sudo + ["apt", "install", "-y", "--only-upgrade", "lbkit"],
|
|
138
|
+
capture_output=True, text=True
|
|
139
|
+
)
|
|
140
|
+
if result.returncode != 0:
|
|
141
|
+
log.error(f"deb 包安装失败:\n{result.stderr}")
|
|
142
|
+
return False
|
|
143
|
+
|
|
144
|
+
new_version = _get_installed_deb_version()
|
|
145
|
+
if new_version:
|
|
146
|
+
log.success(f"deb 包已安装: {new_version}")
|
|
147
|
+
return True
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _install_rpm(version):
|
|
151
|
+
"""通过 dnf 安装指定版本的 rpm 包"""
|
|
152
|
+
is_root = _is_root()
|
|
153
|
+
sudo = [] if is_root else ["sudo"]
|
|
154
|
+
|
|
155
|
+
installed = _get_installed_rpm_version()
|
|
156
|
+
if installed == version:
|
|
157
|
+
log.info(f"rpm 包已是目标版本 {version}")
|
|
158
|
+
return True
|
|
159
|
+
|
|
160
|
+
log.info(f"正在安装 rpm 包: {installed or '未安装'} → {version}")
|
|
161
|
+
result = subprocess.run(
|
|
162
|
+
sudo + ["dnf", "install", "-y", f"lbkit-{version}"],
|
|
163
|
+
capture_output=True, text=True
|
|
164
|
+
)
|
|
165
|
+
if result.returncode != 0:
|
|
166
|
+
log.error(f"rpm 包安装失败:\n{result.stderr}")
|
|
167
|
+
return False
|
|
168
|
+
|
|
169
|
+
new_version = _get_installed_rpm_version()
|
|
170
|
+
if new_version:
|
|
171
|
+
log.success(f"rpm 包已安装: {new_version}")
|
|
172
|
+
return True
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _is_deb_system():
|
|
176
|
+
"""检测是否为 Debian/Ubuntu 系列系统"""
|
|
177
|
+
return os.path.isfile("/etc/debian_version")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _is_rpm_system():
|
|
181
|
+
"""检测是否为 RHEL/Rocky Linux/CentOS 系列系统"""
|
|
182
|
+
if os.path.isfile("/etc/os-release"):
|
|
183
|
+
with open("/etc/os-release", "r") as fp:
|
|
184
|
+
for line in fp:
|
|
185
|
+
if line.strip().startswith("ID="):
|
|
186
|
+
distro_id = line.strip().split("=", 1)[1].strip('"')
|
|
187
|
+
if distro_id in ("rocky", "centos", "almalinux", "rhel", "fedora"):
|
|
188
|
+
return True
|
|
189
|
+
if line.strip().startswith("ID_LIKE="):
|
|
190
|
+
id_like = line.strip().split("=", 1)[1].strip('"')
|
|
191
|
+
if "rhel" in id_like:
|
|
192
|
+
return True
|
|
193
|
+
return os.path.isfile("/etc/redhat-release")
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _pip_install(version):
|
|
197
|
+
"""安装指定版本的 PyPI 包"""
|
|
198
|
+
is_root = _is_root()
|
|
199
|
+
log.info(f"正在安装 PyPI 包 lbkit=={version}...")
|
|
200
|
+
|
|
201
|
+
cmd = [subprocess.sys.executable, "-m", "pip", "install",
|
|
202
|
+
f"lbkit=={version}", "--break-system-packages"]
|
|
203
|
+
if is_root:
|
|
204
|
+
cmd.append("--break-system-packages")
|
|
205
|
+
else:
|
|
206
|
+
cmd.append("--user")
|
|
207
|
+
|
|
208
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
209
|
+
if result.returncode != 0:
|
|
210
|
+
log.error(f"PyPI 包安装失败:\n{result.stderr}")
|
|
211
|
+
return False
|
|
212
|
+
log.success(f"PyPI 包 lbkit=={version} 安装完成")
|
|
213
|
+
return True
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _check_path():
|
|
217
|
+
"""检查 ~/.local/bin 是否在 PATH 中,不在则提示用户"""
|
|
218
|
+
local_bin = os.path.expanduser("~/.local/bin")
|
|
219
|
+
path_env = os.environ.get("PATH", "")
|
|
220
|
+
path_dirs = path_env.split(":")
|
|
221
|
+
|
|
222
|
+
if local_bin in path_dirs:
|
|
223
|
+
return
|
|
224
|
+
|
|
225
|
+
log.warn(f"{local_bin} 不在 PATH 环境变量中")
|
|
226
|
+
log.warn("lbk 命令可能无法直接使用")
|
|
227
|
+
|
|
228
|
+
from lbkit.cli import ask_user_yes_no
|
|
229
|
+
if not ask_user_yes_no(f"是否将 {local_bin} 添加到 ~/.bashrc?"):
|
|
230
|
+
log.info(f"请手动执行: echo 'export PATH=\"{local_bin}:$PATH\"' >> ~/.bashrc")
|
|
231
|
+
return
|
|
232
|
+
|
|
233
|
+
bashrc = os.path.expanduser("~/.bashrc")
|
|
234
|
+
export_line = f'export PATH="{local_bin}:$PATH"\n'
|
|
235
|
+
already_in_bashrc = False
|
|
236
|
+
if os.path.isfile(bashrc):
|
|
237
|
+
with open(bashrc, "r") as f:
|
|
238
|
+
already_in_bashrc = local_bin in f.read()
|
|
239
|
+
|
|
240
|
+
if already_in_bashrc:
|
|
241
|
+
log.info(f"{local_bin} 已在 ~/.bashrc 中,可能是当前 shell 未加载")
|
|
242
|
+
return
|
|
243
|
+
|
|
244
|
+
with open(bashrc, "a") as f:
|
|
245
|
+
f.write("\n# Added by lbkit upgrade\n")
|
|
246
|
+
f.write(export_line)
|
|
247
|
+
log.success(f"已将 {local_bin} 添加到 ~/.bashrc")
|
|
248
|
+
log.info("请执行 source ~/.bashrc 或重新打开终端使配置生效")
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
def upgrade(*args):
|
|
252
|
+
"""执行 lbkit 自升级"""
|
|
253
|
+
parser = argparse.ArgumentParser(
|
|
254
|
+
description="自升级lbkit,查询系统包管理器和PyPI可用版本后安装最新版",
|
|
255
|
+
prog="lbkit upgrade"
|
|
256
|
+
)
|
|
257
|
+
parser.parse_args(*args)
|
|
258
|
+
|
|
259
|
+
log.info(f"当前版本: {current_version}")
|
|
260
|
+
|
|
261
|
+
# 检测系统类型
|
|
262
|
+
is_deb = _is_deb_system()
|
|
263
|
+
is_rpm = _is_rpm_system()
|
|
264
|
+
|
|
265
|
+
if not is_deb and not is_rpm:
|
|
266
|
+
log.error("当前系统不是 Debian/Ubuntu 或 RHEL/Rocky Linux 系列,暂不支持自动升级")
|
|
267
|
+
return 1
|
|
268
|
+
|
|
269
|
+
# 1. 查询系统包管理器候选版本
|
|
270
|
+
sys_ver = None
|
|
271
|
+
if is_deb:
|
|
272
|
+
sys_ver = _get_apt_candidate_version()
|
|
273
|
+
if sys_ver:
|
|
274
|
+
log.info(f"apt 可用版本: {sys_ver}")
|
|
275
|
+
else:
|
|
276
|
+
log.warn("apt 源中未找到 lbkit 包")
|
|
277
|
+
elif is_rpm:
|
|
278
|
+
sys_ver = _get_dnf_candidate_version()
|
|
279
|
+
if sys_ver:
|
|
280
|
+
log.info(f"dnf 可用版本: {sys_ver}")
|
|
281
|
+
else:
|
|
282
|
+
log.warn("dnf 源中未找到 lbkit 包")
|
|
283
|
+
|
|
284
|
+
# 2. 查询 PyPI 最新版本
|
|
285
|
+
pypi_ver = _get_pypi_latest_version()
|
|
286
|
+
if pypi_ver:
|
|
287
|
+
log.info(f"PyPI 最新版本: {pypi_ver}")
|
|
288
|
+
else:
|
|
289
|
+
log.warn("无法查询 PyPI 版本")
|
|
290
|
+
|
|
291
|
+
# 3. 取最大版本号
|
|
292
|
+
versions = {}
|
|
293
|
+
if sys_ver:
|
|
294
|
+
versions["system"] = sys_ver
|
|
295
|
+
if pypi_ver:
|
|
296
|
+
versions["pypi"] = pypi_ver
|
|
297
|
+
|
|
298
|
+
if not versions:
|
|
299
|
+
log.error("未找到任何可安装的版本")
|
|
300
|
+
return 1
|
|
301
|
+
|
|
302
|
+
target = max(versions.values(), key=PkgVersion)
|
|
303
|
+
log.info(f"目标版本: {target}")
|
|
304
|
+
|
|
305
|
+
if PkgVersion(target) <= PkgVersion(current_version):
|
|
306
|
+
log.info(f"当前已是最新版本 {current_version}")
|
|
307
|
+
return 0
|
|
308
|
+
|
|
309
|
+
# 4. 安装系统包(如果系统包管理器有可用版本)
|
|
310
|
+
if sys_ver:
|
|
311
|
+
if is_deb:
|
|
312
|
+
if not _install_deb(target):
|
|
313
|
+
return 1
|
|
314
|
+
elif is_rpm:
|
|
315
|
+
if not _install_rpm(target):
|
|
316
|
+
return 1
|
|
317
|
+
|
|
318
|
+
# 5. 安装 PyPI 包
|
|
319
|
+
if not _pip_install(target):
|
|
320
|
+
return 1
|
|
321
|
+
|
|
322
|
+
# 6. 检查 PATH
|
|
323
|
+
_check_path()
|
|
324
|
+
|
|
325
|
+
log.success(f"升级完成: {current_version} → {target}")
|
|
326
|
+
return 0
|
|
@@ -13,6 +13,7 @@ lbkit/lbkit.py
|
|
|
13
13
|
lbkit/log.py
|
|
14
14
|
lbkit/misc.py
|
|
15
15
|
lbkit/tools.py
|
|
16
|
+
lbkit/upgrade.py
|
|
16
17
|
lbkit.egg-info/PKG-INFO
|
|
17
18
|
lbkit.egg-info/SOURCES.txt
|
|
18
19
|
lbkit.egg-info/dependency_links.txt
|
|
@@ -20,6 +21,7 @@ lbkit.egg-info/entry_points.txt
|
|
|
20
21
|
lbkit.egg-info/requires.txt
|
|
21
22
|
lbkit.egg-info/top_level.txt
|
|
22
23
|
lbkit/ci_robot/__init__.py
|
|
24
|
+
lbkit/codegen/.clang-format
|
|
23
25
|
lbkit/codegen/__init__.py
|
|
24
26
|
lbkit/codegen/codegen.py
|
|
25
27
|
lbkit/codegen/ctype_defination.py
|
|
@@ -18,6 +18,7 @@ from lbkit import errors
|
|
|
18
18
|
from lbkit.codegen.codegen import __version__ as codegen_version
|
|
19
19
|
from lbkit.codegen.codegen import history_versions as codegen_history
|
|
20
20
|
from lbkit.codegen.codegen import Version, CodeGen
|
|
21
|
+
from lbkit.codegen.codegen import _find_clang_format_config
|
|
21
22
|
from lbkit.misc import load_json_schema
|
|
22
23
|
from jsonschema import validate, ValidationError
|
|
23
24
|
|
|
@@ -977,5 +978,103 @@ class TestServerGetterClass(unittest.TestCase):
|
|
|
977
978
|
body = self._assert_getter_in_source(server_h, "path")
|
|
978
979
|
self.assertIn("g_variant_get_string", body)
|
|
979
980
|
|
|
981
|
+
|
|
982
|
+
class TestClangFormatClass(unittest.TestCase):
|
|
983
|
+
"""测试代码生成后的 clang-format 自动格式化"""
|
|
984
|
+
intf_yaml = None
|
|
985
|
+
intf_out = None
|
|
986
|
+
tmp_dir = None
|
|
987
|
+
|
|
988
|
+
@classmethod
|
|
989
|
+
def setUpClass(cls) -> None:
|
|
990
|
+
cls.tmp_dir = tempfile.mktemp(prefix="clang_format_test", suffix=".output")
|
|
991
|
+
cls.intf_yaml = os.path.join(cls.tmp_dir, "test.yaml")
|
|
992
|
+
cls.intf_out = os.path.join(cls.tmp_dir, "output")
|
|
993
|
+
return super().setUpClass()
|
|
994
|
+
|
|
995
|
+
@classmethod
|
|
996
|
+
def tearDownClass(cls) -> None:
|
|
997
|
+
shutil.rmtree(cls.tmp_dir)
|
|
998
|
+
return super().tearDownClass()
|
|
999
|
+
|
|
1000
|
+
def setUp(self):
|
|
1001
|
+
shutil.rmtree(self.tmp_dir, ignore_errors=True)
|
|
1002
|
+
os.makedirs(self.tmp_dir)
|
|
1003
|
+
os.makedirs(self.intf_out)
|
|
1004
|
+
return super().setUp()
|
|
1005
|
+
|
|
1006
|
+
def _mk_interface(self):
|
|
1007
|
+
with open(self.intf_yaml, mode="w+") as fp:
|
|
1008
|
+
fp.write(f"# yaml-language-server: $schema={schema_dir}/idf.v2.json\n")
|
|
1009
|
+
fp.write("version: 1\n")
|
|
1010
|
+
fp.write("description: 测试clang-format格式化\n")
|
|
1011
|
+
fp.write("interface: com.litebmc.FormatTest\n")
|
|
1012
|
+
fp.write("properties:\n")
|
|
1013
|
+
fp.write(" - name: count\n")
|
|
1014
|
+
fp.write(" description: count\n")
|
|
1015
|
+
fp.write(" type: int32\n")
|
|
1016
|
+
|
|
1017
|
+
def test_clang_format_applied(self):
|
|
1018
|
+
"""验证代码生成后文件存在且格式化正常(若 clang-format 可用)"""
|
|
1019
|
+
self._mk_interface()
|
|
1020
|
+
args = ["-d", self.intf_out, "-i", self.intf_yaml]
|
|
1021
|
+
gen = CodeGen(args)
|
|
1022
|
+
gen.run()
|
|
1023
|
+
for ct in ["server", "client", "public"]:
|
|
1024
|
+
h_file = os.path.join(self.intf_out, ct, "test.h")
|
|
1025
|
+
c_file = os.path.join(self.intf_out, ct, "test.c")
|
|
1026
|
+
self.assertTrue(os.path.isfile(h_file), f"{ct}/test.h should exist")
|
|
1027
|
+
self.assertTrue(os.path.isfile(c_file), f"{ct}/test.c should exist")
|
|
1028
|
+
|
|
1029
|
+
def test_config_lookup_idf_priority(self):
|
|
1030
|
+
"""验证 .clang-format 查找优先级:IDF目录 > 组件目录 > toolkit默认"""
|
|
1031
|
+
self._mk_interface()
|
|
1032
|
+
# 在IDF目录创建 .clang-format
|
|
1033
|
+
idf_clang = os.path.join(self.tmp_dir, ".clang-format")
|
|
1034
|
+
with open(idf_clang, "w") as f:
|
|
1035
|
+
f.write("---\nBasedOnStyle: LLVM\n")
|
|
1036
|
+
result = _find_clang_format_config(self.intf_yaml)
|
|
1037
|
+
self.assertEqual(result, idf_clang)
|
|
1038
|
+
|
|
1039
|
+
def test_config_lookup_component_priority(self):
|
|
1040
|
+
"""验证无IDF目录配置时回退到组件目录"""
|
|
1041
|
+
self._mk_interface()
|
|
1042
|
+
# 不在IDF目录创建,只在当前目录(组件目录)创建
|
|
1043
|
+
import unittest.mock
|
|
1044
|
+
comp_dir = os.path.join(self.tmp_dir, "component")
|
|
1045
|
+
os.makedirs(comp_dir)
|
|
1046
|
+
comp_clang = os.path.join(comp_dir, ".clang-format")
|
|
1047
|
+
with open(comp_clang, "w") as f:
|
|
1048
|
+
f.write("---\nBasedOnStyle: LLVM\n")
|
|
1049
|
+
with unittest.mock.patch("os.getcwd", return_value=comp_dir):
|
|
1050
|
+
result = _find_clang_format_config(self.intf_yaml)
|
|
1051
|
+
self.assertEqual(result, comp_clang)
|
|
1052
|
+
|
|
1053
|
+
def test_config_lookup_toolkit_default(self):
|
|
1054
|
+
"""验证无任何自定义配置时回退到toolkit默认"""
|
|
1055
|
+
self._mk_interface()
|
|
1056
|
+
import unittest.mock
|
|
1057
|
+
# 使用空目录作为 CWD,确保没有 .clang-format
|
|
1058
|
+
empty_dir = os.path.join(self.tmp_dir, "empty")
|
|
1059
|
+
os.makedirs(empty_dir)
|
|
1060
|
+
with unittest.mock.patch("os.getcwd", return_value=empty_dir):
|
|
1061
|
+
result = _find_clang_format_config(self.intf_yaml)
|
|
1062
|
+
# toolkit 默认 .clang-format 应该存在
|
|
1063
|
+
self.assertIsNotNone(result)
|
|
1064
|
+
self.assertTrue(os.path.isfile(result))
|
|
1065
|
+
|
|
1066
|
+
def test_missing_clang_format_graceful(self):
|
|
1067
|
+
"""验证 clang-format 不可用时 codegen 正常完成"""
|
|
1068
|
+
self._mk_interface()
|
|
1069
|
+
import unittest.mock
|
|
1070
|
+
with unittest.mock.patch("shutil.which", return_value=None):
|
|
1071
|
+
args = ["-d", self.intf_out, "-i", self.intf_yaml]
|
|
1072
|
+
gen = CodeGen(args)
|
|
1073
|
+
gen.run()
|
|
1074
|
+
for ct in ["server", "client", "public"]:
|
|
1075
|
+
h_file = os.path.join(self.intf_out, ct, "test.h")
|
|
1076
|
+
self.assertTrue(os.path.isfile(h_file), f"{ct}/test.h should exist even without clang-format")
|
|
1077
|
+
|
|
1078
|
+
|
|
980
1079
|
if __name__ == "__main__":
|
|
981
1080
|
unittest.main()
|
|
@@ -60,14 +60,6 @@ class TestDoPermissionBasic(unittest.TestCase):
|
|
|
60
60
|
with self.assertRaises(errors.PermissionFormatError):
|
|
61
61
|
task.do_permission(per)
|
|
62
62
|
|
|
63
|
-
def test_path_not_absolute(self):
|
|
64
|
-
"""路径不以 / 开头应抛出 PermissionFormatError"""
|
|
65
|
-
with tempfile.TemporaryDirectory() as tmpdir:
|
|
66
|
-
per = _write_perm(tmpdir, ["lib/libfoo.so f 555 0 0 - - - - -"])
|
|
67
|
-
task = _make_task()
|
|
68
|
-
with self.assertRaises(errors.PermissionFormatError):
|
|
69
|
-
task.do_permission(per)
|
|
70
|
-
|
|
71
63
|
def test_invalid_mode(self):
|
|
72
64
|
"""mode 字段不合法应抛出 PermissionFormatError"""
|
|
73
65
|
with tempfile.TemporaryDirectory() as tmpdir:
|
lbkit-0.9.11/lbkit/__commit__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__commit__ = '1f8eb04'
|
lbkit-0.9.11/lbkit/__init__.py
DELETED
|
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
|