idf-build-apps 2.6.3__py3-none-any.whl → 2.7.0__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.
- idf_build_apps/__init__.py +1 -1
- idf_build_apps/app.py +32 -54
- idf_build_apps/constants.py +0 -11
- idf_build_apps/junit/report.py +2 -2
- idf_build_apps/log.py +88 -57
- idf_build_apps/main.py +23 -9
- {idf_build_apps-2.6.3.dist-info → idf_build_apps-2.7.0.dist-info}/METADATA +2 -1
- {idf_build_apps-2.6.3.dist-info → idf_build_apps-2.7.0.dist-info}/RECORD +11 -11
- {idf_build_apps-2.6.3.dist-info → idf_build_apps-2.7.0.dist-info}/LICENSE +0 -0
- {idf_build_apps-2.6.3.dist-info → idf_build_apps-2.7.0.dist-info}/WHEEL +0 -0
- {idf_build_apps-2.6.3.dist-info → idf_build_apps-2.7.0.dist-info}/entry_points.txt +0 -0
idf_build_apps/__init__.py
CHANGED
idf_build_apps/app.py
CHANGED
|
@@ -36,7 +36,6 @@ from .constants import (
|
|
|
36
36
|
IDF_VERSION_PATCH,
|
|
37
37
|
PREVIEW_TARGETS,
|
|
38
38
|
PROJECT_DESCRIPTION_JSON,
|
|
39
|
-
BuildStage,
|
|
40
39
|
BuildStatus,
|
|
41
40
|
)
|
|
42
41
|
from .manifest.manifest import (
|
|
@@ -55,17 +54,7 @@ from .utils import (
|
|
|
55
54
|
to_list,
|
|
56
55
|
)
|
|
57
56
|
|
|
58
|
-
|
|
59
|
-
class _AppBuildStageFilter(logging.Filter):
|
|
60
|
-
def __init__(self, *args, app, **kwargs):
|
|
61
|
-
super().__init__(*args, **kwargs)
|
|
62
|
-
self.app = app
|
|
63
|
-
|
|
64
|
-
def filter(self, record: logging.LogRecord) -> bool:
|
|
65
|
-
if self.app._build_stage:
|
|
66
|
-
record.build_stage = self.app._build_stage.value
|
|
67
|
-
|
|
68
|
-
return True
|
|
57
|
+
LOGGER = logging.getLogger(__name__)
|
|
69
58
|
|
|
70
59
|
|
|
71
60
|
class App(BaseModel):
|
|
@@ -120,7 +109,6 @@ class App(BaseModel):
|
|
|
120
109
|
build_status: BuildStatus = BuildStatus.UNKNOWN
|
|
121
110
|
build_comment: t.Optional[str] = None
|
|
122
111
|
|
|
123
|
-
_build_stage: t.Optional[BuildStage] = None
|
|
124
112
|
_build_duration: float = 0
|
|
125
113
|
_build_timestamp: t.Optional[datetime] = None
|
|
126
114
|
|
|
@@ -176,9 +164,6 @@ class App(BaseModel):
|
|
|
176
164
|
# private attrs, won't be dumped to json
|
|
177
165
|
self._checked_should_build = False
|
|
178
166
|
|
|
179
|
-
self._logger = logging.getLogger(f'{__name__}.{hash(self)}')
|
|
180
|
-
self._logger.addFilter(_AppBuildStageFilter(app=self))
|
|
181
|
-
|
|
182
167
|
self._sdkconfig_files, self._sdkconfig_files_defined_target = self._process_sdkconfig_files()
|
|
183
168
|
|
|
184
169
|
@classmethod
|
|
@@ -346,10 +331,10 @@ class App(BaseModel):
|
|
|
346
331
|
# use filepath if abs/rel already point to itself
|
|
347
332
|
if not os.path.isfile(f):
|
|
348
333
|
# find it in the app_dir
|
|
349
|
-
|
|
334
|
+
LOGGER.debug('sdkconfig file %s not found, checking under app_dir...', f)
|
|
350
335
|
f = os.path.join(self.app_dir, f)
|
|
351
336
|
if not os.path.isfile(f):
|
|
352
|
-
|
|
337
|
+
LOGGER.debug('sdkconfig file %s not found, skipping...', f)
|
|
353
338
|
continue
|
|
354
339
|
|
|
355
340
|
expanded_fp = os.path.join(expanded_dir, os.path.basename(f))
|
|
@@ -377,14 +362,14 @@ class App(BaseModel):
|
|
|
377
362
|
with open(f) as fr:
|
|
378
363
|
with open(expanded_fp) as new_fr:
|
|
379
364
|
if fr.read() == new_fr.read():
|
|
380
|
-
|
|
365
|
+
LOGGER.debug('Use sdkconfig file %s', f)
|
|
381
366
|
try:
|
|
382
367
|
os.unlink(expanded_fp)
|
|
383
368
|
except OSError:
|
|
384
|
-
|
|
369
|
+
LOGGER.debug('Failed to remove file %s', expanded_fp)
|
|
385
370
|
real_sdkconfig_files.append(f)
|
|
386
371
|
else:
|
|
387
|
-
|
|
372
|
+
LOGGER.debug('Expand sdkconfig file %s to %s', f, expanded_fp)
|
|
388
373
|
real_sdkconfig_files.append(expanded_fp)
|
|
389
374
|
# copy the related target-specific sdkconfig files
|
|
390
375
|
par_dir = os.path.abspath(os.path.join(f, '..'))
|
|
@@ -392,7 +377,7 @@ class App(BaseModel):
|
|
|
392
377
|
os.path.join(par_dir, str(p))
|
|
393
378
|
for p in Path(par_dir).glob(os.path.basename(f) + f'.{self.target}')
|
|
394
379
|
):
|
|
395
|
-
|
|
380
|
+
LOGGER.debug(
|
|
396
381
|
'Copy target-specific sdkconfig file %s to %s', target_specific_file, expanded_dir
|
|
397
382
|
)
|
|
398
383
|
shutil.copy(target_specific_file, expanded_dir)
|
|
@@ -474,21 +459,16 @@ class App(BaseModel):
|
|
|
474
459
|
return wrapper
|
|
475
460
|
|
|
476
461
|
def _pre_build(self) -> None:
|
|
477
|
-
if self.dry_run:
|
|
478
|
-
self._build_stage = BuildStage.DRY_RUN
|
|
479
|
-
else:
|
|
480
|
-
self._build_stage = BuildStage.PRE_BUILD
|
|
481
|
-
|
|
482
462
|
if self.build_status == BuildStatus.SKIPPED:
|
|
483
463
|
return
|
|
484
464
|
|
|
485
465
|
if self.work_dir != self.app_dir:
|
|
486
466
|
if os.path.exists(self.work_dir):
|
|
487
|
-
|
|
467
|
+
LOGGER.debug('Removed existing work dir: %s', self.work_dir)
|
|
488
468
|
if not self.dry_run:
|
|
489
469
|
shutil.rmtree(self.work_dir)
|
|
490
470
|
|
|
491
|
-
|
|
471
|
+
LOGGER.debug('Copied app from %s to %s', self.app_dir, self.work_dir)
|
|
492
472
|
if not self.dry_run:
|
|
493
473
|
# if the new directory inside the original directory,
|
|
494
474
|
# make sure not to go into recursion.
|
|
@@ -502,7 +482,7 @@ class App(BaseModel):
|
|
|
502
482
|
shutil.copytree(self.app_dir, self.work_dir, ignore=ignore, symlinks=True)
|
|
503
483
|
|
|
504
484
|
if os.path.exists(self.build_path):
|
|
505
|
-
|
|
485
|
+
LOGGER.debug('Removed existing build dir: %s', self.build_path)
|
|
506
486
|
if not self.dry_run:
|
|
507
487
|
shutil.rmtree(self.build_path)
|
|
508
488
|
|
|
@@ -511,17 +491,17 @@ class App(BaseModel):
|
|
|
511
491
|
|
|
512
492
|
sdkconfig_file = os.path.join(self.work_dir, 'sdkconfig')
|
|
513
493
|
if os.path.exists(sdkconfig_file):
|
|
514
|
-
|
|
494
|
+
LOGGER.debug('Removed existing sdkconfig file: %s', sdkconfig_file)
|
|
515
495
|
if not self.dry_run:
|
|
516
496
|
os.unlink(sdkconfig_file)
|
|
517
497
|
|
|
518
498
|
if os.path.isfile(self.build_log_path):
|
|
519
|
-
|
|
499
|
+
LOGGER.debug('Removed existing build log file: %s', self.build_log_path)
|
|
520
500
|
if not self.dry_run:
|
|
521
501
|
os.unlink(self.build_log_path)
|
|
522
502
|
elif not self.dry_run:
|
|
523
503
|
os.makedirs(os.path.dirname(self.build_log_path), exist_ok=True)
|
|
524
|
-
|
|
504
|
+
LOGGER.info('Writing build log to %s', self.build_log_path)
|
|
525
505
|
|
|
526
506
|
if self.dry_run:
|
|
527
507
|
self.build_status = BuildStatus.SKIPPED
|
|
@@ -570,8 +550,6 @@ class App(BaseModel):
|
|
|
570
550
|
):
|
|
571
551
|
return
|
|
572
552
|
|
|
573
|
-
self._build_stage = BuildStage.POST_BUILD
|
|
574
|
-
|
|
575
553
|
# both status applied
|
|
576
554
|
if self.copy_sdkconfig:
|
|
577
555
|
try:
|
|
@@ -580,9 +558,9 @@ class App(BaseModel):
|
|
|
580
558
|
os.path.join(self.build_path, 'sdkconfig'),
|
|
581
559
|
)
|
|
582
560
|
except Exception as e:
|
|
583
|
-
|
|
561
|
+
LOGGER.warning('Copy sdkconfig file from failed: %s', e)
|
|
584
562
|
else:
|
|
585
|
-
|
|
563
|
+
LOGGER.debug('Copied sdkconfig file from %s to %s', self.work_dir, self.build_path)
|
|
586
564
|
|
|
587
565
|
# for originally success builds generate size.json if enabled
|
|
588
566
|
#
|
|
@@ -592,7 +570,7 @@ class App(BaseModel):
|
|
|
592
570
|
self.write_size_json()
|
|
593
571
|
|
|
594
572
|
if not os.path.isfile(self.build_log_path):
|
|
595
|
-
|
|
573
|
+
LOGGER.warning(f'{self.build_log_path} does not exist. Skipping post build actions...')
|
|
596
574
|
return
|
|
597
575
|
|
|
598
576
|
# check warnings
|
|
@@ -603,21 +581,21 @@ class App(BaseModel):
|
|
|
603
581
|
is_error_or_warning, ignored = self.is_error_or_warning(line)
|
|
604
582
|
if is_error_or_warning:
|
|
605
583
|
if ignored:
|
|
606
|
-
|
|
584
|
+
LOGGER.info('[Ignored warning] %s', line)
|
|
607
585
|
else:
|
|
608
|
-
|
|
586
|
+
LOGGER.warning('%s', line)
|
|
609
587
|
has_unignored_warning = True
|
|
610
588
|
|
|
611
589
|
# for failed builds, print last few lines to help debug
|
|
612
590
|
if self.build_status == BuildStatus.FAILED:
|
|
613
591
|
# print last few lines to help debug
|
|
614
|
-
|
|
592
|
+
LOGGER.error(
|
|
615
593
|
'Last %s lines from the build log "%s":',
|
|
616
594
|
self.LOG_DEBUG_LINES,
|
|
617
595
|
self.build_log_path,
|
|
618
596
|
)
|
|
619
597
|
for line in lines[-self.LOG_DEBUG_LINES :]:
|
|
620
|
-
|
|
598
|
+
LOGGER.error('%s', line)
|
|
621
599
|
# correct build status for originally successful builds
|
|
622
600
|
elif self.build_status == BuildStatus.SUCCESS:
|
|
623
601
|
if self.check_warnings and has_unignored_warning:
|
|
@@ -636,7 +614,7 @@ class App(BaseModel):
|
|
|
636
614
|
# remove temp log file
|
|
637
615
|
if self._is_build_log_path_temp:
|
|
638
616
|
os.unlink(self.build_log_path)
|
|
639
|
-
|
|
617
|
+
LOGGER.debug('Removed success build temporary log file: %s', self.build_log_path)
|
|
640
618
|
|
|
641
619
|
# Cleanup build directory if not preserving
|
|
642
620
|
if not self.preserve:
|
|
@@ -649,17 +627,17 @@ class App(BaseModel):
|
|
|
649
627
|
self.build_path,
|
|
650
628
|
exclude_file_patterns=exclude_list,
|
|
651
629
|
)
|
|
652
|
-
|
|
630
|
+
LOGGER.debug('Removed built binaries under: %s', self.build_path)
|
|
653
631
|
|
|
654
632
|
def _build(
|
|
655
633
|
self,
|
|
656
634
|
*,
|
|
657
|
-
manifest_rootpath: t.Optional[str] = None,
|
|
658
|
-
modified_components: t.Optional[t.List[str]] = None,
|
|
659
|
-
modified_files: t.Optional[t.List[str]] = None,
|
|
660
|
-
check_app_dependencies: bool = False,
|
|
635
|
+
manifest_rootpath: t.Optional[str] = None,
|
|
636
|
+
modified_components: t.Optional[t.List[str]] = None,
|
|
637
|
+
modified_files: t.Optional[t.List[str]] = None,
|
|
638
|
+
check_app_dependencies: bool = False,
|
|
661
639
|
) -> None:
|
|
662
|
-
|
|
640
|
+
pass
|
|
663
641
|
|
|
664
642
|
def _write_size_json(self) -> None:
|
|
665
643
|
if not self.size_json_path:
|
|
@@ -667,7 +645,7 @@ class App(BaseModel):
|
|
|
667
645
|
|
|
668
646
|
map_file = find_first_match('*.map', self.build_path)
|
|
669
647
|
if not map_file:
|
|
670
|
-
|
|
648
|
+
LOGGER.warning(
|
|
671
649
|
'.map file not found. Cannot write size json to file: %s',
|
|
672
650
|
self.size_json_path,
|
|
673
651
|
)
|
|
@@ -703,18 +681,18 @@ class App(BaseModel):
|
|
|
703
681
|
check=True,
|
|
704
682
|
)
|
|
705
683
|
|
|
706
|
-
|
|
684
|
+
LOGGER.debug('Generated size info to %s', self.size_json_path)
|
|
707
685
|
|
|
708
686
|
def write_size_json(self) -> None:
|
|
709
687
|
if self.target in PREVIEW_TARGETS:
|
|
710
688
|
# targets in preview may not yet have support in esp-idf-size
|
|
711
|
-
|
|
689
|
+
LOGGER.info('Skipping generation of size json for target %s as it is in preview.', self.target)
|
|
712
690
|
return
|
|
713
691
|
|
|
714
692
|
try:
|
|
715
693
|
self._write_size_json()
|
|
716
694
|
except Exception as e:
|
|
717
|
-
|
|
695
|
+
LOGGER.warning('Failed to generate size json: %s', e)
|
|
718
696
|
|
|
719
697
|
def to_json(self) -> str:
|
|
720
698
|
return self.model_dump_json()
|
|
@@ -969,7 +947,7 @@ class CMakeApp(App):
|
|
|
969
947
|
check=True,
|
|
970
948
|
additional_env_dict=additional_env_dict,
|
|
971
949
|
)
|
|
972
|
-
|
|
950
|
+
LOGGER.debug('generated project_description.json to check app dependencies')
|
|
973
951
|
|
|
974
952
|
with open(os.path.join(self.build_path, PROJECT_DESCRIPTION_JSON)) as fr:
|
|
975
953
|
build_components = {item for item in json.load(fr)['build_components'] if item}
|
idf_build_apps/constants.py
CHANGED
|
@@ -31,17 +31,6 @@ class BuildStatus(str, enum.Enum):
|
|
|
31
31
|
SUCCESS = 'build success'
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
class BuildStage(str, enum.Enum):
|
|
35
|
-
DRY_RUN = 'Dry Run'
|
|
36
|
-
PRE_BUILD = 'Pre Build'
|
|
37
|
-
BUILD = 'Build'
|
|
38
|
-
POST_BUILD = 'Post Build'
|
|
39
|
-
|
|
40
|
-
@classmethod
|
|
41
|
-
def max_length(cls) -> int:
|
|
42
|
-
return max(len(v.value) for v in cls.__members__.values())
|
|
43
|
-
|
|
44
|
-
|
|
45
34
|
completion_instructions = """
|
|
46
35
|
With the `--activate` option, detect your shell type and add the appropriate commands to your shell's config file
|
|
47
36
|
so that it runs on startup. You will likely have to restart.
|
idf_build_apps/junit/report.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2023-
|
|
1
|
+
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
"""
|
|
@@ -211,4 +211,4 @@ class TestReport:
|
|
|
211
211
|
xml.append(test_suite.to_xml_elem())
|
|
212
212
|
|
|
213
213
|
ElementTree.ElementTree(xml).write(self.filepath, encoding='utf-8')
|
|
214
|
-
LOGGER.info('
|
|
214
|
+
LOGGER.info('Generated build junit report at: %s', self.filepath)
|
idf_build_apps/log.py
CHANGED
|
@@ -1,61 +1,92 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2023-
|
|
1
|
+
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
|
2
2
|
# SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
|
|
4
4
|
import logging
|
|
5
|
-
import sys
|
|
6
5
|
import typing as t
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
self.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
|
|
8
|
+
from rich import get_console
|
|
9
|
+
from rich._log_render import LogRender
|
|
10
|
+
from rich.console import Console, ConsoleRenderable
|
|
11
|
+
from rich.containers import Renderables
|
|
12
|
+
from rich.logging import RichHandler
|
|
13
|
+
from rich.text import Text, TextType
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _OneLineLogRender(LogRender):
|
|
17
|
+
def __call__( # type: ignore # the original method returns Table instead of Text
|
|
18
|
+
self,
|
|
19
|
+
console: Console,
|
|
20
|
+
renderables: t.Iterable[ConsoleRenderable],
|
|
21
|
+
log_time: t.Optional[datetime] = None,
|
|
22
|
+
time_format: t.Optional[t.Union[str, t.Callable[[datetime], Text]]] = None,
|
|
23
|
+
level: TextType = '',
|
|
24
|
+
path: t.Optional[str] = None,
|
|
25
|
+
line_no: t.Optional[int] = None,
|
|
26
|
+
link_path: t.Optional[str] = None,
|
|
27
|
+
) -> Text:
|
|
28
|
+
output = Text(no_wrap=True)
|
|
29
|
+
if self.show_time:
|
|
30
|
+
log_time = log_time or console.get_datetime()
|
|
31
|
+
time_format = time_format or self.time_format
|
|
32
|
+
if callable(time_format):
|
|
33
|
+
log_time_display = time_format(log_time)
|
|
34
|
+
else:
|
|
35
|
+
log_time_display = Text(log_time.strftime(time_format), style='log.time')
|
|
36
|
+
if log_time_display == self._last_time and self.omit_repeated_times:
|
|
37
|
+
output.append(' ' * len(log_time_display), style='log.time')
|
|
38
|
+
else:
|
|
39
|
+
output.append(log_time_display)
|
|
40
|
+
self._last_time = log_time_display
|
|
41
|
+
output.pad_right(1)
|
|
42
|
+
|
|
43
|
+
if self.show_level:
|
|
44
|
+
output.append(level)
|
|
45
|
+
if self.level_width:
|
|
46
|
+
output.pad_right(max(1, self.level_width - len(level)))
|
|
47
|
+
else:
|
|
48
|
+
output.pad_right(1)
|
|
49
|
+
|
|
50
|
+
for renderable in Renderables(renderables): # type: ignore
|
|
51
|
+
if isinstance(renderable, Text):
|
|
52
|
+
renderable.stylize('log.message')
|
|
53
|
+
|
|
54
|
+
output.append(renderable)
|
|
55
|
+
output.pad_right(1)
|
|
56
|
+
|
|
57
|
+
if self.show_path and path:
|
|
58
|
+
path_text = Text(style='log.path')
|
|
59
|
+
path_text.append(path, style=f'link file://{link_path}' if link_path else '')
|
|
60
|
+
if line_no:
|
|
61
|
+
path_text.append(':')
|
|
62
|
+
path_text.append(
|
|
63
|
+
f'{line_no}',
|
|
64
|
+
style=f'link file://{link_path}#{line_no}' if link_path else '',
|
|
65
|
+
)
|
|
66
|
+
output.append(path_text)
|
|
67
|
+
output.pad_right(1)
|
|
68
|
+
|
|
69
|
+
output.rstrip()
|
|
70
|
+
return output
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_rich_log_handler(level: int = logging.WARNING, no_color: bool = False) -> RichHandler:
|
|
74
|
+
console = get_console()
|
|
75
|
+
console.soft_wrap = True
|
|
76
|
+
console.no_color = no_color
|
|
77
|
+
console.stderr = True
|
|
78
|
+
|
|
79
|
+
handler = RichHandler(
|
|
80
|
+
level,
|
|
81
|
+
console,
|
|
82
|
+
)
|
|
83
|
+
handler._log_render = _OneLineLogRender(
|
|
84
|
+
show_level=True,
|
|
85
|
+
show_path=False,
|
|
86
|
+
omit_repeated_times=False,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
return handler
|
|
59
90
|
|
|
60
91
|
|
|
61
92
|
def setup_logging(verbose: int = 0, log_file: t.Optional[str] = None, colored: bool = True) -> None:
|
|
@@ -76,13 +107,13 @@ def setup_logging(verbose: int = 0, log_file: t.Optional[str] = None, colored: b
|
|
|
76
107
|
|
|
77
108
|
package_logger = logging.getLogger(__package__)
|
|
78
109
|
package_logger.setLevel(level)
|
|
110
|
+
|
|
79
111
|
if log_file:
|
|
80
112
|
handler: logging.Handler = logging.FileHandler(log_file)
|
|
81
113
|
else:
|
|
82
|
-
handler =
|
|
83
|
-
handler.setFormatter(ColoredFormatter(colored))
|
|
84
|
-
|
|
114
|
+
handler = get_rich_log_handler(level, not colored)
|
|
85
115
|
if package_logger.hasHandlers():
|
|
86
116
|
package_logger.handlers.clear()
|
|
87
117
|
package_logger.addHandler(handler)
|
|
118
|
+
|
|
88
119
|
package_logger.propagate = False # don't propagate to root logger
|
idf_build_apps/main.py
CHANGED
|
@@ -10,6 +10,7 @@ import os
|
|
|
10
10
|
import sys
|
|
11
11
|
import textwrap
|
|
12
12
|
import typing as t
|
|
13
|
+
from pathlib import Path
|
|
13
14
|
|
|
14
15
|
import argcomplete
|
|
15
16
|
from pydantic import (
|
|
@@ -75,7 +76,7 @@ def find_apps(
|
|
|
75
76
|
## `preserve`
|
|
76
77
|
if 'preserve' in kwargs:
|
|
77
78
|
LOGGER.warning(
|
|
78
|
-
'Passing "preserve" directly is deprecated. '
|
|
79
|
+
'DEPRECATED: Passing "preserve" directly is deprecated. '
|
|
79
80
|
'Pass "no_preserve" instead to disable preserving the build directory'
|
|
80
81
|
)
|
|
81
82
|
kwargs['no_preserve'] = not kwargs.pop('preserve')
|
|
@@ -102,11 +103,14 @@ def find_apps(
|
|
|
102
103
|
apps: t.Set[App] = set()
|
|
103
104
|
if find_arguments.target == 'all':
|
|
104
105
|
targets = ALL_TARGETS
|
|
106
|
+
LOGGER.info('Searching for apps by all targets')
|
|
105
107
|
else:
|
|
106
108
|
targets = [find_arguments.target]
|
|
109
|
+
LOGGER.info('Searching for apps by target: %s', find_arguments.target)
|
|
107
110
|
|
|
108
111
|
for _t in targets:
|
|
109
112
|
for _p in find_arguments.paths:
|
|
113
|
+
LOGGER.debug('Searching for apps in path %s for target %s', _p, _t)
|
|
110
114
|
apps.update(
|
|
111
115
|
_find_apps(
|
|
112
116
|
_p,
|
|
@@ -116,7 +120,7 @@ def find_apps(
|
|
|
116
120
|
)
|
|
117
121
|
)
|
|
118
122
|
|
|
119
|
-
LOGGER.info(
|
|
123
|
+
LOGGER.info('Found %d apps in total', len(apps))
|
|
120
124
|
|
|
121
125
|
return sorted(apps)
|
|
122
126
|
|
|
@@ -140,7 +144,7 @@ def build_apps(
|
|
|
140
144
|
## `check_app_dependencies`
|
|
141
145
|
if 'check_app_dependencies' in kwargs:
|
|
142
146
|
LOGGER.warning(
|
|
143
|
-
'Passing "check_app_dependencies" directly is deprecated. '
|
|
147
|
+
'DEPRECATED: Passing "check_app_dependencies" directly is deprecated. '
|
|
144
148
|
'Pass "modified_components" instead to enable dependency-driven build feature'
|
|
145
149
|
)
|
|
146
150
|
kwargs.pop('check_app_dependencies')
|
|
@@ -161,15 +165,24 @@ def build_apps(
|
|
|
161
165
|
test_suite = TestSuite('build_apps')
|
|
162
166
|
|
|
163
167
|
start, stop = get_parallel_start_stop(len(apps), build_arguments.parallel_count, build_arguments.parallel_index)
|
|
164
|
-
LOGGER.info('
|
|
168
|
+
LOGGER.info('Processing %d total apps: building apps %d-%d', len(apps), start, stop)
|
|
165
169
|
|
|
166
170
|
# cleanup collect files if exists at this early-stage
|
|
167
171
|
for f in (build_arguments.collect_app_info, build_arguments.collect_size_info, build_arguments.junitxml):
|
|
168
172
|
if f and os.path.isfile(f):
|
|
173
|
+
LOGGER.debug('Removing existing collect file: %s', f)
|
|
169
174
|
os.remove(f)
|
|
170
|
-
LOGGER.debug('Remove existing collect file %s', f)
|
|
171
175
|
|
|
172
176
|
exit_code = 0
|
|
177
|
+
|
|
178
|
+
# create empty files, avoid no file when no app is built
|
|
179
|
+
if build_arguments.collect_app_info:
|
|
180
|
+
LOGGER.debug('Creating empty app info file: %s', build_arguments.collect_app_info)
|
|
181
|
+
Path(build_arguments.collect_app_info).touch()
|
|
182
|
+
if build_arguments.collect_size_info:
|
|
183
|
+
LOGGER.debug('Creating empty size info file: %s', build_arguments.collect_size_info)
|
|
184
|
+
Path(build_arguments.collect_size_info).touch()
|
|
185
|
+
|
|
173
186
|
for i, app in enumerate(apps):
|
|
174
187
|
index = i + 1 # we use 1-based
|
|
175
188
|
if index < start or index > stop:
|
|
@@ -181,7 +194,7 @@ def build_apps(
|
|
|
181
194
|
app.verbose = build_arguments.build_verbose
|
|
182
195
|
app.copy_sdkconfig = build_arguments.copy_sdkconfig
|
|
183
196
|
|
|
184
|
-
LOGGER.info('(%
|
|
197
|
+
LOGGER.info('(%d/%d) Building app: %s', index, len(apps), app)
|
|
185
198
|
|
|
186
199
|
app.build(
|
|
187
200
|
manifest_rootpath=build_arguments.manifest_rootpath,
|
|
@@ -199,12 +212,14 @@ def build_apps(
|
|
|
199
212
|
if build_arguments.collect_app_info:
|
|
200
213
|
with open(build_arguments.collect_app_info, 'a') as fw:
|
|
201
214
|
fw.write(app.to_json() + '\n')
|
|
202
|
-
LOGGER.debug('Recorded app info in %s', build_arguments.collect_app_info)
|
|
215
|
+
LOGGER.debug('Recorded app info in file: %s', build_arguments.collect_app_info)
|
|
203
216
|
|
|
204
217
|
if app.build_status == BuildStatus.FAILED:
|
|
205
218
|
if not build_arguments.keep_going:
|
|
219
|
+
LOGGER.error('Build failed and keep_going=False, stopping build process')
|
|
206
220
|
return 1
|
|
207
221
|
else:
|
|
222
|
+
LOGGER.warning('Build failed but keep_going=True, continuing with next app')
|
|
208
223
|
exit_code = 1
|
|
209
224
|
elif app.build_status == BuildStatus.SUCCESS:
|
|
210
225
|
if build_arguments.collect_size_info and app.size_json_path:
|
|
@@ -221,13 +236,12 @@ def build_apps(
|
|
|
221
236
|
)
|
|
222
237
|
+ '\n'
|
|
223
238
|
)
|
|
224
|
-
LOGGER.debug('Recorded size info file path
|
|
239
|
+
LOGGER.debug('Recorded size info file path: %s', build_arguments.collect_size_info)
|
|
225
240
|
|
|
226
241
|
LOGGER.info('') # add one empty line for separating different builds
|
|
227
242
|
|
|
228
243
|
if build_arguments.junitxml:
|
|
229
244
|
TestReport([test_suite], build_arguments.junitxml).create_test_report()
|
|
230
|
-
LOGGER.info('Generated junit report for build apps: %s', build_arguments.junitxml)
|
|
231
245
|
|
|
232
246
|
return exit_code
|
|
233
247
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: idf-build-apps
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.0
|
|
4
4
|
Summary: Tools for building ESP-IDF related apps.
|
|
5
5
|
Author-email: Fu Hanxi <fuhanxi@espressif.com>
|
|
6
6
|
Requires-Python: >=3.7
|
|
@@ -22,6 +22,7 @@ Requires-Dist: pydantic_settings
|
|
|
22
22
|
Requires-Dist: argcomplete>=3
|
|
23
23
|
Requires-Dist: typing-extensions; python_version < '3.11'
|
|
24
24
|
Requires-Dist: esp-bool-parser>=0.1.2,<1
|
|
25
|
+
Requires-Dist: rich
|
|
25
26
|
Requires-Dist: sphinx ; extra == "doc"
|
|
26
27
|
Requires-Dist: sphinx-rtd-theme ; extra == "doc"
|
|
27
28
|
Requires-Dist: sphinx_copybutton ; extra == "doc"
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
idf_build_apps/__init__.py,sha256=
|
|
1
|
+
idf_build_apps/__init__.py,sha256=BfvfLYTWG1Y1Uy05w7PkCywbLswdIL5d-xh54mKeoGM,650
|
|
2
2
|
idf_build_apps/__main__.py,sha256=8E-5xHm2MlRun0L88XJleNh5U50dpE0Q1nK5KqomA7I,182
|
|
3
|
-
idf_build_apps/app.py,sha256=
|
|
3
|
+
idf_build_apps/app.py,sha256=DKofid9Z0DqoDL1DcsO05rVBtgSd5O9SoCDqKzl8iFQ,36674
|
|
4
4
|
idf_build_apps/args.py,sha256=rNOzetQYyfCfz9RQq_bzLXL6FrFpJllvLU2VooL_p9s,34178
|
|
5
5
|
idf_build_apps/autocompletions.py,sha256=g-bx0pzXoFKI0VQqftkHyGVWN6MLjuFOdozeuAf45yo,2138
|
|
6
|
-
idf_build_apps/constants.py,sha256=
|
|
6
|
+
idf_build_apps/constants.py,sha256=2iwLPZRhSQcn1v4RAcOJnHbqp1fDTp6A1gHaxn5ciTE,2166
|
|
7
7
|
idf_build_apps/finder.py,sha256=hY6uSMB2s65MqMKIDBSHABOfa93mOLT7x5hlMxC43EQ,5794
|
|
8
|
-
idf_build_apps/log.py,sha256=
|
|
9
|
-
idf_build_apps/main.py,sha256=
|
|
8
|
+
idf_build_apps/log.py,sha256=15sSQhv9dJsHShDR2KgFGFp8ByjV0HogLr1X1lHYqGs,3899
|
|
9
|
+
idf_build_apps/main.py,sha256=7CGwRBxi9j2Yqw7zYkfNbKXg_DSkLWkWMYvgiYjUFl4,17393
|
|
10
10
|
idf_build_apps/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
11
11
|
idf_build_apps/session_args.py,sha256=2WDTy40IFAc0KQ57HaeBcYj_k10eUXRKkDOWLrFCaHY,2985
|
|
12
12
|
idf_build_apps/utils.py,sha256=cQJ5N-53vrASa4d8WW0AQCPJzendArXyU3kB5Vx-AH8,10880
|
|
13
13
|
idf_build_apps/junit/__init__.py,sha256=IxvdaS6eSXp7kZxRuXqyZyGxuA_A1nOW1jF1HMi8Gns,231
|
|
14
|
-
idf_build_apps/junit/report.py,sha256=
|
|
14
|
+
idf_build_apps/junit/report.py,sha256=yzt5SiJEA_AUlw2Ld23J7enYlfDluvmKAcCnAM8ccqE,6565
|
|
15
15
|
idf_build_apps/junit/utils.py,sha256=NXZxQD4tdbSVKjKMNx1kO2H3IoEiysXkDoDjLEf1RO8,1303
|
|
16
16
|
idf_build_apps/manifest/__init__.py,sha256=LYGR9doEKGPEdsJPuHnmJmV-qw2kuAipV0bodkhY_JE,399
|
|
17
17
|
idf_build_apps/manifest/manifest.py,sha256=5BIzNzGAk0w5qOCTMGsmvKcN4DQLwUi6JVGt2G4JKQs,14793
|
|
@@ -20,8 +20,8 @@ idf_build_apps/vendors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
20
20
|
idf_build_apps/vendors/pydantic_sources.py,sha256=cxSIPRc3eI5peVMhDxwf58YaGhuG4SCwPRVX2znFEek,4553
|
|
21
21
|
idf_build_apps/yaml/__init__.py,sha256=W-3z5no07RQ6eYKGyOAPA8Z2CLiMPob8DD91I4URjrA,162
|
|
22
22
|
idf_build_apps/yaml/parser.py,sha256=b3LvogO6do-eJPRsYzT-8xk8AT2MnXpLCzQutJqyC7M,2128
|
|
23
|
-
idf_build_apps-2.
|
|
24
|
-
idf_build_apps-2.
|
|
25
|
-
idf_build_apps-2.
|
|
26
|
-
idf_build_apps-2.
|
|
27
|
-
idf_build_apps-2.
|
|
23
|
+
idf_build_apps-2.7.0.dist-info/entry_points.txt,sha256=3pVUirUEsb6jsDRikkQWNUt4hqLK2ci1HvW_Vf8b6uE,59
|
|
24
|
+
idf_build_apps-2.7.0.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
25
|
+
idf_build_apps-2.7.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
|
26
|
+
idf_build_apps-2.7.0.dist-info/METADATA,sha256=QGSjoUnmfFJk9l7bxO-QfOeIR-dKxgeZAsEihosMPpQ,4713
|
|
27
|
+
idf_build_apps-2.7.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|