Skip to content

Commit 8878050

Browse files
committed
Merge branch 'fix/kconfig_retain_local_component_list_file_during_second_cmake_run' into 'main'
fix(kconfig): transitive dependencies incorrect source on second CMake run Closes PACMAN-1190 See merge request espressif/idf-component-manager!549
2 parents 7b049fa + db98449 commit 8878050

File tree

9 files changed

+174
-12
lines changed

9 files changed

+174
-12
lines changed

idf_component_manager/local_component_list.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@ class LocalComponentList(BaseModel):
2020
components: t.List[LocalComponent]
2121

2222

23-
def parse_component_list(path: str) -> t.List[t.Dict[str, str]]:
24-
with open(path, encoding='utf-8') as f:
25-
try:
26-
components = LocalComponentList.fromdict(YAML(typ='safe').load(f))
27-
return [c for c in components.components] # type: ignore
28-
except (YAMLError, ValidationError):
29-
raise ProcessingError('Cannot parse components list file.')
23+
def parse_component_list(path: str) -> t.List[LocalComponent]:
24+
"""
25+
Parse component list from YAML file.
26+
"""
27+
28+
try:
29+
with open(path, encoding='utf-8') as f:
30+
data = YAML(typ='safe').load(f)
31+
components = LocalComponentList.fromdict(data)
32+
except (YAMLError, ValidationError):
33+
raise ProcessingError('Cannot parse components list file.')
34+
35+
return components.components

idf_component_manager/prepare_components/prepare.py

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88

99
import argparse
1010
import os
11+
import shutil
1112
import sys
1213
import typing as t
14+
from pathlib import Path
1315

1416
from idf_component_manager.core import ComponentManager
1517
from idf_component_tools import error, setup_logging, warn
@@ -23,19 +25,50 @@ def _component_list_file(build_dir):
2325
return os.path.join(build_dir, 'components_with_manifests_list.temp')
2426

2527

28+
def _get_ppid_file_path(local_component_list_file: t.Optional[str]) -> Path:
29+
return Path(f'{local_component_list_file}.{os.getppid()}')
30+
31+
32+
def _get_component_list_file(local_components_list_file):
33+
"""
34+
Get the appropriate component list file, preferring the PPID version
35+
if it exists from a parent CMake run.
36+
37+
Args:
38+
args: Command line arguments containing local_components_list_file
39+
40+
Returns:
41+
Path to the component list file to use, or None if not configured
42+
"""
43+
if not local_components_list_file:
44+
return None
45+
46+
component_list_parent_pid = _get_ppid_file_path(local_components_list_file)
47+
# Always use local component list file from the first execution of the component manager
48+
# (component_list_parent_pid) during one CMake run,
49+
# to be sure that it doesn't contain components introduced by the component manager.
50+
if component_list_parent_pid.exists():
51+
return component_list_parent_pid
52+
else:
53+
return local_components_list_file
54+
55+
2656
def prepare_dep_dirs(args):
2757
if args.sdkconfig_json_file:
2858
KCONFIG_CONTEXT.get().update_from_file(args.sdkconfig_json_file)
2959

3060
build_dir = args.build_dir or os.path.dirname(args.managed_components_list_file)
61+
62+
local_components_list_file = _get_component_list_file(args.local_components_list_file)
63+
3164
ComponentManager(
3265
args.project_dir,
3366
lock_path=args.lock_path,
3467
interface_version=args.interface_version,
3568
).prepare_dep_dirs(
3669
managed_components_list_file=args.managed_components_list_file,
3770
component_list_file=_component_list_file(build_dir),
38-
local_components_list_file=args.local_components_list_file,
71+
local_components_list_file=local_components_list_file,
3972
)
4073

4174
kconfig_ctx = KCONFIG_CONTEXT.get()
@@ -67,7 +100,26 @@ def debug_message(req: ComponentRequirement) -> str:
67100
f'but not found in any Kconfig file:\n'
68101
f'{_nl.join(sorted(debug_strs))}\n'
69102
)
70-
exit(10)
103+
104+
# Copy local component list file for next run of CMake before exiting
105+
if args.local_components_list_file:
106+
ppid_file = _get_ppid_file_path(args.local_components_list_file)
107+
108+
if not Path(ppid_file).exists():
109+
try:
110+
shutil.copyfile(args.local_components_list_file, ppid_file)
111+
except (OSError, IOError) as e:
112+
raise FatalError(
113+
f"Failed to copy '{args.local_components_list_file}' → '{ppid_file}': {e}"
114+
) from e
115+
# Exiting with code 10 to signal CMake to re-run component discovery due to missing KConfig options
116+
sys.exit(10)
117+
118+
# Clean up PPID file on successful completion
119+
if args.local_components_list_file:
120+
ppid_file_path = Path(_get_ppid_file_path(args.local_components_list_file))
121+
if ppid_file_path.exists():
122+
ppid_file_path.unlink()
71123

