uv2compdb 0.4.0__py3-none-any.whl → 0.5.1__py3-none-any.whl

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.
uv2compdb/main.py CHANGED
@@ -2,12 +2,13 @@
2
2
  Generate Compilation Database by parse Keil µVision project.
3
3
  """
4
4
 
5
+ import shlex
5
6
  import logging
6
7
  import argparse
7
8
  from pathlib import Path
8
9
  from importlib.metadata import version
9
10
 
10
- from uv2compdb.parser import UV2CompDB, _split_and_strip, generate_compile_commands
11
+ from uv2compdb.parser import UV2CompDB, generate_compile_commands
11
12
 
12
13
  __version__ = version("uv2compdb")
13
14
  logger = logging.getLogger(__name__)
@@ -63,7 +64,7 @@ def main() -> int:
63
64
  if not args.target:
64
65
  args.target = targets[0]
65
66
  logger.warning(
66
- f"Project has multi targets: {targets}, use the first {args.target}"
67
+ f"Project has target(s): {targets}, use the first {args.target}"
67
68
  )
68
69
  elif args.target not in targets:
69
70
  logger.error(f"Not found target: {args.target}")
@@ -80,7 +81,7 @@ def main() -> int:
80
81
  target_setting = uv2compdb.parse(args.target, args.build)
81
82
  command_objects = uv2compdb.generate_command_objects(
82
83
  target_setting,
83
- _split_and_strip(args.arguments, delimiter=" ") if args.arguments else [],
84
+ shlex.split(args.arguments) if args.arguments else [],
84
85
  args.predefined,
85
86
  )
86
87
  if not generate_compile_commands(command_objects, args.output):
uv2compdb/parser.py CHANGED
@@ -43,12 +43,12 @@ PREDEFINED_FILTER_ARGUMENT_REGEX = [
43
43
  ]
44
44
 
45
45
 
46
- def _to_posix_path(path: str) -> str:
46
+ def to_posix_path(path: str) -> str:
47
47
  """Convert Windows path separators to POSIX format."""
48
48
  return path.replace("\\", "/")
49
49
 
50
50
 
51
- def _split_and_strip(text: str, delimiter: str) -> list[str]:
51
+ def split_and_strip(text: str, delimiter: str) -> list[str]:
52
52
  """Split text by delimiter and strip whitespace from each part."""
53
53
  return [striped for item in text.split(delimiter) if (striped := item.strip())]
54
54
 
@@ -58,6 +58,7 @@ class Toolchain:
58
58
  path: str
59
59
  compiler: str
60
60
  assembler: str
61
+ xml_tag: tuple[str, str]
61
62
 
62
63
 
63
64
  @dataclass
@@ -108,10 +109,10 @@ class VariousControls:
108
109
  # 'MBEDTLS_CONFIG_FILE=/\"config-aes-cbc.h/\"'
109
110
  # => '-DMBEDTLS_CONFIG_FILE="config-aes-cbc.h"'
110
111
  return (
111
- [f"-I{_to_posix_path(x)}" for x in self.include_path]
112
- + [f"{_to_posix_path(x)}" for x in self.misc_controls]
113
- + [f"-U{_to_posix_path(x)}" for x in self.undefine]
114
- + ["-D" + _to_posix_path(x.replace(r'\\"', '"')) for x in self.define]
112
+ [f"-I{to_posix_path(x)}" for x in self.include_path]
113
+ + [f"{to_posix_path(x)}" for x in self.misc_controls]
114
+ + [f"-U{to_posix_path(x)}" for x in self.undefine]
115
+ + ["-D" + to_posix_path(x.replace(r'\\"', '"')) for x in self.define]
115
116
  )
116
117
 
117
118
  @classmethod
@@ -129,16 +130,48 @@ class UV2CompDB:
129
130
 
130
131
  # TODO: how to deal with delimiters inside text (e.g., -DFOO="(1, 2)")
131
132
  UV_VARIOUS_CONTROLS_MAP: dict[str, tuple[str, Callable[[str], list[str]]]] = {
132
- "MiscControls": ("misc_controls", partial(_split_and_strip, delimiter=" ")),
133
- "Define": ("define", partial(_split_and_strip, delimiter=",")),
134
- "Undefine": ("undefine", partial(_split_and_strip, delimiter=",")),
135
- "IncludePath": ("include_path", partial(_split_and_strip, delimiter=";")),
133
+ "MiscControls": ("misc_controls", partial(split_and_strip, delimiter=" ")),
134
+ "Define": ("define", partial(split_and_strip, delimiter=",")),
135
+ "Undefine": ("undefine", partial(split_and_strip, delimiter=",")),
136
+ "IncludePath": ("include_path", partial(split_and_strip, delimiter=";")),
137
+ }
138
+
139
+ UV_C51_XML_TAG: tuple[str, str] = ("C51", "Ax51")
140
+ UV_ARM_XML_TAG: tuple[str, str] = ("Cads", "Aads")
141
+
142
+ # Language-Extensions: https://developer.arm.com/documentation/101655/0961/Cx51-User-s-Guide/Language-Extensions?lang=en
143
+ UV_C51_EXTENSION_KEYWORDS: dict[str, str] = {
144
+ # Data Type
145
+ "bit": "unsigned char",
146
+ "sbit": "volatile unsigned char",
147
+ "sfr": "volatile unsigned char",
148
+ "sfr16": "volatile unsigned short",
149
+ # Memory Models
150
+ "small": "",
151
+ "compact": "",
152
+ "large": "",
153
+ # Memory Type
154
+ "bdata": "",
155
+ "data": "",
156
+ "idata": "",
157
+ "pdata": "",
158
+ "xdata": "",
159
+ "far": "",
160
+ "code": "",
161
+ # Other
162
+ "_at_": "",
163
+ "alien": "",
164
+ "interrupt": "",
165
+ "_priority_": "",
166
+ "reentrant": "",
167
+ "_task_": "",
168
+ "using": "",
136
169
  }
137
170
 
138
171
  UV_TOOLCHAIN_MAP: dict[str, Toolchain] = {
139
- "0x00": Toolchain("", "c51", ""),
140
- "0x40": Toolchain("", "armcc", "armasm"),
141
- "0x41": Toolchain("", "armclang", "armasm"),
172
+ "0x00": Toolchain("", "c51", "a51", UV_C51_XML_TAG),
173
+ "0x40": Toolchain("", "armcc", "armasm", UV_ARM_XML_TAG),
174
+ "0x41": Toolchain("", "armclang", "armasm", UV_ARM_XML_TAG),
142
175
  }
143
176
 
144
177
  UV_CLI_ERRORLEVEL_MAP: dict[int, str] = {
@@ -174,8 +207,10 @@ class UV2CompDB:
174
207
  return None
175
208
  return elem.text
176
209
 
177
- def get_various_controls(self, elem: ET.Element | None) -> VariousControls | None:
178
- if elem is None:
210
+ def get_various_controls(
211
+ self, elem: ET.Element | None, xml_tag: str | None
212
+ ) -> VariousControls | None:
213
+ if elem is None or not xml_tag:
179
214
  return None
180
215
 
181
216
  # None: True, "0": False, "1": True, "2": inherit
@@ -184,7 +219,7 @@ class UV2CompDB:
184
219
 
185
220
  result = {}
186
221
  for name, (var_name, pred) in self.UV_VARIOUS_CONTROLS_MAP.items():
187
- text = self._get_text(elem.find(f".//Cads/VariousControls/{name}"))
222
+ text = self._get_text(elem.find(f".//{xml_tag}/VariousControls/{name}"))
188
223
  result[var_name] = pred(text) if text else []
189
224
  return VariousControls(**result)
190
225
 
@@ -199,8 +234,15 @@ class UV2CompDB:
199
234
  if not (uv4_path := shutil.which("uv4")):
200
235
  return False
201
236
 
202
- cmd = f"{uv4_path} -b -t {target_name} {self.project_path.resolve().as_posix()} -j0"
203
- logger.info(f"Run: `{cmd}`")
237
+ cmd = [
238
+ uv4_path,
239
+ "-b",
240
+ "-t",
241
+ target_name,
242
+ self.project_path.resolve().as_posix(),
243
+ "-j0",
244
+ ]
245
+ logger.info(f"Run: `{subprocess.list2cmdline(cmd)}`")
204
246
  try:
205
247
  result = subprocess.run(cmd, capture_output=True, text=True)
206
248
  logger.info(
@@ -242,11 +284,14 @@ class UV2CompDB:
242
284
  if not (m := TOOLCHAIN_REGEX.search(text)):
243
285
  return None
244
286
 
245
- toolchain_path = _to_posix_path(m.group(1))
287
+ toolchain_path = to_posix_path(m.group(1))
246
288
  return Toolchain(
247
289
  path=toolchain_path,
248
290
  compiler=f"{toolchain_path}/{m.group(2)}",
249
291
  assembler=f"{toolchain_path}/{m.group(3)}",
292
+ xml_tag=self.UV_C51_XML_TAG
293
+ if "c51" in m.group(2).lower()
294
+ else self.UV_ARM_XML_TAG,
250
295
  )
251
296
 
252
297
  def get_toolchain_from_xml(self, target: ET.Element | None) -> Toolchain | None:
@@ -269,13 +314,14 @@ class UV2CompDB:
269
314
  else toolchain.path
270
315
  ),
271
316
  compiler=(
272
- _to_posix_path(compiler_path) if compiler_path else toolchain.compiler
317
+ to_posix_path(compiler_path) if compiler_path else toolchain.compiler
273
318
  ),
274
319
  assembler=(
275
320
  (Path(compiler_path).parent / toolchain.assembler).resolve().as_posix()
276
321
  if compiler_path
277
322
  else toolchain.assembler
278
323
  ),
324
+ xml_tag=toolchain.xml_tag,
279
325
  )
280
326
 
281
327
  def get_toolchain(
@@ -340,17 +386,21 @@ class UV2CompDB:
340
386
  file_objects.append(FileObject(file=file, arguments=args))
341
387
  return file_objects
342
388
 
343
- def parse_xml(self, target: ET.Element | None) -> list[FileObject]:
344
- if target is None:
389
+ def parse_xml(
390
+ self, target: ET.Element | None, toolchain: Toolchain | None
391
+ ) -> list[FileObject]:
392
+ if target is None or toolchain is None:
345
393
  return []
346
394
 
347
- if (target_vc := self.get_various_controls(target)) is None:
395
+ xml_tag = toolchain.xml_tag[0]
396
+
397
+ if (target_vc := self.get_various_controls(target, xml_tag)) is None:
348
398
  logger.warning("Not found target_controls in target")
349
399
  return []
350
400
 
351
401
  file_objects = []
352
402
  for group in target.findall(".//Group"):
353
- if (group_vc := self.get_various_controls(group)) is None:
403
+ if (group_vc := self.get_various_controls(group, xml_tag)) is None:
354
404
  continue
355
405
 
356
406
  current_vc = VariousControls.merge(target_vc, group_vc)
@@ -358,17 +408,17 @@ class UV2CompDB:
358
408
  file_path = self._get_text(file.find("FilePath"))
359
409
  # file_type = self._get_text(file.find("FileType"))
360
410
 
361
- if not file_path or not file_path.endswith(
362
- (".s", ".c", ".cpp", ".cc", ".cx", ".cxx")
411
+ if not file_path or not file_path.lower().endswith(
412
+ (".a51", ".s", ".c", ".cpp", ".cc", ".cx", ".cxx")
363
413
  ):
364
414
  continue
365
415
 
366
- if (file_controls := self.get_various_controls(file)) is None:
416
+ if (file_controls := self.get_various_controls(file, xml_tag)) is None:
367
417
  continue
368
418
 
369
419
  file_objects.append(
370
420
  FileObject(
371
- file=_to_posix_path(file_path),
421
+ file=to_posix_path(file_path),
372
422
  arguments=VariousControls.merge(
373
423
  current_vc, file_controls
374
424
  ).get_options(),
@@ -387,9 +437,15 @@ class UV2CompDB:
387
437
  return None
388
438
  logger.info(f"Toolchain: {toolchain}")
389
439
 
390
- if not (file_objects := self.parse_dep(target, try_build)):
391
- logger.warning("Not found dep file, fallback to parse xml")
392
- file_objects = self.parse_xml(target)
440
+ if toolchain.xml_tag == self.UV_C51_XML_TAG:
441
+ file_objects = self.parse_xml(target, toolchain)
442
+ elif toolchain.xml_tag == self.UV_ARM_XML_TAG:
443
+ if not (file_objects := self.parse_dep(target, try_build)):
444
+ logger.warning("Not found dep file, fallback to parse xml")
445
+ file_objects = self.parse_xml(target, toolchain)
446
+ else:
447
+ logger.error(f"Unknown {toolchain.xml_tag=}")
448
+ return None
393
449
 
394
450
  return TargetSetting(
395
451
  name=target_name,
@@ -399,16 +455,18 @@ class UV2CompDB:
399
455
 
400
456
  @staticmethod
401
457
  @lru_cache(maxsize=32)
402
- def _get_predefined_macros_cached(compiler: str, args_str: str) -> tuple[str, ...]:
458
+ def _get_predefined_macros_cached(
459
+ compiler: str, args: tuple[str, ...]
460
+ ) -> tuple[str, ...]:
403
461
  """Get predefined macros from compiler with caching."""
404
462
  if "armcc" in compiler.lower():
405
- cmd = f"{compiler} {args_str} --list_macros"
463
+ cmd = [compiler, *args, "--list_macros"]
406
464
  elif "armclang" in compiler.lower():
407
- cmd = f"{compiler} {args_str} --target=arm-arm-none-eabi -dM -E -"
465
+ cmd = [compiler, *args, "--target=arm-arm-none-eabi", "-dM", "-E", "-"]
408
466
  else:
409
467
  return ()
410
468
 
411
- logger.info(f"Get predefined macro by: `{cmd}`")
469
+ logger.info(f"Get predefined macro by: `{subprocess.list2cmdline(cmd)}`")
412
470
  try:
413
471
  result = subprocess.run(cmd, capture_output=True, text=True, input="")
414
472
  if result.returncode != 0:
@@ -433,6 +491,21 @@ class UV2CompDB:
433
491
  if toolchain is None or not args:
434
492
  return []
435
493
 
494
+ if toolchain.xml_tag == self.UV_C51_XML_TAG:
495
+ # Predefined-Macros: https://developer.arm.com/documentation/101655/0961/Cx51-User-s-Guide/Preprocessor/Macros/Predefined-Macros?lang=en
496
+ c51_defs = ["-D__C51__"]
497
+ c51_defs.extend(
498
+ f"-D{key}={val}" for key, val in self.UV_C51_EXTENSION_KEYWORDS.items()
499
+ )
500
+
501
+ # /path/to/keil/c51/bin -> /path/to/keil/c51/inc
502
+ if (idx := toolchain.path.lower().rfind("bin")) != -1:
503
+ c51_inc = toolchain.path[:idx] + toolchain.path[idx:].lower().replace(
504
+ "bin", "inc"
505
+ )
506
+ c51_defs.append(f"-I{c51_inc}")
507
+ return c51_defs
508
+
436
509
  filtered_args = []
437
510
  args_iter = iter(args)
438
511
  for arg in args_iter:
@@ -445,9 +518,7 @@ class UV2CompDB:
445
518
  next(args_iter, None)
446
519
 
447
520
  return list(
448
- self._get_predefined_macros_cached(
449
- toolchain.compiler, " ".join(filtered_args)
450
- )
521
+ self._get_predefined_macros_cached(toolchain.compiler, tuple(filtered_args))
451
522
  )
452
523
 
453
524
  def filter_unknown_argument(
@@ -487,7 +558,8 @@ class UV2CompDB:
487
558
  self.get_predefined_macros(
488
559
  target_setting.toolchain, file_object.arguments
489
560
  )
490
- if predefined_macros and not file_object.file.endswith(".s")
561
+ if predefined_macros
562
+ and not file_object.file.lower().endswith((".a51", ".s"))
491
563
  else []
492
564
  )
493
565
  arguments = self.filter_unknown_argument(
@@ -499,13 +571,16 @@ class UV2CompDB:
499
571
  file=file_object.file,
500
572
  arguments=(
501
573
  [
502
- target_setting.toolchain.compiler
503
- if not file_object.file.endswith(".s")
504
- else target_setting.toolchain.assembler
574
+ (
575
+ target_setting.toolchain.compiler
576
+ if not file_object.file.lower().endswith((".a51", ".s"))
577
+ else target_setting.toolchain.assembler
578
+ )
505
579
  ]
506
580
  + toolchain_args
507
581
  + arguments
508
582
  + extra_args
583
+ + [file_object.file]
509
584
  ),
510
585
  )
511
586
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: uv2compdb
3
- Version: 0.4.0
3
+ Version: 0.5.1
4
4
  Summary: Generate Compilation Database by parse Keil µVision project
5
5
  Keywords: keil,MDK,µVision,clangd,Compilation Database,compiled_commands.json
6
6
  Author: xbin
@@ -79,12 +79,11 @@ optional arguments:
79
79
 
80
80
  ## Limit
81
81
 
82
- + [ ] Not support C51
83
82
  + [x] Not parsed `"Options" -> "C/C++" -> "Language / Code Generation"`
84
83
  + [x] Not parsed `"Options" -> "ASM"`, so Asm file use same options with C file
85
84
  + [x] Can't parse **RTE** components
86
85
  + [x] Can't add toolchain predefined macros and include path
87
- + [ ] The support for ARMCC (AC5) not well
86
+ + [ ] The support for C51 / ARMCC (AC5) not well
88
87
  + need config `.clangd` manually
89
88
 
90
89
  ## [Clangd]
@@ -0,0 +1,9 @@
1
+ uv2compdb/__init__.py,sha256=eL3VL6_9X_0E-oz3h4d1ovhCLrATHvviKkyZQOguqqQ,55
2
+ uv2compdb/__main__.py,sha256=6nm32QqZwc9URAzRAwrgJTyoLM-nGu0r9wH5iZfOKuE,70
3
+ uv2compdb/main.py,sha256=SvgdSzYk7jVywnlm0y67Vb15j8LOaPxFr2bvAjal96M,2970
4
+ uv2compdb/parser.py,sha256=v4GTmzwradOIFsgbhrDj_cO8iaJXWhfmDH2NcRHo-5o,21630
5
+ uv2compdb-0.5.1.dist-info/licenses/LICENSE,sha256=jBXhW_dm3ZqVqD8CitzmaDMGPonkPOmqWSDaIEUO30M,1085
6
+ uv2compdb-0.5.1.dist-info/WHEEL,sha256=XjEbIc5-wIORjWaafhI6vBtlxDBp7S9KiujWF1EM7Ak,79
7
+ uv2compdb-0.5.1.dist-info/entry_points.txt,sha256=O6qqKx-ZWjCetvl-PiycCpx92c9Xwm-EN9_UZEMHo04,46
8
+ uv2compdb-0.5.1.dist-info/METADATA,sha256=cS-Bz5kZesMNJMEiV7bGpMmJdbC7L7WBhh5sce9rbpE,3549
9
+ uv2compdb-0.5.1.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- uv2compdb/__init__.py,sha256=eL3VL6_9X_0E-oz3h4d1ovhCLrATHvviKkyZQOguqqQ,55
2
- uv2compdb/__main__.py,sha256=6nm32QqZwc9URAzRAwrgJTyoLM-nGu0r9wH5iZfOKuE,70
3
- uv2compdb/main.py,sha256=0druk2CVT-FfUoHjYayC3J27zAEXBFPA1tYz0DhSeOg,2998
4
- uv2compdb/parser.py,sha256=BN-MHujtrSiL9wibFpPGIu8le9WEWZhLtAnXdUXFeig,18927
5
- uv2compdb-0.4.0.dist-info/licenses/LICENSE,sha256=jBXhW_dm3ZqVqD8CitzmaDMGPonkPOmqWSDaIEUO30M,1085
6
- uv2compdb-0.4.0.dist-info/WHEEL,sha256=XjEbIc5-wIORjWaafhI6vBtlxDBp7S9KiujWF1EM7Ak,79
7
- uv2compdb-0.4.0.dist-info/entry_points.txt,sha256=O6qqKx-ZWjCetvl-PiycCpx92c9Xwm-EN9_UZEMHo04,46
8
- uv2compdb-0.4.0.dist-info/METADATA,sha256=k7L8K53W4BihcOLFgu4MBBRlXsoleqakCkf1ZfsTQF8,3566
9
- uv2compdb-0.4.0.dist-info/RECORD,,