mirror of
https://github.com/Leonmmcoset/cleonos.git
synced 2026-04-21 18:44:01 +00:00
menuconfig
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -12,3 +12,7 @@ compile_commands.json
|
|||||||
CTestTestfile.cmake
|
CTestTestfile.cmake
|
||||||
Testing/
|
Testing/
|
||||||
.cmake/
|
.cmake/
|
||||||
|
|
||||||
|
# menuconfig generated
|
||||||
|
configs/menuconfig/.config.json
|
||||||
|
configs/menuconfig/config.cmake
|
||||||
|
|||||||
105
CMakeLists.txt
105
CMakeLists.txt
@@ -117,6 +117,56 @@ set(CLKS_ARCH "x86_64" CACHE STRING "Target CLKS arch")
|
|||||||
set(LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/clks/arch/${CLKS_ARCH}/linker.ld")
|
set(LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/clks/arch/${CLKS_ARCH}/linker.ld")
|
||||||
set(USER_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/cleonos/c/user.ld")
|
set(USER_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/cleonos/c/user.ld")
|
||||||
set(KELF_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/cleonos/c/kelf.ld")
|
set(KELF_LINKER_SCRIPT "${CMAKE_SOURCE_DIR}/cleonos/c/kelf.ld")
|
||||||
|
set(CLEONOS_MENUCONFIG_CMAKE "${CMAKE_SOURCE_DIR}/configs/menuconfig/config.cmake" CACHE FILEPATH "menuconfig generated CMake config")
|
||||||
|
|
||||||
|
if(EXISTS "${CLEONOS_MENUCONFIG_CMAKE}")
|
||||||
|
include("${CLEONOS_MENUCONFIG_CMAKE}")
|
||||||
|
cl_log_info("menuconfig loaded from ${CLEONOS_MENUCONFIG_CMAKE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
function(cl_set_bool_cache VAR_NAME DEFAULT_VALUE DOC_TEXT)
|
||||||
|
if(NOT DEFINED ${VAR_NAME})
|
||||||
|
set(${VAR_NAME} ${DEFAULT_VALUE} CACHE BOOL "${DOC_TEXT}")
|
||||||
|
else()
|
||||||
|
set(${VAR_NAME} ${${VAR_NAME}} CACHE BOOL "${DOC_TEXT}" FORCE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(cl_bool_to_int BOOL_VAR OUT_VAR)
|
||||||
|
if(${BOOL_VAR})
|
||||||
|
set(${OUT_VAR} 1 PARENT_SCOPE)
|
||||||
|
else()
|
||||||
|
set(${OUT_VAR} 0 PARENT_SCOPE)
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_AUDIO ON "Initialize kernel audio subsystem")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_MOUSE ON "Initialize kernel mouse subsystem")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_DESKTOP ON "Initialize desktop compositor subsystem")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_DRIVER_MANAGER ON "Initialize kernel driver manager")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KELF ON "Enable KELF app dispatcher")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USERLAND_AUTO_EXEC ON "Auto exec /shell/shell.elf on boot")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_HEAP_SELFTEST ON "Run heap selftest at boot")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_EXTERNAL_PSF ON "Load /system/tty.psf external font")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KEYBOARD ON "Initialize keyboard input subsystem")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_ELFRUNNER_PROBE ON "Run ELFRUNNER kernel ELF probe")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KLOGD_TASK ON "Enable scheduler klogd task")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_KWORKER_TASK ON "Enable scheduler kworker task")
|
||||||
|
cl_set_bool_cache(CLEONOS_CLKS_ENABLE_USRD_TASK ON "Enable scheduler usrd task")
|
||||||
|
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_AUDIO CLKS_CFG_AUDIO_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_MOUSE CLKS_CFG_MOUSE_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_DESKTOP CLKS_CFG_DESKTOP_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_DRIVER_MANAGER CLKS_CFG_DRIVER_MANAGER_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KELF CLKS_CFG_KELF_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USERLAND_AUTO_EXEC CLKS_CFG_USERLAND_AUTO_EXEC_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_HEAP_SELFTEST CLKS_CFG_HEAP_SELFTEST_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_EXTERNAL_PSF CLKS_CFG_EXTERNAL_PSF_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KEYBOARD CLKS_CFG_KEYBOARD_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_ELFRUNNER_PROBE CLKS_CFG_ELFRUNNER_PROBE_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KLOGD_TASK CLKS_CFG_KLOGD_TASK_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_KWORKER_TASK CLKS_CFG_KWORKER_TASK_INT)
|
||||||
|
cl_bool_to_int(CLEONOS_CLKS_ENABLE_USRD_TASK CLKS_CFG_USRD_TASK_INT)
|
||||||
|
|
||||||
set(CFLAGS_COMMON
|
set(CFLAGS_COMMON
|
||||||
-std=c11
|
-std=c11
|
||||||
@@ -132,6 +182,19 @@ set(CFLAGS_COMMON
|
|||||||
|
|
||||||
set(ARCH_CFLAGS
|
set(ARCH_CFLAGS
|
||||||
-DCLKS_ARCH_X86_64=1
|
-DCLKS_ARCH_X86_64=1
|
||||||
|
"-DCLKS_CFG_AUDIO=${CLKS_CFG_AUDIO_INT}"
|
||||||
|
"-DCLKS_CFG_MOUSE=${CLKS_CFG_MOUSE_INT}"
|
||||||
|
"-DCLKS_CFG_DESKTOP=${CLKS_CFG_DESKTOP_INT}"
|
||||||
|
"-DCLKS_CFG_DRIVER_MANAGER=${CLKS_CFG_DRIVER_MANAGER_INT}"
|
||||||
|
"-DCLKS_CFG_KELF=${CLKS_CFG_KELF_INT}"
|
||||||
|
"-DCLKS_CFG_USERLAND_AUTO_EXEC=${CLKS_CFG_USERLAND_AUTO_EXEC_INT}"
|
||||||
|
"-DCLKS_CFG_HEAP_SELFTEST=${CLKS_CFG_HEAP_SELFTEST_INT}"
|
||||||
|
"-DCLKS_CFG_EXTERNAL_PSF=${CLKS_CFG_EXTERNAL_PSF_INT}"
|
||||||
|
"-DCLKS_CFG_KEYBOARD=${CLKS_CFG_KEYBOARD_INT}"
|
||||||
|
"-DCLKS_CFG_ELFRUNNER_PROBE=${CLKS_CFG_ELFRUNNER_PROBE_INT}"
|
||||||
|
"-DCLKS_CFG_KLOGD_TASK=${CLKS_CFG_KLOGD_TASK_INT}"
|
||||||
|
"-DCLKS_CFG_KWORKER_TASK=${CLKS_CFG_KWORKER_TASK_INT}"
|
||||||
|
"-DCLKS_CFG_USRD_TASK=${CLKS_CFG_USRD_TASK_INT}"
|
||||||
-m64
|
-m64
|
||||||
-mno-red-zone
|
-mno-red-zone
|
||||||
-mcmodel=kernel
|
-mcmodel=kernel
|
||||||
@@ -393,6 +456,9 @@ set(USER_SHELL_COMMAND_APPS
|
|||||||
foreach(SRC IN LISTS USER_APP_MAIN_SOURCES)
|
foreach(SRC IN LISTS USER_APP_MAIN_SOURCES)
|
||||||
get_filename_component(_stem "${SRC}" NAME_WE)
|
get_filename_component(_stem "${SRC}" NAME_WE)
|
||||||
string(REGEX REPLACE "_main$" "" _app_name "${_stem}")
|
string(REGEX REPLACE "_main$" "" _app_name "${_stem}")
|
||||||
|
string(TOUPPER "${_app_name}" _app_name_upper)
|
||||||
|
string(REGEX REPLACE "[^A-Z0-9]" "_" _app_name_key "${_app_name_upper}")
|
||||||
|
set(_app_enabled_var "CLEONOS_USER_APP_${_app_name_key}")
|
||||||
|
|
||||||
list(FIND USER_KELF_APP_NAMES "${_app_name}" _shadowed_by_kmain)
|
list(FIND USER_KELF_APP_NAMES "${_app_name}" _shadowed_by_kmain)
|
||||||
if(NOT _shadowed_by_kmain EQUAL -1)
|
if(NOT _shadowed_by_kmain EQUAL -1)
|
||||||
@@ -400,6 +466,17 @@ foreach(SRC IN LISTS USER_APP_MAIN_SOURCES)
|
|||||||
continue()
|
continue()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(NOT DEFINED ${_app_enabled_var})
|
||||||
|
set(${_app_enabled_var} ON CACHE BOOL "Build user app ${_app_name}")
|
||||||
|
else()
|
||||||
|
set(${_app_enabled_var} ${${_app_enabled_var}} CACHE BOOL "Build user app ${_app_name}" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT ${_app_enabled_var})
|
||||||
|
cl_log_info("menuconfig: skip disabled user app ${_app_name}")
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
|
|
||||||
list(FIND USER_APP_NAMES "${_app_name}" _dup_name_idx)
|
list(FIND USER_APP_NAMES "${_app_name}" _dup_name_idx)
|
||||||
if(NOT _dup_name_idx EQUAL -1)
|
if(NOT _dup_name_idx EQUAL -1)
|
||||||
cl_log_error("duplicate user app name: ${_app_name}")
|
cl_log_error("duplicate user app name: ${_app_name}")
|
||||||
@@ -456,6 +533,20 @@ endforeach()
|
|||||||
foreach(SRC IN LISTS USER_APP_KMAIN_SOURCES)
|
foreach(SRC IN LISTS USER_APP_KMAIN_SOURCES)
|
||||||
get_filename_component(_stem "${SRC}" NAME_WE)
|
get_filename_component(_stem "${SRC}" NAME_WE)
|
||||||
string(REGEX REPLACE "_kmain$" "" _app_name "${_stem}")
|
string(REGEX REPLACE "_kmain$" "" _app_name "${_stem}")
|
||||||
|
string(TOUPPER "${_app_name}" _app_name_upper)
|
||||||
|
string(REGEX REPLACE "[^A-Z0-9]" "_" _app_name_key "${_app_name_upper}")
|
||||||
|
set(_app_enabled_var "CLEONOS_USER_APP_${_app_name_key}")
|
||||||
|
|
||||||
|
if(NOT DEFINED ${_app_enabled_var})
|
||||||
|
set(${_app_enabled_var} ON CACHE BOOL "Build user app ${_app_name}")
|
||||||
|
else()
|
||||||
|
set(${_app_enabled_var} ${${_app_enabled_var}} CACHE BOOL "Build user app ${_app_name}" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT ${_app_enabled_var})
|
||||||
|
cl_log_info("menuconfig: skip disabled user app ${_app_name}")
|
||||||
|
continue()
|
||||||
|
endif()
|
||||||
|
|
||||||
list(FIND USER_APP_NAMES "${_app_name}" _dup_name_idx)
|
list(FIND USER_APP_NAMES "${_app_name}" _dup_name_idx)
|
||||||
if(NOT _dup_name_idx EQUAL -1)
|
if(NOT _dup_name_idx EQUAL -1)
|
||||||
@@ -690,9 +781,23 @@ add_custom_target(clean-all
|
|||||||
-P "${CL_LOG_EMIT_SCRIPT}"
|
-P "${CL_LOG_EMIT_SCRIPT}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
find_package(Python3 COMPONENTS Interpreter QUIET)
|
||||||
|
if(Python3_Interpreter_FOUND)
|
||||||
|
add_custom_target(menuconfig
|
||||||
|
COMMAND ${Python3_EXECUTABLE} "${CMAKE_SOURCE_DIR}/scripts/menuconfig.py"
|
||||||
|
USES_TERMINAL
|
||||||
|
)
|
||||||
|
else()
|
||||||
|
add_custom_target(menuconfig
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E echo "python3 not found; run scripts/menuconfig.py manually"
|
||||||
|
USES_TERMINAL
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_custom_target(cleonos-help
|
add_custom_target(cleonos-help
|
||||||
COMMAND ${CMAKE_COMMAND} -E echo "CLeonOS CMake build system (x86_64 only)"
|
COMMAND ${CMAKE_COMMAND} -E echo "CLeonOS CMake build system (x86_64 only)"
|
||||||
COMMAND ${CMAKE_COMMAND} -E echo " cmake -S . -B build-cmake"
|
COMMAND ${CMAKE_COMMAND} -E echo " cmake -S . -B build-cmake"
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target menuconfig"
|
||||||
COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target setup"
|
COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target setup"
|
||||||
COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target userapps"
|
COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target userapps"
|
||||||
COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target iso"
|
COMMAND ${CMAKE_COMMAND} -E echo " cmake --build build-cmake --target iso"
|
||||||
|
|||||||
16
Makefile
16
Makefile
@@ -15,6 +15,8 @@ LIMINE_BIN_DIR ?=
|
|||||||
OBJCOPY_FOR_TARGET ?=
|
OBJCOPY_FOR_TARGET ?=
|
||||||
OBJDUMP_FOR_TARGET ?=
|
OBJDUMP_FOR_TARGET ?=
|
||||||
READELF_FOR_TARGET ?=
|
READELF_FOR_TARGET ?=
|
||||||
|
PYTHON ?= python3
|
||||||
|
MENUCONFIG_ARGS ?=
|
||||||
|
|
||||||
ifeq ($(strip $(CMAKE_GENERATOR)),)
|
ifeq ($(strip $(CMAKE_GENERATOR)),)
|
||||||
GEN_ARG :=
|
GEN_ARG :=
|
||||||
@@ -49,7 +51,7 @@ ifneq ($(strip $(READELF_FOR_TARGET)),)
|
|||||||
CMAKE_PASSTHROUGH_ARGS += -DREADELF_FOR_TARGET=$(READELF_FOR_TARGET)
|
CMAKE_PASSTHROUGH_ARGS += -DREADELF_FOR_TARGET=$(READELF_FOR_TARGET)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
.PHONY: all configure reconfigure setup setup-tools setup-limine kernel userapps ramdisk-root ramdisk iso run debug clean clean-all help
|
.PHONY: all configure reconfigure menuconfig setup setup-tools setup-limine kernel userapps ramdisk-root ramdisk iso run debug clean clean-all help
|
||||||
|
|
||||||
all: iso
|
all: iso
|
||||||
|
|
||||||
@@ -60,6 +62,17 @@ reconfigure:
|
|||||||
> @rm -rf $(CMAKE_BUILD_DIR)
|
> @rm -rf $(CMAKE_BUILD_DIR)
|
||||||
> @$(MAKE) configure CMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) CMAKE_GENERATOR="$(CMAKE_GENERATOR)" CMAKE_EXTRA_ARGS="$(CMAKE_EXTRA_ARGS)" NO_COLOR="$(NO_COLOR)" LIMINE_SKIP_CONFIGURE="$(LIMINE_SKIP_CONFIGURE)" LIMINE_REF="$(LIMINE_REF)" LIMINE_REPO="$(LIMINE_REPO)" LIMINE_DIR="$(LIMINE_DIR)" LIMINE_BIN_DIR="$(LIMINE_BIN_DIR)" OBJCOPY_FOR_TARGET="$(OBJCOPY_FOR_TARGET)" OBJDUMP_FOR_TARGET="$(OBJDUMP_FOR_TARGET)" READELF_FOR_TARGET="$(READELF_FOR_TARGET)"
|
> @$(MAKE) configure CMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) CMAKE_GENERATOR="$(CMAKE_GENERATOR)" CMAKE_EXTRA_ARGS="$(CMAKE_EXTRA_ARGS)" NO_COLOR="$(NO_COLOR)" LIMINE_SKIP_CONFIGURE="$(LIMINE_SKIP_CONFIGURE)" LIMINE_REF="$(LIMINE_REF)" LIMINE_REPO="$(LIMINE_REPO)" LIMINE_DIR="$(LIMINE_DIR)" LIMINE_BIN_DIR="$(LIMINE_BIN_DIR)" OBJCOPY_FOR_TARGET="$(OBJCOPY_FOR_TARGET)" OBJDUMP_FOR_TARGET="$(OBJDUMP_FOR_TARGET)" READELF_FOR_TARGET="$(READELF_FOR_TARGET)"
|
||||||
|
|
||||||
|
menuconfig:
|
||||||
|
> @if command -v $(PYTHON) >/dev/null 2>&1; then \
|
||||||
|
> $(PYTHON) scripts/menuconfig.py $(MENUCONFIG_ARGS); \
|
||||||
|
> elif command -v python >/dev/null 2>&1; then \
|
||||||
|
> python scripts/menuconfig.py $(MENUCONFIG_ARGS); \
|
||||||
|
> else \
|
||||||
|
> echo "python3/python not found"; \
|
||||||
|
> exit 1; \
|
||||||
|
> fi
|
||||||
|
> @$(MAKE) configure CMAKE_BUILD_TYPE=$(CMAKE_BUILD_TYPE) CMAKE_GENERATOR="$(CMAKE_GENERATOR)" CMAKE_EXTRA_ARGS="$(CMAKE_EXTRA_ARGS)" NO_COLOR="$(NO_COLOR)" LIMINE_SKIP_CONFIGURE="$(LIMINE_SKIP_CONFIGURE)" LIMINE_REF="$(LIMINE_REF)" LIMINE_REPO="$(LIMINE_REPO)" LIMINE_DIR="$(LIMINE_DIR)" LIMINE_BIN_DIR="$(LIMINE_BIN_DIR)" OBJCOPY_FOR_TARGET="$(OBJCOPY_FOR_TARGET)" OBJDUMP_FOR_TARGET="$(OBJDUMP_FOR_TARGET)" READELF_FOR_TARGET="$(READELF_FOR_TARGET)"
|
||||||
|
|
||||||
setup: configure
|
setup: configure
|
||||||
> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target setup
|
> @$(CMAKE) --build $(CMAKE_BUILD_DIR) --target setup
|
||||||
|
|
||||||
@@ -107,6 +120,7 @@ clean-all:
|
|||||||
help:
|
help:
|
||||||
> @echo "CLeonOS (CMake-backed wrapper)"
|
> @echo "CLeonOS (CMake-backed wrapper)"
|
||||||
> @echo " make configure"
|
> @echo " make configure"
|
||||||
|
> @echo " make menuconfig"
|
||||||
> @echo " make setup"
|
> @echo " make setup"
|
||||||
> @echo " make userapps"
|
> @echo " make userapps"
|
||||||
> @echo " make iso"
|
> @echo " make iso"
|
||||||
|
|||||||
@@ -26,6 +26,54 @@
|
|||||||
#include <clks/types.h>
|
#include <clks/types.h>
|
||||||
#include <clks/userland.h>
|
#include <clks/userland.h>
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_AUDIO
|
||||||
|
#define CLKS_CFG_AUDIO 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_MOUSE
|
||||||
|
#define CLKS_CFG_MOUSE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_DESKTOP
|
||||||
|
#define CLKS_CFG_DESKTOP 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_DRIVER_MANAGER
|
||||||
|
#define CLKS_CFG_DRIVER_MANAGER 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_KELF
|
||||||
|
#define CLKS_CFG_KELF 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_HEAP_SELFTEST
|
||||||
|
#define CLKS_CFG_HEAP_SELFTEST 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_EXTERNAL_PSF
|
||||||
|
#define CLKS_CFG_EXTERNAL_PSF 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_KEYBOARD
|
||||||
|
#define CLKS_CFG_KEYBOARD 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_ELFRUNNER_PROBE
|
||||||
|
#define CLKS_CFG_ELFRUNNER_PROBE 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_KLOGD_TASK
|
||||||
|
#define CLKS_CFG_KLOGD_TASK 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_KWORKER_TASK
|
||||||
|
#define CLKS_CFG_KWORKER_TASK 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USRD_TASK
|
||||||
|
#define CLKS_CFG_USRD_TASK 1
|
||||||
|
#endif
|
||||||
|
|
||||||
static void clks_task_klogd(u64 tick) {
|
static void clks_task_klogd(u64 tick) {
|
||||||
static u64 last_emit = 0ULL;
|
static u64 last_emit = 0ULL;
|
||||||
|
|
||||||
@@ -61,15 +109,21 @@ static void clks_task_kworker(u64 tick) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void clks_task_kelfd(u64 tick) {
|
static void clks_task_kelfd(u64 tick) {
|
||||||
|
#if CLKS_CFG_KELF
|
||||||
clks_service_heartbeat(CLKS_SERVICE_KELF, tick);
|
clks_service_heartbeat(CLKS_SERVICE_KELF, tick);
|
||||||
clks_kelf_tick(tick);
|
clks_kelf_tick(tick);
|
||||||
|
#else
|
||||||
|
(void)tick;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clks_task_usrd(u64 tick) {
|
static void clks_task_usrd(u64 tick) {
|
||||||
clks_service_heartbeat(CLKS_SERVICE_USER, tick);
|
clks_service_heartbeat(CLKS_SERVICE_USER, tick);
|
||||||
clks_exec_tick(tick);
|
clks_exec_tick(tick);
|
||||||
clks_userland_tick(tick);
|
clks_userland_tick(tick);
|
||||||
|
#if CLKS_CFG_DESKTOP
|
||||||
clks_desktop_tick(tick);
|
clks_desktop_tick(tick);
|
||||||
|
#endif
|
||||||
clks_tty_tick(tick);
|
clks_tty_tick(tick);
|
||||||
clks_shell_tick(tick);
|
clks_shell_tick(tick);
|
||||||
}
|
}
|
||||||
@@ -81,11 +135,8 @@ void clks_kernel_main(void) {
|
|||||||
struct clks_heap_stats heap_stats;
|
struct clks_heap_stats heap_stats;
|
||||||
struct clks_scheduler_stats sched_stats;
|
struct clks_scheduler_stats sched_stats;
|
||||||
struct clks_fs_node_info fs_system_dir = {0};
|
struct clks_fs_node_info fs_system_dir = {0};
|
||||||
void *heap_probe = CLKS_NULL;
|
|
||||||
u64 syscall_ticks;
|
u64 syscall_ticks;
|
||||||
u64 fs_root_children;
|
u64 fs_root_children;
|
||||||
const void *tty_psf_blob = CLKS_NULL;
|
|
||||||
u64 tty_psf_size = 0ULL;
|
|
||||||
|
|
||||||
clks_serial_init();
|
clks_serial_init();
|
||||||
|
|
||||||
@@ -139,7 +190,8 @@ void clks_kernel_main(void) {
|
|||||||
clks_log_hex(CLKS_LOG_INFO, "HEAP", "TOTAL_BYTES", heap_stats.total_bytes);
|
clks_log_hex(CLKS_LOG_INFO, "HEAP", "TOTAL_BYTES", heap_stats.total_bytes);
|
||||||
clks_log_hex(CLKS_LOG_INFO, "HEAP", "FREE_BYTES", heap_stats.free_bytes);
|
clks_log_hex(CLKS_LOG_INFO, "HEAP", "FREE_BYTES", heap_stats.free_bytes);
|
||||||
|
|
||||||
heap_probe = clks_kmalloc(128);
|
#if CLKS_CFG_HEAP_SELFTEST
|
||||||
|
void *heap_probe = clks_kmalloc(128);
|
||||||
|
|
||||||
if (heap_probe == CLKS_NULL) {
|
if (heap_probe == CLKS_NULL) {
|
||||||
clks_log(CLKS_LOG_ERROR, "HEAP", "KMALLOC SELFTEST FAILED");
|
clks_log(CLKS_LOG_ERROR, "HEAP", "KMALLOC SELFTEST FAILED");
|
||||||
@@ -147,6 +199,9 @@ void clks_kernel_main(void) {
|
|||||||
clks_log(CLKS_LOG_INFO, "HEAP", "KMALLOC SELFTEST OK");
|
clks_log(CLKS_LOG_INFO, "HEAP", "KMALLOC SELFTEST OK");
|
||||||
clks_kfree(heap_probe);
|
clks_kfree(heap_probe);
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "CFG", "HEAP SELFTEST DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
|
||||||
clks_fs_init();
|
clks_fs_init();
|
||||||
|
|
||||||
@@ -164,6 +219,10 @@ void clks_kernel_main(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (boot_fb != CLKS_NULL) {
|
if (boot_fb != CLKS_NULL) {
|
||||||
|
#if CLKS_CFG_EXTERNAL_PSF
|
||||||
|
const void *tty_psf_blob;
|
||||||
|
u64 tty_psf_size = 0ULL;
|
||||||
|
|
||||||
tty_psf_blob = clks_fs_read_all("/system/tty.psf", &tty_psf_size);
|
tty_psf_blob = clks_fs_read_all("/system/tty.psf", &tty_psf_size);
|
||||||
|
|
||||||
if (tty_psf_blob != CLKS_NULL && clks_fb_load_psf_font(tty_psf_blob, tty_psf_size) == CLKS_TRUE) {
|
if (tty_psf_blob != CLKS_NULL && clks_fb_load_psf_font(tty_psf_blob, tty_psf_size) == CLKS_TRUE) {
|
||||||
@@ -173,39 +232,82 @@ void clks_kernel_main(void) {
|
|||||||
} else {
|
} else {
|
||||||
clks_log(CLKS_LOG_WARN, "TTY", "EXTERNAL PSF LOAD FAILED, USING BUILTIN");
|
clks_log(CLKS_LOG_WARN, "TTY", "EXTERNAL PSF LOAD FAILED, USING BUILTIN");
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "CFG", "EXTERNAL PSF LOADING DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
clks_exec_init();
|
clks_exec_init();
|
||||||
|
#if CLKS_CFG_AUDIO
|
||||||
clks_audio_init();
|
clks_audio_init();
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "CFG", "AUDIO DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
#if CLKS_CFG_KEYBOARD
|
||||||
clks_keyboard_init();
|
clks_keyboard_init();
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "CFG", "KEYBOARD DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
#if CLKS_CFG_MOUSE
|
||||||
clks_mouse_init();
|
clks_mouse_init();
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "CFG", "MOUSE DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
#if CLKS_CFG_DESKTOP
|
||||||
clks_desktop_init();
|
clks_desktop_init();
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "CFG", "DESKTOP DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
|
||||||
if (clks_userland_init() == CLKS_FALSE) {
|
if (clks_userland_init() == CLKS_FALSE) {
|
||||||
clks_log(CLKS_LOG_ERROR, "USER", "USERLAND INIT FAILED");
|
clks_log(CLKS_LOG_ERROR, "USER", "USERLAND INIT FAILED");
|
||||||
clks_cpu_halt_forever();
|
clks_cpu_halt_forever();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if CLKS_CFG_DRIVER_MANAGER
|
||||||
clks_driver_init();
|
clks_driver_init();
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "CFG", "DRIVER MANAGER DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
#if CLKS_CFG_KELF
|
||||||
clks_kelf_init();
|
clks_kelf_init();
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "CFG", "KELF DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
|
||||||
clks_scheduler_init();
|
clks_scheduler_init();
|
||||||
|
|
||||||
|
#if CLKS_CFG_KLOGD_TASK
|
||||||
if (clks_scheduler_add_kernel_task_ex("klogd", 4U, clks_task_klogd) == CLKS_FALSE) {
|
if (clks_scheduler_add_kernel_task_ex("klogd", 4U, clks_task_klogd) == CLKS_FALSE) {
|
||||||
clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD KLOGD TASK");
|
clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD KLOGD TASK");
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "SCHED", "KLOGD TASK DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CLKS_CFG_KWORKER_TASK
|
||||||
if (clks_scheduler_add_kernel_task_ex("kworker", 3U, clks_task_kworker) == CLKS_FALSE) {
|
if (clks_scheduler_add_kernel_task_ex("kworker", 3U, clks_task_kworker) == CLKS_FALSE) {
|
||||||
clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD KWORKER TASK");
|
clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD KWORKER TASK");
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "SCHED", "KWORKER TASK DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CLKS_CFG_KELF
|
||||||
if (clks_scheduler_add_kernel_task_ex("kelfd", 5U, clks_task_kelfd) == CLKS_FALSE) {
|
if (clks_scheduler_add_kernel_task_ex("kelfd", 5U, clks_task_kelfd) == CLKS_FALSE) {
|
||||||
clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD KELFD TASK");
|
clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD KELFD TASK");
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "SCHED", "KELFD TASK DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if CLKS_CFG_USRD_TASK
|
||||||
if (clks_scheduler_add_kernel_task_ex("usrd", 4U, clks_task_usrd) == CLKS_FALSE) {
|
if (clks_scheduler_add_kernel_task_ex("usrd", 4U, clks_task_usrd) == CLKS_FALSE) {
|
||||||
clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD USRD TASK");
|
clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD USRD TASK");
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "SCHED", "USRD TASK DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
|
||||||
sched_stats = clks_scheduler_get_stats();
|
sched_stats = clks_scheduler_get_stats();
|
||||||
clks_log_hex(CLKS_LOG_INFO, "SCHED", "TASK_COUNT", sched_stats.task_count);
|
clks_log_hex(CLKS_LOG_INFO, "SCHED", "TASK_COUNT", sched_stats.task_count);
|
||||||
@@ -214,9 +316,13 @@ void clks_kernel_main(void) {
|
|||||||
|
|
||||||
clks_elfrunner_init();
|
clks_elfrunner_init();
|
||||||
|
|
||||||
|
#if CLKS_CFG_ELFRUNNER_PROBE
|
||||||
if (clks_elfrunner_probe_kernel_executable() == CLKS_FALSE) {
|
if (clks_elfrunner_probe_kernel_executable() == CLKS_FALSE) {
|
||||||
clks_log(CLKS_LOG_ERROR, "ELF", "KERNEL ELF PROBE FAILED");
|
clks_log(CLKS_LOG_ERROR, "ELF", "KERNEL ELF PROBE FAILED");
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "CFG", "ELFRUNNER PROBE DISABLED BY MENUCONFIG");
|
||||||
|
#endif
|
||||||
|
|
||||||
clks_syscall_init();
|
clks_syscall_init();
|
||||||
|
|
||||||
@@ -228,11 +334,15 @@ void clks_kernel_main(void) {
|
|||||||
|
|
||||||
clks_shell_init();
|
clks_shell_init();
|
||||||
|
|
||||||
|
#if CLKS_CFG_USRD_TASK
|
||||||
if (clks_userland_shell_auto_exec_enabled() == CLKS_TRUE) {
|
if (clks_userland_shell_auto_exec_enabled() == CLKS_TRUE) {
|
||||||
clks_log(CLKS_LOG_INFO, "SHELL", "DEFAULT ENTER USER SHELL MODE");
|
clks_log(CLKS_LOG_INFO, "SHELL", "DEFAULT ENTER USER SHELL MODE");
|
||||||
} else {
|
} else {
|
||||||
clks_log(CLKS_LOG_INFO, "SHELL", "KERNEL SHELL ACTIVE");
|
clks_log(CLKS_LOG_INFO, "SHELL", "KERNEL SHELL ACTIVE");
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
clks_log(CLKS_LOG_WARN, "SHELL", "USRD TASK DISABLED; INTERACTIVE SHELL TICK OFF");
|
||||||
|
#endif
|
||||||
|
|
||||||
clks_log_hex(CLKS_LOG_INFO, "TTY", "COUNT", (u64)clks_tty_count());
|
clks_log_hex(CLKS_LOG_INFO, "TTY", "COUNT", (u64)clks_tty_count());
|
||||||
clks_log_hex(CLKS_LOG_INFO, "TTY", "ACTIVE", (u64)clks_tty_active());
|
clks_log_hex(CLKS_LOG_INFO, "TTY", "ACTIVE", (u64)clks_tty_active());
|
||||||
|
|||||||
@@ -7,6 +7,10 @@
|
|||||||
|
|
||||||
#define CLKS_USERLAND_RETRY_INTERVAL 500ULL
|
#define CLKS_USERLAND_RETRY_INTERVAL 500ULL
|
||||||
|
|
||||||
|
#ifndef CLKS_CFG_USERLAND_AUTO_EXEC
|
||||||
|
#define CLKS_CFG_USERLAND_AUTO_EXEC 1
|
||||||
|
#endif
|
||||||
|
|
||||||
static clks_bool clks_user_shell_ready = CLKS_FALSE;
|
static clks_bool clks_user_shell_ready = CLKS_FALSE;
|
||||||
static clks_bool clks_user_shell_exec_requested_flag = CLKS_FALSE;
|
static clks_bool clks_user_shell_exec_requested_flag = CLKS_FALSE;
|
||||||
static clks_bool clks_user_shell_exec_enabled = CLKS_FALSE;
|
static clks_bool clks_user_shell_exec_enabled = CLKS_FALSE;
|
||||||
@@ -84,7 +88,7 @@ clks_bool clks_userland_init(void) {
|
|||||||
|
|
||||||
clks_user_shell_ready = CLKS_FALSE;
|
clks_user_shell_ready = CLKS_FALSE;
|
||||||
clks_user_shell_exec_requested_flag = CLKS_FALSE;
|
clks_user_shell_exec_requested_flag = CLKS_FALSE;
|
||||||
clks_user_shell_exec_enabled = CLKS_TRUE;
|
clks_user_shell_exec_enabled = (CLKS_CFG_USERLAND_AUTO_EXEC != 0) ? CLKS_TRUE : CLKS_FALSE;
|
||||||
clks_user_launch_attempt_count = 0ULL;
|
clks_user_launch_attempt_count = 0ULL;
|
||||||
clks_user_launch_success_count = 0ULL;
|
clks_user_launch_success_count = 0ULL;
|
||||||
clks_user_launch_fail_count = 0ULL;
|
clks_user_launch_fail_count = 0ULL;
|
||||||
@@ -107,7 +111,11 @@ clks_bool clks_userland_init(void) {
|
|||||||
return CLKS_FALSE;
|
return CLKS_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
clks_log(CLKS_LOG_INFO, "USER", "USER SHELL AUTO EXEC ENABLED");
|
if (clks_user_shell_exec_enabled == CLKS_TRUE) {
|
||||||
|
clks_log(CLKS_LOG_INFO, "USER", "USER SHELL AUTO EXEC ENABLED");
|
||||||
|
} else {
|
||||||
|
clks_log(CLKS_LOG_WARN, "USER", "USER SHELL AUTO EXEC DISABLED BY MENUCONFIG");
|
||||||
|
}
|
||||||
return CLKS_TRUE;
|
return CLKS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
82
configs/menuconfig/clks_features.json
Normal file
82
configs/menuconfig/clks_features.json
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"features": [
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_AUDIO",
|
||||||
|
"title": "Audio Driver Init",
|
||||||
|
"description": "Initialize kernel audio subsystem during boot.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_MOUSE",
|
||||||
|
"title": "PS/2 Mouse Input",
|
||||||
|
"description": "Initialize kernel PS/2 mouse input subsystem.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_DESKTOP",
|
||||||
|
"title": "TTY2 Desktop",
|
||||||
|
"description": "Enable desktop compositor tick/update path on TTY2.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_DRIVER_MANAGER",
|
||||||
|
"title": "Driver Manager",
|
||||||
|
"description": "Initialize kernel ELF driver manager.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_KELF",
|
||||||
|
"title": "KELF Executor",
|
||||||
|
"description": "Enable kernel ELF app dispatcher and kelfd task.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USERLAND_AUTO_EXEC",
|
||||||
|
"title": "Auto Enter User Shell",
|
||||||
|
"description": "Auto-exec /shell/shell.elf after kernel boot.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_HEAP_SELFTEST",
|
||||||
|
"title": "Heap Selftest",
|
||||||
|
"description": "Run kmalloc/kfree selftest during kernel boot.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_EXTERNAL_PSF",
|
||||||
|
"title": "Load External PSF Font",
|
||||||
|
"description": "Load /system/tty.psf and apply it to framebuffer TTY.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_KEYBOARD",
|
||||||
|
"title": "PS/2 Keyboard Input",
|
||||||
|
"description": "Initialize PS/2 keyboard input subsystem.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_ELFRUNNER_PROBE",
|
||||||
|
"title": "ELFRUNNER Probe",
|
||||||
|
"description": "Probe kernel ELF runtime metadata after ELFRUNNER init.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_KLOGD_TASK",
|
||||||
|
"title": "Scheduler Task: klogd",
|
||||||
|
"description": "Enable periodic klogd maintenance task.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_KWORKER_TASK",
|
||||||
|
"title": "Scheduler Task: kworker",
|
||||||
|
"description": "Enable periodic kernel worker service-heartbeat task.",
|
||||||
|
"default": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"key": "CLEONOS_CLKS_ENABLE_USRD_TASK",
|
||||||
|
"title": "Scheduler Task: usrd",
|
||||||
|
"description": "Enable user/runtime dispatch task (shell tick, tty tick, exec tick).",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
518
scripts/menuconfig.py
Normal file
518
scripts/menuconfig.py
Normal file
@@ -0,0 +1,518 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
CLeonOS menuconfig
|
||||||
|
|
||||||
|
Interactive feature selector that writes:
|
||||||
|
- configs/menuconfig/.config.json
|
||||||
|
- configs/menuconfig/config.cmake
|
||||||
|
|
||||||
|
Design:
|
||||||
|
- CLKS options come from configs/menuconfig/clks_features.json
|
||||||
|
- User-space app options are discovered dynamically from *_main.c / *_kmain.c
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import textwrap
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Dict, Iterable, List, Tuple
|
||||||
|
|
||||||
|
try:
|
||||||
|
import curses
|
||||||
|
except Exception:
|
||||||
|
curses = None
|
||||||
|
|
||||||
|
|
||||||
|
ROOT_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
APPS_DIR = ROOT_DIR / "cleonos" / "c" / "apps"
|
||||||
|
MENUCONFIG_DIR = ROOT_DIR / "configs" / "menuconfig"
|
||||||
|
CLKS_FEATURES_PATH = MENUCONFIG_DIR / "clks_features.json"
|
||||||
|
CONFIG_JSON_PATH = MENUCONFIG_DIR / ".config.json"
|
||||||
|
CONFIG_CMAKE_PATH = MENUCONFIG_DIR / "config.cmake"
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class OptionItem:
|
||||||
|
key: str
|
||||||
|
title: str
|
||||||
|
description: str
|
||||||
|
default: bool
|
||||||
|
|
||||||
|
|
||||||
|
def normalize_bool(raw: object, default: bool) -> bool:
|
||||||
|
if isinstance(raw, bool):
|
||||||
|
return raw
|
||||||
|
if isinstance(raw, (int, float)):
|
||||||
|
return raw != 0
|
||||||
|
if isinstance(raw, str):
|
||||||
|
text = raw.strip().lower()
|
||||||
|
if text in {"1", "on", "true", "yes", "y"}:
|
||||||
|
return True
|
||||||
|
if text in {"0", "off", "false", "no", "n"}:
|
||||||
|
return False
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def sanitize_token(name: str) -> str:
|
||||||
|
token = re.sub(r"[^A-Za-z0-9]+", "_", name.strip().upper())
|
||||||
|
token = token.strip("_")
|
||||||
|
return token or "UNKNOWN"
|
||||||
|
|
||||||
|
|
||||||
|
def load_clks_options() -> List[OptionItem]:
|
||||||
|
if not CLKS_FEATURES_PATH.exists():
|
||||||
|
raise RuntimeError(f"missing CLKS feature file: {CLKS_FEATURES_PATH}")
|
||||||
|
|
||||||
|
raw = json.loads(CLKS_FEATURES_PATH.read_text(encoding="utf-8"))
|
||||||
|
if not isinstance(raw, dict) or "features" not in raw or not isinstance(raw["features"], list):
|
||||||
|
raise RuntimeError(f"invalid feature format in {CLKS_FEATURES_PATH}")
|
||||||
|
|
||||||
|
options: List[OptionItem] = []
|
||||||
|
for entry in raw["features"]:
|
||||||
|
if not isinstance(entry, dict):
|
||||||
|
continue
|
||||||
|
key = str(entry.get("key", "")).strip()
|
||||||
|
title = str(entry.get("title", key)).strip()
|
||||||
|
description = str(entry.get("description", "")).strip()
|
||||||
|
default = normalize_bool(entry.get("default", True), True)
|
||||||
|
if not key:
|
||||||
|
continue
|
||||||
|
options.append(OptionItem(key=key, title=title, description=description, default=default))
|
||||||
|
|
||||||
|
if not options:
|
||||||
|
raise RuntimeError(f"no CLKS feature options in {CLKS_FEATURES_PATH}")
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
|
def discover_user_apps() -> List[OptionItem]:
|
||||||
|
main_paths = sorted(APPS_DIR.glob("*_main.c"))
|
||||||
|
kmain_paths = sorted(APPS_DIR.glob("*_kmain.c"))
|
||||||
|
|
||||||
|
kmain_names = set()
|
||||||
|
for path in kmain_paths:
|
||||||
|
name = path.stem
|
||||||
|
if name.endswith("_kmain"):
|
||||||
|
kmain_names.add(name[:-6])
|
||||||
|
|
||||||
|
final_apps: List[Tuple[str, str]] = []
|
||||||
|
for path in main_paths:
|
||||||
|
name = path.stem
|
||||||
|
if not name.endswith("_main"):
|
||||||
|
continue
|
||||||
|
app = name[:-5]
|
||||||
|
if app in kmain_names:
|
||||||
|
continue
|
||||||
|
if app.endswith("drv"):
|
||||||
|
section = "driver"
|
||||||
|
elif app == "hello":
|
||||||
|
section = "root"
|
||||||
|
else:
|
||||||
|
section = "shell"
|
||||||
|
final_apps.append((app, section))
|
||||||
|
|
||||||
|
for app in sorted(kmain_names):
|
||||||
|
final_apps.append((app, "system"))
|
||||||
|
|
||||||
|
final_apps.sort(key=lambda item: (item[1], item[0]))
|
||||||
|
|
||||||
|
options: List[OptionItem] = []
|
||||||
|
for app, section in final_apps:
|
||||||
|
key = f"CLEONOS_USER_APP_{sanitize_token(app)}"
|
||||||
|
title = f"{app}.elf [{section}]"
|
||||||
|
description = f"Build and package user app '{app}' into ramdisk/{section}."
|
||||||
|
options.append(OptionItem(key=key, title=title, description=description, default=True))
|
||||||
|
|
||||||
|
return options
|
||||||
|
|
||||||
|
|
||||||
|
def load_previous_values() -> Dict[str, bool]:
|
||||||
|
if not CONFIG_JSON_PATH.exists():
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
raw = json.loads(CONFIG_JSON_PATH.read_text(encoding="utf-8"))
|
||||||
|
except Exception:
|
||||||
|
return {}
|
||||||
|
|
||||||
|
if not isinstance(raw, dict):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
out: Dict[str, bool] = {}
|
||||||
|
for key, value in raw.items():
|
||||||
|
if not isinstance(key, str):
|
||||||
|
continue
|
||||||
|
out[key] = normalize_bool(value, False)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def init_values(options: Iterable[OptionItem], previous: Dict[str, bool], use_defaults: bool) -> Dict[str, bool]:
|
||||||
|
values: Dict[str, bool] = {}
|
||||||
|
for item in options:
|
||||||
|
if not use_defaults and item.key in previous:
|
||||||
|
values[item.key] = previous[item.key]
|
||||||
|
else:
|
||||||
|
values[item.key] = item.default
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
def print_section(title: str, options: List[OptionItem], values: Dict[str, bool]) -> None:
|
||||||
|
print()
|
||||||
|
print(f"== {title} ==")
|
||||||
|
for idx, item in enumerate(options, start=1):
|
||||||
|
mark = "x" if values.get(item.key, item.default) else " "
|
||||||
|
print(f"{idx:3d}. [{mark}] {item.title}")
|
||||||
|
print("Commands: <number> toggle, a enable-all, n disable-all, i <n> info, b back")
|
||||||
|
|
||||||
|
|
||||||
|
def section_loop(title: str, options: List[OptionItem], values: Dict[str, bool]) -> None:
|
||||||
|
while True:
|
||||||
|
print_section(title, options, values)
|
||||||
|
raw = input(f"{title}> ").strip()
|
||||||
|
if not raw:
|
||||||
|
continue
|
||||||
|
lower = raw.lower()
|
||||||
|
|
||||||
|
if lower in {"b", "back", "q", "quit"}:
|
||||||
|
return
|
||||||
|
if lower in {"a", "all", "on"}:
|
||||||
|
for item in options:
|
||||||
|
values[item.key] = True
|
||||||
|
continue
|
||||||
|
if lower in {"n", "none", "off"}:
|
||||||
|
for item in options:
|
||||||
|
values[item.key] = False
|
||||||
|
continue
|
||||||
|
if lower.startswith("i "):
|
||||||
|
token = lower[2:].strip()
|
||||||
|
if token.isdigit():
|
||||||
|
idx = int(token)
|
||||||
|
if 1 <= idx <= len(options):
|
||||||
|
item = options[idx - 1]
|
||||||
|
state = "ON" if values.get(item.key, item.default) else "OFF"
|
||||||
|
print()
|
||||||
|
print(f"[{idx}] {item.title}")
|
||||||
|
print(f"key: {item.key}")
|
||||||
|
print(f"state: {state}")
|
||||||
|
print(f"desc: {item.description}")
|
||||||
|
continue
|
||||||
|
print("invalid info index")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if raw.isdigit():
|
||||||
|
idx = int(raw)
|
||||||
|
if 1 <= idx <= len(options):
|
||||||
|
item = options[idx - 1]
|
||||||
|
values[item.key] = not values.get(item.key, item.default)
|
||||||
|
else:
|
||||||
|
print("invalid index")
|
||||||
|
continue
|
||||||
|
|
||||||
|
print("unknown command")
|
||||||
|
|
||||||
|
|
||||||
|
def _safe_addnstr(stdscr, y: int, x: int, text: str, attr: int = 0) -> None:
|
||||||
|
h, w = stdscr.getmaxyx()
|
||||||
|
if y < 0 or y >= h or x >= w:
|
||||||
|
return
|
||||||
|
max_len = max(0, w - x - 1)
|
||||||
|
if max_len <= 0:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
stdscr.addnstr(y, x, text, max_len, attr)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _option_enabled(values: Dict[str, bool], item: OptionItem) -> bool:
|
||||||
|
return values.get(item.key, item.default)
|
||||||
|
|
||||||
|
|
||||||
|
def _set_all(values: Dict[str, bool], options: List[OptionItem], enabled: bool) -> None:
|
||||||
|
for item in options:
|
||||||
|
values[item.key] = enabled
|
||||||
|
|
||||||
|
|
||||||
|
def _run_ncurses_section(stdscr, title: str, options: List[OptionItem], values: Dict[str, bool]) -> None:
|
||||||
|
selected = 0
|
||||||
|
top = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
stdscr.erase()
|
||||||
|
h, w = stdscr.getmaxyx()
|
||||||
|
|
||||||
|
if h < 10 or w < 40:
|
||||||
|
_safe_addnstr(stdscr, 0, 0, "Terminal too small for menuconfig (need >= 40x10).", curses.A_BOLD)
|
||||||
|
_safe_addnstr(stdscr, 2, 0, "Resize terminal then press any key, or ESC to go back.")
|
||||||
|
key = stdscr.getch()
|
||||||
|
if key in (27,):
|
||||||
|
return
|
||||||
|
continue
|
||||||
|
|
||||||
|
list_top = 2
|
||||||
|
desc_area = 4
|
||||||
|
help_area = 2
|
||||||
|
list_bottom = h - (desc_area + help_area) - 1
|
||||||
|
visible = max(1, list_bottom - list_top + 1)
|
||||||
|
|
||||||
|
if selected < 0:
|
||||||
|
selected = 0
|
||||||
|
if selected >= len(options):
|
||||||
|
selected = max(0, len(options) - 1)
|
||||||
|
|
||||||
|
if selected < top:
|
||||||
|
top = selected
|
||||||
|
if selected >= top + visible:
|
||||||
|
top = selected - visible + 1
|
||||||
|
if top < 0:
|
||||||
|
top = 0
|
||||||
|
|
||||||
|
_safe_addnstr(stdscr, 0, 0, f"CLeonOS menuconfig / {title}", curses.A_REVERSE)
|
||||||
|
_safe_addnstr(stdscr, 1, 0, f"Items: {len(options)}")
|
||||||
|
|
||||||
|
for row in range(visible):
|
||||||
|
idx = top + row
|
||||||
|
if idx >= len(options):
|
||||||
|
break
|
||||||
|
item = options[idx]
|
||||||
|
mark = "x" if _option_enabled(values, item) else " "
|
||||||
|
line = f"{idx + 1:3d}. [{mark}] {item.title}"
|
||||||
|
attr = curses.A_REVERSE if idx == selected else 0
|
||||||
|
_safe_addnstr(stdscr, list_top + row, 0, line, attr)
|
||||||
|
|
||||||
|
if options:
|
||||||
|
cur = options[selected]
|
||||||
|
key_line = f"{cur.key}"
|
||||||
|
desc_line = cur.description
|
||||||
|
wrapped = textwrap.wrap(desc_line, max(10, w - 2))
|
||||||
|
_safe_addnstr(stdscr, list_bottom + 1, 0, key_line, curses.A_DIM)
|
||||||
|
for i, part in enumerate(wrapped[: max(1, desc_area - 1)]):
|
||||||
|
_safe_addnstr(stdscr, list_bottom + 2 + i, 0, part)
|
||||||
|
|
||||||
|
_safe_addnstr(
|
||||||
|
stdscr,
|
||||||
|
h - 2,
|
||||||
|
0,
|
||||||
|
"Arrows/jk move Space toggle a all-on n all-off PgUp/PgDn Home/End",
|
||||||
|
curses.A_DIM,
|
||||||
|
)
|
||||||
|
_safe_addnstr(stdscr, h - 1, 0, "Enter/ESC/q back", curses.A_DIM)
|
||||||
|
|
||||||
|
stdscr.refresh()
|
||||||
|
key = stdscr.getch()
|
||||||
|
|
||||||
|
if key in (27, ord("q"), ord("Q"), curses.KEY_LEFT, curses.KEY_ENTER, 10, 13):
|
||||||
|
return
|
||||||
|
if key in (curses.KEY_UP, ord("k"), ord("K")):
|
||||||
|
selected -= 1
|
||||||
|
continue
|
||||||
|
if key in (curses.KEY_DOWN, ord("j"), ord("J")):
|
||||||
|
selected += 1
|
||||||
|
continue
|
||||||
|
if key == curses.KEY_PPAGE:
|
||||||
|
selected -= visible
|
||||||
|
continue
|
||||||
|
if key == curses.KEY_NPAGE:
|
||||||
|
selected += visible
|
||||||
|
continue
|
||||||
|
if key == curses.KEY_HOME:
|
||||||
|
selected = 0
|
||||||
|
continue
|
||||||
|
if key == curses.KEY_END:
|
||||||
|
selected = max(0, len(options) - 1)
|
||||||
|
continue
|
||||||
|
if key == ord(" "):
|
||||||
|
if options:
|
||||||
|
item = options[selected]
|
||||||
|
values[item.key] = not _option_enabled(values, item)
|
||||||
|
continue
|
||||||
|
if key in (ord("a"), ord("A")):
|
||||||
|
_set_all(values, options, True)
|
||||||
|
continue
|
||||||
|
if key in (ord("n"), ord("N")):
|
||||||
|
_set_all(values, options, False)
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
def _run_ncurses_main(stdscr, clks_options: List[OptionItem], user_options: List[OptionItem], values: Dict[str, bool]) -> bool:
|
||||||
|
try:
|
||||||
|
curses.curs_set(0)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
stdscr.keypad(True)
|
||||||
|
selected = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
stdscr.erase()
|
||||||
|
h, _w = stdscr.getmaxyx()
|
||||||
|
|
||||||
|
clks_on = sum(1 for item in clks_options if _option_enabled(values, item))
|
||||||
|
user_on = sum(1 for item in user_options if _option_enabled(values, item))
|
||||||
|
|
||||||
|
items = [
|
||||||
|
f"CLKS features ({clks_on}/{len(clks_options)} enabled)",
|
||||||
|
f"User apps ({user_on}/{len(user_options)} enabled)",
|
||||||
|
"Save and Exit",
|
||||||
|
"Quit without Saving",
|
||||||
|
]
|
||||||
|
|
||||||
|
_safe_addnstr(stdscr, 0, 0, "CLeonOS menuconfig (ncurses)", curses.A_REVERSE)
|
||||||
|
_safe_addnstr(stdscr, 1, 0, "Enter open/select, Arrow/jk move, s save, q quit", curses.A_DIM)
|
||||||
|
|
||||||
|
base = 3
|
||||||
|
for i, text in enumerate(items):
|
||||||
|
attr = curses.A_REVERSE if i == selected else 0
|
||||||
|
_safe_addnstr(stdscr, base + i, 0, text, attr)
|
||||||
|
|
||||||
|
_safe_addnstr(stdscr, h - 1, 0, "Tip: Space toggles options inside sections.", curses.A_DIM)
|
||||||
|
stdscr.refresh()
|
||||||
|
|
||||||
|
key = stdscr.getch()
|
||||||
|
|
||||||
|
if key in (ord("q"), ord("Q"), 27):
|
||||||
|
return False
|
||||||
|
if key in (ord("s"), ord("S")):
|
||||||
|
return True
|
||||||
|
if key in (curses.KEY_UP, ord("k"), ord("K")):
|
||||||
|
selected = (selected - 1) % len(items)
|
||||||
|
continue
|
||||||
|
if key in (curses.KEY_DOWN, ord("j"), ord("J")):
|
||||||
|
selected = (selected + 1) % len(items)
|
||||||
|
continue
|
||||||
|
if key in (curses.KEY_ENTER, 10, 13):
|
||||||
|
if selected == 0:
|
||||||
|
_run_ncurses_section(stdscr, "CLKS", clks_options, values)
|
||||||
|
elif selected == 1:
|
||||||
|
_run_ncurses_section(stdscr, "USER", user_options, values)
|
||||||
|
elif selected == 2:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
def interactive_menu_ncurses(clks_options: List[OptionItem], user_options: List[OptionItem], values: Dict[str, bool]) -> bool:
|
||||||
|
if curses is None:
|
||||||
|
raise RuntimeError("python curses module unavailable (install python3-curses / ncurses)")
|
||||||
|
if "TERM" not in os.environ or not os.environ["TERM"]:
|
||||||
|
raise RuntimeError("TERM is not set; cannot start ncurses UI")
|
||||||
|
return bool(curses.wrapper(lambda stdscr: _run_ncurses_main(stdscr, clks_options, user_options, values)))
|
||||||
|
|
||||||
|
|
||||||
|
def write_outputs(all_values: Dict[str, bool], ordered_options: List[OptionItem]) -> None:
|
||||||
|
MENUCONFIG_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
ordered_keys = [item.key for item in ordered_options]
|
||||||
|
output_values: Dict[str, bool] = {key: all_values[key] for key in ordered_keys if key in all_values}
|
||||||
|
|
||||||
|
CONFIG_JSON_PATH.write_text(
|
||||||
|
json.dumps(output_values, ensure_ascii=True, indent=2, sort_keys=True) + "\n",
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
lines = [
|
||||||
|
"# Auto-generated by scripts/menuconfig.py",
|
||||||
|
"# Do not edit manually unless you know what you are doing.",
|
||||||
|
'set(CLEONOS_MENUCONFIG_LOADED ON CACHE BOOL "CLeonOS menuconfig loaded" FORCE)',
|
||||||
|
]
|
||||||
|
for item in ordered_options:
|
||||||
|
value = "ON" if all_values.get(item.key, item.default) else "OFF"
|
||||||
|
lines.append(f'set({item.key} {value} CACHE BOOL "{item.title}" FORCE)')
|
||||||
|
|
||||||
|
CONFIG_CMAKE_PATH.write_text("\n".join(lines) + "\n", encoding="utf-8")
|
||||||
|
|
||||||
|
|
||||||
|
def show_summary(clks_options: List[OptionItem], user_options: List[OptionItem], values: Dict[str, bool]) -> None:
|
||||||
|
clks_on = sum(1 for item in clks_options if values.get(item.key, item.default))
|
||||||
|
user_on = sum(1 for item in user_options if values.get(item.key, item.default))
|
||||||
|
print()
|
||||||
|
print("========== CLeonOS menuconfig ==========")
|
||||||
|
print(f"1) CLKS features : {clks_on}/{len(clks_options)} enabled")
|
||||||
|
print(f"2) User features : {user_on}/{len(user_options)} enabled")
|
||||||
|
print("s) Save and exit")
|
||||||
|
print("q) Quit without saving")
|
||||||
|
|
||||||
|
|
||||||
|
def interactive_menu(clks_options: List[OptionItem], user_options: List[OptionItem], values: Dict[str, bool]) -> bool:
|
||||||
|
while True:
|
||||||
|
show_summary(clks_options, user_options, values)
|
||||||
|
choice = input("Select> ").strip().lower()
|
||||||
|
if choice == "1":
|
||||||
|
section_loop("CLKS", clks_options, values)
|
||||||
|
continue
|
||||||
|
if choice == "2":
|
||||||
|
section_loop("USER", user_options, values)
|
||||||
|
continue
|
||||||
|
if choice in {"s", "save"}:
|
||||||
|
return True
|
||||||
|
if choice in {"q", "quit"}:
|
||||||
|
return False
|
||||||
|
print("unknown selection")
|
||||||
|
|
||||||
|
|
||||||
|
def parse_set_overrides(values: Dict[str, bool], kv_pairs: List[str]) -> None:
|
||||||
|
for pair in kv_pairs:
|
||||||
|
if "=" not in pair:
|
||||||
|
raise RuntimeError(f"invalid --set entry: {pair!r}, expected KEY=ON|OFF")
|
||||||
|
key, raw = pair.split("=", 1)
|
||||||
|
key = key.strip()
|
||||||
|
if not key:
|
||||||
|
raise RuntimeError(f"invalid --set entry: {pair!r}, empty key")
|
||||||
|
values[key] = normalize_bool(raw, False)
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args() -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(description="CLeonOS menuconfig")
|
||||||
|
parser.add_argument("--defaults", action="store_true", help="ignore previous .config and use defaults")
|
||||||
|
parser.add_argument("--non-interactive", action="store_true", help="save config without opening interactive menu")
|
||||||
|
parser.add_argument("--plain", action="store_true", help="use legacy plain-text menu instead of ncurses")
|
||||||
|
parser.add_argument(
|
||||||
|
"--set",
|
||||||
|
action="append",
|
||||||
|
default=[],
|
||||||
|
metavar="KEY=ON|OFF",
|
||||||
|
help="override one option before save (can be repeated)",
|
||||||
|
)
|
||||||
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
clks_options = load_clks_options()
|
||||||
|
user_options = discover_user_apps()
|
||||||
|
all_options = clks_options + user_options
|
||||||
|
|
||||||
|
previous = load_previous_values()
|
||||||
|
values = init_values(all_options, previous, use_defaults=args.defaults)
|
||||||
|
parse_set_overrides(values, args.set)
|
||||||
|
|
||||||
|
should_save = args.non_interactive
|
||||||
|
if not args.non_interactive:
|
||||||
|
if not sys.stdin.isatty():
|
||||||
|
raise RuntimeError("menuconfig requires interactive tty (or use --non-interactive)")
|
||||||
|
if args.plain:
|
||||||
|
should_save = interactive_menu(clks_options, user_options, values)
|
||||||
|
else:
|
||||||
|
should_save = interactive_menu_ncurses(clks_options, user_options, values)
|
||||||
|
|
||||||
|
if not should_save:
|
||||||
|
print("menuconfig: no changes saved")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
write_outputs(values, all_options)
|
||||||
|
print(f"menuconfig: wrote {CONFIG_JSON_PATH}")
|
||||||
|
print(f"menuconfig: wrote {CONFIG_CMAKE_PATH}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
raise SystemExit(main())
|
||||||
|
except RuntimeError as exc:
|
||||||
|
print(f"menuconfig error: {exc}", file=sys.stderr)
|
||||||
|
raise SystemExit(1)
|
||||||
Reference in New Issue
Block a user