72124

73125
def inject_requirements(args):

integration_tests/test_integration.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,15 +119,15 @@ def test_set_component_version(project):
119119
'main': {
120120
'dependencies': {
121121
'idf': {
122-
'version': '^6.1',
122+
'version': '^4.9',
123123
}
124124
}
125125
},
126126
'component_foo': {
127127
'version': '1.0.0',
128128
'dependencies': {
129129
'idf': {
130-
'version': '^6.1',
130+
'version': '^4.9',
131131
}
132132
},
133133
},
@@ -138,7 +138,7 @@ def test_set_component_version(project):
138138
)
139139
def test_root_dep_failed(project):
140140
res = project_action(project, 'reconfigure')
141-
assert "Result: Because project depends on idf (^6.1) which doesn't match any" in res
141+
assert "Result: Because project depends on idf (^4.9) which doesn't match any" in res
142142
assert 'versions, version solving failed.' in res
143143
assert 'Please check manifest file of the following component(s): main,' in res
144144
assert 'component_foo' in res
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
2+
# SPDX-License-Identifier: Apache-2.0
3+
import os
4+
from pathlib import Path
5+
6+
import pytest
7+
from ruamel.yaml import YAML
8+
9+
from idf_component_tools.semver.base import Version
10+
from integration_tests.integration_test_helpers import fixtures_path, project_action
11+
12+
idf_version = Version.coerce(os.getenv('ESP_IDF_VERSION'))
13+
14+
15+
@pytest.mark.skipif(
16+
idf_version < Version.coerce('5.3'),
17+
reason='KConfig variables in the manifest are not supported in ESP-IDF < 5.3',
18+
)
19+
@pytest.mark.parametrize(
20+
'project',
21+
[
22+
{
23+
'components': {
24+
'main': {
25+
'dependencies': {
26+
'cmp': {
27+
'override_path': fixtures_path(
28+
'components', 'cmp_with_kconfig_var', 'cmp'
29+
),
30+
},
31+
},
32+
},
33+
},
34+
},
35+
],
36+
indirect=True,
37+
)
38+
def test_prepare_dep_dirs_with_kconfig(project):
39+
# Create a mock sdkconfig file
40+
(Path(project) / 'sdkconfig.default').write_text('CONFIG_ESP_BOARD_DEV_AUDIO_CODEC_SUPPORT=y')
41+
42+
project_action(
43+
project,
44+
'reconfigure',
45+
)
46+
47+
# Verify that valid Kconfig options are resolved correctly
48+
lock = YAML().load(Path(project) / 'dependencies.lock')
49+
assert 'cmp' in lock['dependencies']
50+
assert 'espressif/esp_codec_dev' in lock['dependencies']
51+
assert (
52+
'$CONFIG{ESP_BOARD_DEV_AUDIO_CODEC_SUPPORT} == True'
53+
in lock['dependencies']['cmp']['dependencies'][0]['matches'][0]['if']
54+
)
55+
assert 'service' in lock['dependencies']['espressif/esp_codec_dev']['source']['type']
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
menu "Device Support"
2+
3+
config ESP_BOARD_DEV_AUDIO_CODEC_SUPPORT
4+
bool "Device 'audio_codec' support"
5+
default y
6+
help
7+
Enable audio_codec device support.
8+
This option enables the audio_codec device driver.
9+
The driver is located at: devices/dev_audio_codec
10+
11+
endmenu
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
menu "ESP Board Manager Configuration"
2+
3+
menu "Board Manager Setting"
4+
config ESP_BOARD_MANAGER_AUTO_CONFIG_DEVICE_AND_PERIPHERAL
5+
bool "Enable automatic configure device and peripheral in sdkconfig"
6+
default y
7+
help
8+
When enabled, the board manager will automatically configure corresponding
9+
device and peripheral types in user's sdkconfig based on detected
10+
device and peripheral information from YAML configuration files.
11+
endmenu
12+
13+
orsource "./Kconfig.in"
14+
15+
endmenu
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
license
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
#include <stdio.h>
7+
8+
void cmp_hello()
9+
{
10+
printf("Hello from CMP version 1.0.0\n");
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
version: '1.0.0'
2+
targets:
3+
- esp32
4+
maintainers:
5+
- 'Test Tester <[email protected]>'
6+
dependencies:
7+
espressif/esp_codec_dev:
8+
matches:
9+
- if: $CONFIG{ESP_BOARD_DEV_AUDIO_CODEC_SUPPORT} == True
10+
require: public
11+
version: 1.5.0

0 commit comments

Comments
 (0)