idf-build-apps 2.6.4__tar.gz → 2.8.0__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.
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.github/workflows/test-build-docs.yml +1 -1
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.pre-commit-config.yaml +1 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/CHANGELOG.md +12 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/PKG-INFO +4 -2
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/__init__.py +1 -1
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/app.py +32 -54
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/args.py +17 -1
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/constants.py +0 -11
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/junit/report.py +2 -2
- idf_build_apps-2.8.0/idf_build_apps/log.py +119 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/main.py +15 -9
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/pyproject.toml +4 -2
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/setup.py +3 -2
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/tests/test_args.py +49 -1
- idf_build_apps-2.6.4/idf_build_apps/log.py +0 -88
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.editorconfig +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.git-blame-ignore-revs +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.gitattributes +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.github/dependabot.yml +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.github/workflows/check-pre-commit.yml +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.github/workflows/publish-pypi.yml +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.github/workflows/sync-jira.yml +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.github/workflows/test-build-idf-apps.yml +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.gitignore +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/.readthedocs.yml +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/CONTRIBUTING.md +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/LICENSE +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/README.md +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/_apidoc_templates/module.rst_t +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/_apidoc_templates/package.rst_t +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/_apidoc_templates/toc.rst_t +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/_static/espressif-logo.svg +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/_static/theme_overrides.css +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/_templates/layout.html +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/conf_common.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/Makefile +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/conf.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/explanations/build.rst +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/explanations/config_rules.rst +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/explanations/dependency_driven_build.rst +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/explanations/find.rst +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/guides/1.x_to_2.x.md +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/index.rst +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/others/CHANGELOG.md +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/others/CONTRIBUTING.md +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/references/cli.rst +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/references/config_file.rst +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/references/manifest.rst +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/__main__.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/autocompletions.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/finder.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/junit/__init__.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/junit/utils.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/manifest/__init__.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/manifest/manifest.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/manifest/soc_header.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/py.typed +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/session_args.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/utils.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/vendors/__init__.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/vendors/pydantic_sources.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/yaml/__init__.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/idf_build_apps/yaml/parser.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/license_header.txt +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/tests/conftest.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/tests/test_app.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/tests/test_build.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/tests/test_cmd.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/tests/test_finder.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/tests/test_manifest.py +0 -0
- {idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/tests/test_utils.py +0 -0
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## v2.8.0 (2025-02-20)
|
|
6
|
+
|
|
7
|
+
### Feat
|
|
8
|
+
|
|
9
|
+
- support '--disable-targets'
|
|
10
|
+
|
|
11
|
+
## v2.7.0 (2025-02-18)
|
|
12
|
+
|
|
13
|
+
### Feat
|
|
14
|
+
|
|
15
|
+
- improve debug info with rich
|
|
16
|
+
|
|
5
17
|
## v2.6.4 (2025-02-14)
|
|
6
18
|
|
|
7
19
|
### Fix
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: idf-build-apps
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.8.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
|
|
@@ -13,6 +13,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
License-File: LICENSE
|
|
16
17
|
Requires-Dist: pyparsing
|
|
17
18
|
Requires-Dist: pyyaml
|
|
18
19
|
Requires-Dist: packaging
|
|
@@ -22,6 +23,7 @@ Requires-Dist: pydantic_settings
|
|
|
22
23
|
Requires-Dist: argcomplete>=3
|
|
23
24
|
Requires-Dist: typing-extensions; python_version < '3.11'
|
|
24
25
|
Requires-Dist: esp-bool-parser>=0.1.2,<1
|
|
26
|
+
Requires-Dist: rich
|
|
25
27
|
Requires-Dist: sphinx ; extra == "doc"
|
|
26
28
|
Requires-Dist: sphinx-rtd-theme ; extra == "doc"
|
|
27
29
|
Requires-Dist: sphinx_copybutton ; extra == "doc"
|
|
@@ -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}
|
|
@@ -27,7 +27,7 @@ from pydantic_settings import (
|
|
|
27
27
|
from typing_extensions import Concatenate, ParamSpec
|
|
28
28
|
|
|
29
29
|
from . import SESSION_ARGS, App, setup_logging
|
|
30
|
-
from .constants import ALL_TARGETS, IDF_BUILD_APPS_TOML_FN
|
|
30
|
+
from .constants import ALL_TARGETS, IDF_BUILD_APPS_TOML_FN, SUPPORTED_TARGETS
|
|
31
31
|
from .manifest.manifest import FolderRule, Manifest
|
|
32
32
|
from .utils import InvalidCommand, files_matches_patterns, semicolon_separated_str_to_list, to_absolute_path, to_list
|
|
33
33
|
from .vendors.pydantic_sources import PyprojectTomlConfigSettingsSource, TomlConfigSettingsSource
|
|
@@ -500,6 +500,14 @@ class FindBuildArguments(DependencyDrivenBuildArguments):
|
|
|
500
500
|
'including the preview targets. As the targets defined in `idf.py --list-targets --preview`',
|
|
501
501
|
default=False, # type: ignore
|
|
502
502
|
)
|
|
503
|
+
disable_targets: t.Optional[t.List[str]] = field(
|
|
504
|
+
FieldMetadata(
|
|
505
|
+
validate_method=[ValidateMethod.TO_LIST],
|
|
506
|
+
nargs='+',
|
|
507
|
+
),
|
|
508
|
+
description='space-separated list of targets that should be disabled to all apps.',
|
|
509
|
+
default=None, # type: ignore
|
|
510
|
+
)
|
|
503
511
|
include_skipped_apps: bool = field(
|
|
504
512
|
FieldMetadata(
|
|
505
513
|
action='store_true',
|
|
@@ -549,6 +557,14 @@ class FindBuildArguments(DependencyDrivenBuildArguments):
|
|
|
549
557
|
self.default_build_targets = deepcopy(ALL_TARGETS)
|
|
550
558
|
LOGGER.info('Overriding default build targets to %s', self.default_build_targets)
|
|
551
559
|
FolderRule.DEFAULT_BUILD_TARGETS = self.default_build_targets
|
|
560
|
+
else:
|
|
561
|
+
# restore default build targets
|
|
562
|
+
FolderRule.DEFAULT_BUILD_TARGETS = SUPPORTED_TARGETS
|
|
563
|
+
|
|
564
|
+
if self.disable_targets and FolderRule.DEFAULT_BUILD_TARGETS:
|
|
565
|
+
LOGGER.info('Disable targets: %s', self.disable_targets)
|
|
566
|
+
self.default_build_targets = [t for t in FolderRule.DEFAULT_BUILD_TARGETS if t not in self.disable_targets]
|
|
567
|
+
FolderRule.DEFAULT_BUILD_TARGETS = self.default_build_targets
|
|
552
568
|
|
|
553
569
|
if self.override_sdkconfig_items or self.override_sdkconfig_items:
|
|
554
570
|
SESSION_ARGS.set(self)
|
|
@@ -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.
|
|
@@ -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)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
import typing as t
|
|
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
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def setup_logging(verbose: int = 0, log_file: t.Optional[str] = None, colored: bool = True) -> None:
|
|
93
|
+
"""
|
|
94
|
+
Setup logging stream handler
|
|
95
|
+
|
|
96
|
+
:param verbose: 0 - WARNING, 1 - INFO, 2 - DEBUG
|
|
97
|
+
:param log_file: log file path
|
|
98
|
+
:param colored: colored output or not
|
|
99
|
+
:return: None
|
|
100
|
+
"""
|
|
101
|
+
if not verbose:
|
|
102
|
+
level = logging.WARNING
|
|
103
|
+
elif verbose == 1:
|
|
104
|
+
level = logging.INFO
|
|
105
|
+
else:
|
|
106
|
+
level = logging.DEBUG
|
|
107
|
+
|
|
108
|
+
package_logger = logging.getLogger(__package__)
|
|
109
|
+
package_logger.setLevel(level)
|
|
110
|
+
|
|
111
|
+
if log_file:
|
|
112
|
+
handler: logging.Handler = logging.FileHandler(log_file)
|
|
113
|
+
else:
|
|
114
|
+
handler = get_rich_log_handler(level, not colored)
|
|
115
|
+
if package_logger.hasHandlers():
|
|
116
|
+
package_logger.handlers.clear()
|
|
117
|
+
package_logger.addHandler(handler)
|
|
118
|
+
|
|
119
|
+
package_logger.propagate = False # don't propagate to root logger
|
|
@@ -76,7 +76,7 @@ def find_apps(
|
|
|
76
76
|
## `preserve`
|
|
77
77
|
if 'preserve' in kwargs:
|
|
78
78
|
LOGGER.warning(
|
|
79
|
-
'Passing "preserve" directly is deprecated. '
|
|
79
|
+
'DEPRECATED: Passing "preserve" directly is deprecated. '
|
|
80
80
|
'Pass "no_preserve" instead to disable preserving the build directory'
|
|
81
81
|
)
|
|
82
82
|
kwargs['no_preserve'] = not kwargs.pop('preserve')
|
|
@@ -103,11 +103,14 @@ def find_apps(
|
|
|
103
103
|
apps: t.Set[App] = set()
|
|
104
104
|
if find_arguments.target == 'all':
|
|
105
105
|
targets = ALL_TARGETS
|
|
106
|
+
LOGGER.info('Searching for apps by all targets')
|
|
106
107
|
else:
|
|
107
108
|
targets = [find_arguments.target]
|
|
109
|
+
LOGGER.info('Searching for apps by target: %s', find_arguments.target)
|
|
108
110
|
|
|
109
111
|
for _t in targets:
|
|
110
112
|
for _p in find_arguments.paths:
|
|
113
|
+
LOGGER.debug('Searching for apps in path %s for target %s', _p, _t)
|
|
111
114
|
apps.update(
|
|
112
115
|
_find_apps(
|
|
113
116
|
_p,
|
|
@@ -117,7 +120,7 @@ def find_apps(
|
|
|
117
120
|
)
|
|
118
121
|
)
|
|
119
122
|
|
|
120
|
-
LOGGER.info(
|
|
123
|
+
LOGGER.info('Found %d apps in total', len(apps))
|
|
121
124
|
|
|
122
125
|
return sorted(apps)
|
|
123
126
|
|
|
@@ -141,7 +144,7 @@ def build_apps(
|
|
|
141
144
|
## `check_app_dependencies`
|
|
142
145
|
if 'check_app_dependencies' in kwargs:
|
|
143
146
|
LOGGER.warning(
|
|
144
|
-
'Passing "check_app_dependencies" directly is deprecated. '
|
|
147
|
+
'DEPRECATED: Passing "check_app_dependencies" directly is deprecated. '
|
|
145
148
|
'Pass "modified_components" instead to enable dependency-driven build feature'
|
|
146
149
|
)
|
|
147
150
|
kwargs.pop('check_app_dependencies')
|
|
@@ -162,20 +165,22 @@ def build_apps(
|
|
|
162
165
|
test_suite = TestSuite('build_apps')
|
|
163
166
|
|
|
164
167
|
start, stop = get_parallel_start_stop(len(apps), build_arguments.parallel_count, build_arguments.parallel_index)
|
|
165
|
-
LOGGER.info('
|
|
168
|
+
LOGGER.info('Processing %d total apps: building apps %d-%d', len(apps), start, stop)
|
|
166
169
|
|
|
167
170
|
# cleanup collect files if exists at this early-stage
|
|
168
171
|
for f in (build_arguments.collect_app_info, build_arguments.collect_size_info, build_arguments.junitxml):
|
|
169
172
|
if f and os.path.isfile(f):
|
|
173
|
+
LOGGER.debug('Removing existing collect file: %s', f)
|
|
170
174
|
os.remove(f)
|
|
171
|
-
LOGGER.debug('Remove existing collect file %s', f)
|
|
172
175
|
|
|
173
176
|
exit_code = 0
|
|
174
177
|
|
|
175
178
|
# create empty files, avoid no file when no app is built
|
|
176
179
|
if build_arguments.collect_app_info:
|
|
180
|
+
LOGGER.debug('Creating empty app info file: %s', build_arguments.collect_app_info)
|
|
177
181
|
Path(build_arguments.collect_app_info).touch()
|
|
178
182
|
if build_arguments.collect_size_info:
|
|
183
|
+
LOGGER.debug('Creating empty size info file: %s', build_arguments.collect_size_info)
|
|
179
184
|
Path(build_arguments.collect_size_info).touch()
|
|
180
185
|
|
|
181
186
|
for i, app in enumerate(apps):
|
|
@@ -189,7 +194,7 @@ def build_apps(
|
|
|
189
194
|
app.verbose = build_arguments.build_verbose
|
|
190
195
|
app.copy_sdkconfig = build_arguments.copy_sdkconfig
|
|
191
196
|
|
|
192
|
-
LOGGER.info('(%
|
|
197
|
+
LOGGER.info('(%d/%d) Building app: %s', index, len(apps), app)
|
|
193
198
|
|
|
194
199
|
app.build(
|
|
195
200
|
manifest_rootpath=build_arguments.manifest_rootpath,
|
|
@@ -207,12 +212,14 @@ def build_apps(
|
|
|
207
212
|
if build_arguments.collect_app_info:
|
|
208
213
|
with open(build_arguments.collect_app_info, 'a') as fw:
|
|
209
214
|
fw.write(app.to_json() + '\n')
|
|
210
|
-
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)
|
|
211
216
|
|
|
212
217
|
if app.build_status == BuildStatus.FAILED:
|
|
213
218
|
if not build_arguments.keep_going:
|
|
219
|
+
LOGGER.error('Build failed and keep_going=False, stopping build process')
|
|
214
220
|
return 1
|
|
215
221
|
else:
|
|
222
|
+
LOGGER.warning('Build failed but keep_going=True, continuing with next app')
|
|
216
223
|
exit_code = 1
|
|
217
224
|
elif app.build_status == BuildStatus.SUCCESS:
|
|
218
225
|
if build_arguments.collect_size_info and app.size_json_path:
|
|
@@ -229,13 +236,12 @@ def build_apps(
|
|
|
229
236
|
)
|
|
230
237
|
+ '\n'
|
|
231
238
|
)
|
|
232
|
-
LOGGER.debug('Recorded size info file path
|
|
239
|
+
LOGGER.debug('Recorded size info file path: %s', build_arguments.collect_size_info)
|
|
233
240
|
|
|
234
241
|
LOGGER.info('') # add one empty line for separating different builds
|
|
235
242
|
|
|
236
243
|
if build_arguments.junitxml:
|
|
237
244
|
TestReport([test_suite], build_arguments.junitxml).create_test_report()
|
|
238
|
-
LOGGER.info('Generated junit report for build apps: %s', build_arguments.junitxml)
|
|
239
245
|
|
|
240
246
|
return exit_code
|
|
241
247
|
|
|
@@ -31,7 +31,9 @@ dependencies = [
|
|
|
31
31
|
"pydantic_settings",
|
|
32
32
|
"argcomplete>=3",
|
|
33
33
|
"typing-extensions; python_version < '3.11'",
|
|
34
|
-
"esp-bool-parser>=0.1.2,<1"
|
|
34
|
+
"esp-bool-parser>=0.1.2,<1",
|
|
35
|
+
# debug/print
|
|
36
|
+
"rich",
|
|
35
37
|
]
|
|
36
38
|
|
|
37
39
|
[project.optional-dependencies]
|
|
@@ -62,7 +64,7 @@ idf-build-apps = "idf_build_apps:main.main"
|
|
|
62
64
|
|
|
63
65
|
[tool.commitizen]
|
|
64
66
|
name = "cz_conventional_commits"
|
|
65
|
-
version = "2.
|
|
67
|
+
version = "2.8.0"
|
|
66
68
|
tag_format = "v$version"
|
|
67
69
|
version_files = [
|
|
68
70
|
"idf_build_apps/__init__.py",
|
|
@@ -20,7 +20,8 @@ install_requires = \
|
|
|
20
20
|
'pydantic~=2.0',
|
|
21
21
|
'pydantic_settings',
|
|
22
22
|
'argcomplete>=3',
|
|
23
|
-
'esp-bool-parser>=0.1.2,<1'
|
|
23
|
+
'esp-bool-parser>=0.1.2,<1',
|
|
24
|
+
'rich']
|
|
24
25
|
|
|
25
26
|
extras_require = \
|
|
26
27
|
{":python_version < '3.11'": ['toml', 'typing-extensions'],
|
|
@@ -37,7 +38,7 @@ entry_points = \
|
|
|
37
38
|
{'console_scripts': ['idf-build-apps = idf_build_apps:main.main']}
|
|
38
39
|
|
|
39
40
|
setup(name='idf-build-apps',
|
|
40
|
-
version='2.
|
|
41
|
+
version='2.8.0',
|
|
41
42
|
description='Tools for building ESP-IDF related apps.',
|
|
42
43
|
author=None,
|
|
43
44
|
author_email='Fu Hanxi <fuhanxi@espressif.com>',
|
|
@@ -16,7 +16,7 @@ from idf_build_apps.args import (
|
|
|
16
16
|
FindArguments,
|
|
17
17
|
FindBuildArguments,
|
|
18
18
|
)
|
|
19
|
-
from idf_build_apps.constants import IDF_BUILD_APPS_TOML_FN
|
|
19
|
+
from idf_build_apps.constants import IDF_BUILD_APPS_TOML_FN, PREVIEW_TARGETS, SUPPORTED_TARGETS
|
|
20
20
|
from idf_build_apps.main import main
|
|
21
21
|
|
|
22
22
|
|
|
@@ -161,6 +161,54 @@ modified_files = [
|
|
|
161
161
|
assert args.deactivate_dependency_driven_build_by_components == ['baz']
|
|
162
162
|
|
|
163
163
|
|
|
164
|
+
def test_build_targets_cli(tmp_path, monkeypatch):
|
|
165
|
+
create_project('foo', tmp_path)
|
|
166
|
+
with open(IDF_BUILD_APPS_TOML_FN, 'w') as fw:
|
|
167
|
+
fw.write("""paths = ["foo"]
|
|
168
|
+
build_dir = "build_@t"
|
|
169
|
+
junitxml = "test.xml"
|
|
170
|
+
dry_run = true
|
|
171
|
+
""")
|
|
172
|
+
|
|
173
|
+
def get_enabled_targets(args):
|
|
174
|
+
with monkeypatch.context() as m:
|
|
175
|
+
m.setattr('sys.argv', args)
|
|
176
|
+
main()
|
|
177
|
+
with open('test.xml') as f:
|
|
178
|
+
xml = ElementTree.fromstring(f.read())
|
|
179
|
+
test_suite = xml.findall('testsuite')[0]
|
|
180
|
+
testcases = test_suite.findall('testcase')
|
|
181
|
+
# get targets
|
|
182
|
+
return [c.attrib['name'].replace('foo/build_', '') for c in testcases]
|
|
183
|
+
|
|
184
|
+
# default build SUPPORTED_TARGETS
|
|
185
|
+
targets = get_enabled_targets(['idf-build-apps', 'build'])
|
|
186
|
+
assert len(targets) == len(SUPPORTED_TARGETS)
|
|
187
|
+
assert set(targets) == set(SUPPORTED_TARGETS)
|
|
188
|
+
# build with --target
|
|
189
|
+
targets = get_enabled_targets(['idf-build-apps', 'build', '--target', 'esp32'])
|
|
190
|
+
assert len(targets) == 1
|
|
191
|
+
assert targets[0] == 'esp32'
|
|
192
|
+
# build with --default-build-targets
|
|
193
|
+
targets = get_enabled_targets(['idf-build-apps', 'build', '--default-build-targets', 'esp32', 'esp32s2'])
|
|
194
|
+
assert len(targets) == 2
|
|
195
|
+
assert set(targets) == {'esp32', 'esp32s2'}
|
|
196
|
+
# build with --enable-preview-targets
|
|
197
|
+
targets = get_enabled_targets(['idf-build-apps', 'build', '--enable-preview-targets'])
|
|
198
|
+
assert len(targets) == len(SUPPORTED_TARGETS) + len(PREVIEW_TARGETS)
|
|
199
|
+
assert set(targets) == set(SUPPORTED_TARGETS) | set(PREVIEW_TARGETS)
|
|
200
|
+
# build with --disable-targets
|
|
201
|
+
assert 'esp32' in SUPPORTED_TARGETS and 'esp32s2' in SUPPORTED_TARGETS
|
|
202
|
+
targets = get_enabled_targets(['idf-build-apps', 'build', '--disable-targets', 'esp32', 'esp32s2'])
|
|
203
|
+
assert set(targets) == set(SUPPORTED_TARGETS) - {'esp32', 'esp32s2'}
|
|
204
|
+
# build with --enable-preview-targets and --disable-targets
|
|
205
|
+
targets = get_enabled_targets(
|
|
206
|
+
['idf-build-apps', 'build', '--enable-preview-targets', '--disable-targets', PREVIEW_TARGETS[0]]
|
|
207
|
+
)
|
|
208
|
+
assert len(targets) == len(SUPPORTED_TARGETS) + len(PREVIEW_TARGETS) - 1
|
|
209
|
+
assert set(targets) == set(SUPPORTED_TARGETS) | set(PREVIEW_TARGETS) - {PREVIEW_TARGETS[0]}
|
|
210
|
+
|
|
211
|
+
|
|
164
212
|
class TestIgnoreWarningFile:
|
|
165
213
|
def test_deprecated_cli(self, monkeypatch, capsys):
|
|
166
214
|
with open('foo.txt', 'w') as fw:
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
|
2
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
-
|
|
4
|
-
import logging
|
|
5
|
-
import sys
|
|
6
|
-
import typing as t
|
|
7
|
-
|
|
8
|
-
from .constants import (
|
|
9
|
-
BuildStage,
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class ColoredFormatter(logging.Formatter):
|
|
14
|
-
grey: str = '\x1b[37;20m'
|
|
15
|
-
yellow: str = '\x1b[33;20m'
|
|
16
|
-
red: str = '\x1b[31;20m'
|
|
17
|
-
bold_red: str = '\x1b[31;1m'
|
|
18
|
-
|
|
19
|
-
reset: str = '\x1b[0m'
|
|
20
|
-
|
|
21
|
-
fmt: str = '%(asctime)s %(levelname)8s %(message)s'
|
|
22
|
-
app_fmt: str = f'%(asctime)s %(levelname)8s [%(build_stage){BuildStage.max_length()}s] %(message)s'
|
|
23
|
-
|
|
24
|
-
datefmt: str = '%Y-%m-%d %H:%M:%S'
|
|
25
|
-
|
|
26
|
-
FORMATS: t.Dict[int, str] = {
|
|
27
|
-
logging.DEBUG: f'{grey}{{}}{reset}',
|
|
28
|
-
logging.INFO: '{}',
|
|
29
|
-
logging.WARNING: f'{yellow}{{}}{reset}',
|
|
30
|
-
logging.ERROR: f'{red}{{}}{reset}',
|
|
31
|
-
logging.CRITICAL: f'{bold_red}{{}}{reset}',
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
def __init__(self, colored: bool = True) -> None:
|
|
35
|
-
self.colored = colored
|
|
36
|
-
if sys.platform == 'win32': # does not support it
|
|
37
|
-
self.colored = False
|
|
38
|
-
|
|
39
|
-
super().__init__(datefmt=self.datefmt)
|
|
40
|
-
|
|
41
|
-
def format(self, record: logging.LogRecord) -> str:
|
|
42
|
-
if getattr(record, 'build_stage', None):
|
|
43
|
-
base_fmt = self.app_fmt
|
|
44
|
-
else:
|
|
45
|
-
base_fmt = self.fmt
|
|
46
|
-
|
|
47
|
-
if self.colored:
|
|
48
|
-
log_fmt = self.FORMATS[record.levelno].format(base_fmt)
|
|
49
|
-
else:
|
|
50
|
-
log_fmt = base_fmt
|
|
51
|
-
|
|
52
|
-
if record.levelno in [logging.WARNING, logging.ERROR]:
|
|
53
|
-
record.msg = '>>> ' + str(record.msg)
|
|
54
|
-
elif record.levelno == logging.CRITICAL:
|
|
55
|
-
record.msg = '!!! ' + str(record.msg)
|
|
56
|
-
|
|
57
|
-
formatter = logging.Formatter(log_fmt, datefmt=self.datefmt)
|
|
58
|
-
return formatter.format(record)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def setup_logging(verbose: int = 0, log_file: t.Optional[str] = None, colored: bool = True) -> None:
|
|
62
|
-
"""
|
|
63
|
-
Setup logging stream handler
|
|
64
|
-
|
|
65
|
-
:param verbose: 0 - WARNING, 1 - INFO, 2 - DEBUG
|
|
66
|
-
:param log_file: log file path
|
|
67
|
-
:param colored: colored output or not
|
|
68
|
-
:return: None
|
|
69
|
-
"""
|
|
70
|
-
if not verbose:
|
|
71
|
-
level = logging.WARNING
|
|
72
|
-
elif verbose == 1:
|
|
73
|
-
level = logging.INFO
|
|
74
|
-
else:
|
|
75
|
-
level = logging.DEBUG
|
|
76
|
-
|
|
77
|
-
package_logger = logging.getLogger(__package__)
|
|
78
|
-
package_logger.setLevel(level)
|
|
79
|
-
if log_file:
|
|
80
|
-
handler: logging.Handler = logging.FileHandler(log_file)
|
|
81
|
-
else:
|
|
82
|
-
handler = logging.StreamHandler(sys.stderr)
|
|
83
|
-
handler.setFormatter(ColoredFormatter(colored))
|
|
84
|
-
|
|
85
|
-
if package_logger.hasHandlers():
|
|
86
|
-
package_logger.handlers.clear()
|
|
87
|
-
package_logger.addHandler(handler)
|
|
88
|
-
package_logger.propagate = False # don't propagate to root logger
|
|
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
|
{idf_build_apps-2.6.4 → idf_build_apps-2.8.0}/docs/en/explanations/dependency_driven_build.rst
RENAMED
|
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
|