설정값은 어디서 와서 어디에 남는가
Zephyr 3.7.0의 Kconfig는 빌드 중 심볼 값을 결정한다. 보드의 <BOARD>_defconfig, CMake의 CONFIG_ cache 항목, 애플리케이션의 prj.conf 또는 CONF_FILE 조각이 입력이다. Kconfig 정의의 prompt·dependency·default·choice를 통과한 결과가 build/zephyr/.config와 C용 autoconf.h에 남는다. 이 결과를 장치의 런타임 ready 상태와 혼동하면 원인 분석이 빗나간다.
화살표는 빌드 설정의 흐름이다. Kconfig.defconfig는 숨은 심볼 등의 보드 기본값을 정의하는 Kconfig 파일이고, <BOARD>_defconfig는 병합할 설정 조각이다. 작은 화면에서는 그림을 좌우로 스크롤할 수 있다.
| 입력 또는 결과 | 역할 | 확인할 근거 |
|---|---|---|
Kconfig, Kconfig.defconfig | 심볼 정의·의존성·기본값 | 보드의 숨은 심볼은 prj.conf로 직접 지정할 수 있는지 확인 |
<BOARD>_defconfig | 선택한 보드의 초기 요청값 | 해당 보드 타깃과 실제 파일의 대응 |
prj.conf / CONF_FILE | 애플리케이션 설정 조각 | 어느 앱 설정 파일이 이번 빌드에 포함됐는가 |
build/zephyr/.config | 최종 설정값 | 요청한 값과 실제 값이 같은가 |
autoconf.h | C 전처리에서 사용할 설정 매크로 | 필요한 기능이 컴파일에 반영됐는가 |
고정 원본과 공식 문서
다음 행 번호는 Zephyr v3.7.0 태그가 가리키는 commit 36940db938a8f4a1e919496793ed439850a221c2의 원본 물리 행이다. 일반 설명은 v3.7.0 Kconfig 안내, 입력 병합·가시성·우선순위는 설정값 안내, select·imply의 주의점은 작성 지침을 기준으로 했다.
| 고정 원본 | 이 글에서 확인한 근거 |
|---|---|
scripts/kconfig/kconfig.py | 조각 병합 60–64행, prompt 없는 심볼·요청값 불일치 점검 66–79행, 출력 120–122행 |
nrf52840dk_nrf52840_defconfig | 보드의 가시 설정 요청값 3–20행 |
Kconfig.defconfig | 보드 조건 아래 BT_CTLR 기본값 6–11행 |
samples/hello_world/prj.conf | 애플리케이션 소유 설정 파일의 실제 위치; 이 예제는 1행 주석만 있음 |
prj.conf는 애플리케이션 구성 디렉터리에서 찾는다. 저장소 전체에 적용되는 최상위 설정 파일로 취급하면 입력 출처를 잘못 추적하게 된다.
원본 발췌와 설명용 설정
1. 여러 조각은 병합되고 두 출력이 기록된다
고정 원본 61–64행은 첫 조각을 불러온 뒤 다음 조각을 병합한다. 아래의 두 발췌는 파일에서 떨어진 위치에 있으므로 따로 표시했다. 공통으로 들여쓴 네 칸만 제거했다.
print(kconf.load_config(args.configs_in[0]))
for config in args.configs_in[1:]:
# replace=False creates a merged configuration
print(kconf.load_config(config, replace=False))
마지막에 원본 120–122행이 최종 설정과 C 헤더를 기록한다.
# Write the merged configuration and the C header
print(kconf.write_config(args.config_out))
print(kconf.write_autoconf(args.header_out))
출력은 검사가 통과했을 때 기록된다. 87–122행은 심볼을 평가하고 경고를 확인한 뒤, 오류로 처리할 경고가 있으면 출력을 쓰기 전에 중단한다. 빌드가 실패했다면 기존 .config를 이번 요청이 반영된 결과로 간주하지 않는다.
입력 조각의 실제 목록과 보드·앱 선택은 빌드 설정에서 확인해야 한다. 위의 호출만으로 모든 심볼의 최종값이 결정되는 것은 아니다. 각 요청은 Kconfig 정의의 가시성·의존성·기본값 판정을 거친다.
2. 보드 설정 파일과 앱 설정 파일은 다르다
고정 보드 원본의 12–20행에는 CONFIG_GPIO=y, CONFIG_SERIAL=y, CONFIG_CONSOLE=y가 있다. 반면 고정 hello_world/prj.conf 1행은 주석뿐이다. 앱 설정을 직접 수정하지 않았다는 사실과 최종 심볼 값은 같지 않다. 보드 입력과 Kconfig 판정도 확인해야 한다.
3. 다음은 설명용 예제다
config MY_FEATURE
bool "Example feature"
depends on GPIO
# application prj.conf의 가상 요청
CONFIG_MY_FEATURE=y
MY_FEATURE는 upstream 심볼이 아니다. GPIO=n이면 요청을 적어도 의존성을 만족하지 못해 최종값이 y가 될 수 없다. 같은 이유로 select를 일반적인 dependency 해결책처럼 쓰면 안 된다. Zephyr 문서는 select가 피선택 심볼의 의존성을 무시할 수 있다고 경고하고, imply는 강제하지 않는 제안으로 구분한다.
병합과 판정 순서
- 보드를 고른다. 선택된 타깃에 대응하는 보드
_defconfig와 보드 Kconfig 정의를 찾는다. 보드별 고정 하드웨어 기술은 Kconfig보다 devicetree가 적절한 경우도 있다. - 앱 설정 출처를 확인한다. 기본은
prj.conf이고,CONF_FILE을 지정하면 그 설정 조각을 쓴다. 보드별 앱 설정과 추가 조각이 있으면 실제 빌드 입력 목록을 확인한다. - 요청과 최종값을 구분한다. 가시 심볼에서 앱 조각의 나중 요청은 보드 조각의 요청보다 우선할 수 있지만, dependency를 넘지는 못한다. prompt 없는 심볼은
prj.conf대입으로 직접 바꾸지 못한다. - 산출물을 읽는다.
build/zephyr/.config에서 실제 값을 확인하고, 불일치 경고가 있다면 심볼의 가시성·의존성·기본값·choice를 추적한다.
설정 입력과 결과의 경계
Kconfig의 심볼 값 판정은 빌드 단계에서 끝난다. 실행 중 장치 준비나 실패는 이후의 별도 경계에서 판단한다. 두 시점을 분리하면 설정 문제의 증거부터 바로 찾을 수 있다.
| 상황 | Kconfig에서 판정할 것 | 다음 확인 |
|---|---|---|
가시 심볼에 CONFIG_FOO=y 요청 | prompt와 dependency가 만족하는가 | .config의 실제 값과 경고 |
| prompt 없는 심볼을 앱에서 지정 | 직접 대입은 효력이 없음 | 그 값을 정하는 default·select·보드 Kconfig.defconfig |
| 보드와 앱이 같은 가시 심볼을 지정 | 앱 설정 요청이 우선할 수 있음 | 최종값은 dependency 판정 뒤에 확인 |
select 또는 imply 사용 | 강제 선택과 제안의 차이 | 피선택 심볼의 직접·상속 의존성 |
하나의 choice에서 여러 y | 마지막 선택 요청이 남을 수 있음 | 최종 선택 심볼과 입력 순서 |
증상별 첫 진단표
| 증상 | 먼저 볼 증거 | 좁혀 갈 원인 |
|---|---|---|
설정에 =y를 썼는데 결과가 n | 빌드 경고, .config, 심볼의 depends on | 의존성 미충족 또는 prompt 가시성 |
| 설정 파일 대입이 무시됨 | 심볼 정의의 prompt, 빌드가 읽은 앱 조각 | 숨은 심볼 또는 잘못된 CONF_FILE 선택 |
| 보드 교체 뒤 값이 달라짐 | 대상 보드 _defconfig, Kconfig.defconfig, .config | 보드 요청값·기본값·의존성 차이 |
| 한 choice의 예상 심볼이 선택되지 않음 | 모든 조각의 선택 요청 순서 | 뒤 조각의 선택 또는 choice 의존성 |
코드가 실행되지 않는 문제는 그다음 단계다. 먼저 설정이 어떤 C 코드를 포함시켰는지 확인한 후, devicetree·링크·초기화 문제로 범위를 넓힌다.
nRF52840 DK + hello_world 예: 빈 prj.conf도 입력이다
Zephyr 3.7.0 고정 원본에서 nrf52840dk_nrf52840_defconfig는 GPIO, SERIAL, CONSOLE 등을 요청하고, samples/hello_world/prj.conf는 # nothing here 한 줄뿐이다. 따라서 앱 파일에 설정이 없다는 말만으로 모든 기능이 꺼졌다고 결론내릴 수 없다. 실제 빌드를 수행한 뒤에는 .config와 빌드 경고로 최종값을 확인한다.
같은 보드의 Kconfig.defconfig는 BOARD_NRF52840DK 조건 아래 BT_CTLR의 default BT를 정의한다. 이는 보드별 기본값의 예이지, 숨은 심볼의 예는 아니다. BT_CTLR의 기본 정의에는 bool "Bluetooth Controller"라는 prompt가 있다. 따라서 Kconfig.defconfig에 기본값이 있다는 이유만으로 앱에서 값을 지정할 수 없는 심볼이라고 판단하면 안 된다. 실제 설정 가능 여부는 전체 정의와 의존성을 함께 확인한다.
Linux Kconfig와 비교할 때
Zephyr와 Linux는 Kconfig 언어와 dependency 개념을 공유한다. 다만 이 글의 핵심은 Zephyr 애플리케이션을 특정 보드로 빌드할 때 보드 조각, 앱 조각, CMake 입력을 어떤 결과에 연결하는가다. Linux에서 익힌 depends on, default, select의 의미를 가져오더라도, 파일의 역할과 빌드 산출물 경로는 Zephyr 3.7.0의 문서·원본으로 다시 확인한다.
재현·확인 명령
다음은 Zephyr 3.7.0 작업공간에서 저장소 루트를 현재 디렉터리로 둔 설명용 예시다. 실제 보드와 앱 경로는 자신의 작업공간에 맞춰 바꾼다.
west build -b nrf52840dk/nrf52840 samples/hello_world -d build/kconfig-lab -p always
rg '^CONFIG_(GPIO|SERIAL|CONSOLE)=' build/kconfig-lab/zephyr/.config
west build -d build/kconfig-lab -t menuconfig
첫 줄의 보드 qualifier 형식은 v3.7.0 공식 보드 문서에 맞췄다. menuconfig에서 심볼을 검색하면 의존성을 따라갈 수 있다. 최종 값은 각자의 빌드 산출물에서 확인한다.