zephyr/cmake/kconfig.cmake
Ulf Magnusson 73549ad852 scripts: kconfig: Add a Python menuconfig implementation
This commit adds a Kconfiglib-based menuconfig implementation, built
with the standard Python 'curses' module. A new 'pymenuconfig' target is
added to run it.

The C tools are kept for now. Removing them separately allows testing of
pymenuconfig alongside the C tools, and keeps changes small and focused.

A feature is planned for later that shows all symbols -- including those
that aren't currently visible -- along with a search and "jump to"
feature. Loading of arbitrary .config files will be supported later as
well (as opposed to always loading .config/KCONFIG_CONFIG). Those
features are all connected implementation-wise.

For Windows, the wheels at
https://www.lfd.uci.edu/~gohlke/pythonlibs/#curses provide the curses
implementation. They use the standard Python curses module
(_cursesmodule.c), linked against PDCurses.

Running 'python -VV' gives the Python version and bitness, to know which
wheel to install. User documentation will be added once the C tools are
removed and the 'pymenuconfig' target is moved over to 'menuconfig'.

The CMake parts are originally by Sebastian Bøe.

Description, taken from the menuconfig.py docstring:

    Overview
    ========

    A curses-based menuconfig implementation. The interface should feel
    familiar to people used to mconf ('make menuconfig').

    Supports the same keys as mconf, and also supports a set of
    keybindings inspired by Vi:

      J/K     : Down/Up
      L       : Enter menu/Toggle item
      H       : Leave menu
      Ctrl-D/U: Page Down/Page Down
      G/End   : Jump to end of list
      g/Home  : Jump to beginning of list

    The mconf feature where pressing a key jumps to a menu entry with
    that character in it in the current menu isn't supported. A search
    feature with a "jump to" function for jumping directly to a
    particular symbol regardless of where it is defined will be added
    later instead.

    Space and Enter are "smart" and try to do what you'd expect for the
    given menu entry.

    Running
    =======

    menuconfig.py can be run either as a standalone executable or by
    calling the menu.menuconfig() function with an existing Kconfig
    instance. The second option is a bit inflexible in that it will
    still load and save .config, etc.

    When run in standalone mode, the top-level Kconfig file to load can
    be passed as a command-line argument. With no argument, it defaults
    to "Kconfig".

    The KCONFIG_CONFIG environment variable specifies the .config file
    to load (if it exists) and save. If KCONFIG_CONFIG is unset,
    ".config" is used.

    $srctree is supported through Kconfiglib.

    Other features
    ==============

      - Seamless terminal resizing

      - No dependencies on *nix, as the 'curses' module is in the Python
        standard library

      - Unicode text entry

      - Improved information screen compared to mconf:

          * Expressions are split up by their top-level &&/|| operands
            to improve readability

          * Undefined symbols in expressions are pointed out

          * Menus and comments have information displays

          * Kconfig definitions are printed

    Limitations
    ===========

      - Python 3 only

        This is mostly due to Python 2 not having curses.get_wch(),
        which is needed for Unicode support.

      - Doesn't work out of the box on Windows

        Has been tested to work with the wheels provided at
        https://www.lfd.uci.edu/~gohlke/pythonlibs/#curses though.

Signed-off-by: Ulf Magnusson <ulfalizer@gmail.com>
2018-05-01 20:51:11 +02:00

168 lines
5.2 KiB
CMake

# Folders needed for conf/mconf files (kconfig has no method of redirecting all output files).
# conf/mconf needs to be run from a different directory because of: GH-3408
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/kconfig/include/generated)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/kconfig/include/config)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/include/generated)
set_ifndef(KCONFIG_ROOT ${ZEPHYR_BASE}/Kconfig)
set(BOARD_DEFCONFIG ${BOARD_DIR}/${BOARD}_defconfig)
set(DOTCONFIG ${PROJECT_BINARY_DIR}/.config)
if(CONF_FILE)
string(REPLACE " " ";" CONF_FILE_AS_LIST ${CONF_FILE})
endif()
set(ENV{srctree} ${ZEPHYR_BASE})
set(ENV{KERNELVERSION} ${PROJECT_VERSION})
set(ENV{KCONFIG_CONFIG} ${DOTCONFIG})
set(ENV{KCONFIG_AUTOHEADER} ${AUTOCONF_H})
set(kconfig_target_list
config
gconfig
menuconfig
pymenuconfig
oldconfig
xconfig
)
set(COMMAND_FOR_config ${KCONFIG_CONF} --oldaskconfig ${KCONFIG_ROOT})
set(COMMAND_FOR_gconfig gconf ${KCONFIG_ROOT})
set(COMMAND_FOR_menuconfig ${KCONFIG_MCONF} ${KCONFIG_ROOT})
set(COMMAND_FOR_pymenuconfig ${PYTHON_EXECUTABLE} ${ZEPHYR_BASE}/scripts/kconfig/menuconfig.py ${KCONFIG_ROOT})
set(COMMAND_FOR_oldconfig ${KCONFIG_CONF} --oldconfig ${KCONFIG_ROOT})
set(COMMAND_FOR_xconfig qconf ${KCONFIG_ROOT})
set(COMMAND_RUNS_ON_WIN_pymenuconfig 1)
# Set environment variables so that Kconfig can prune Kconfig source
# files for other architectures
set(ENV{ENV_VAR_ARCH} ${ARCH})
set(ENV{ENV_VAR_BOARD_DIR} ${BOARD_DIR})
foreach(kconfig_target ${kconfig_target_list})
if ((NOT WIN32) OR (DEFINED COMMAND_RUNS_ON_WIN_${kconfig_target}))
add_custom_target(
${kconfig_target}
${CMAKE_COMMAND} -E env
srctree=${ZEPHYR_BASE}
KERNELVERSION=${PROJECT_VERSION}
KCONFIG_CONFIG=${DOTCONFIG}
ENV_VAR_ARCH=$ENV{ENV_VAR_ARCH}
ENV_VAR_BOARD_DIR=$ENV{ENV_VAR_BOARD_DIR}
${COMMAND_FOR_${kconfig_target}}
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/kconfig
USES_TERMINAL
)
else()
add_custom_target(
${kconfig_target}
${CMAKE_COMMAND} -E echo
"========================================="
"Reconfiguration not supported on Windows."
"========================================="
)
endif()
endforeach()
# Bring in extra configuration files dropped in by the user or anyone else;
# make sure they are set at the end so we can override any other setting
file(GLOB config_files ${APPLICATION_BINARY_DIR}/*.conf)
list(SORT config_files)
set(
merge_config_files
${BOARD_DEFCONFIG}
${CONF_FILE_AS_LIST}
${OVERLAY_CONFIG}
${config_files}
)
# Create a list of absolute paths to the .config sources from
# merge_config_files, which is a mix of absolute and relative paths.
set(merge_config_files_with_absolute_paths "")
foreach(f ${merge_config_files})
if(IS_ABSOLUTE ${f})
set(path ${f})
else()
set(path ${APPLICATION_SOURCE_DIR}/${f})
endif()
list(APPEND merge_config_files_with_absolute_paths ${path})
endforeach()
foreach(f ${merge_config_files_with_absolute_paths})
if(NOT EXISTS ${f} OR IS_DIRECTORY ${f})
message(FATAL_ERROR "File not found: ${f}")
endif()
endforeach()
# Calculate a checksum of merge_config_files to determine if we need
# to re-generate .config
set(merge_config_files_checksum "")
foreach(f ${merge_config_files_with_absolute_paths})
file(MD5 ${f} checksum)
set(merge_config_files_checksum "${merge_config_files_checksum}${checksum}")
endforeach()
# Create a new .config if it does not exists, or if the checksum of
# the dependencies has changed
set(merge_config_files_checksum_file ${PROJECT_BINARY_DIR}/.cmake.dotconfig.checksum)
set(CREATE_NEW_DOTCONFIG "")
if(NOT EXISTS ${DOTCONFIG})
set(CREATE_NEW_DOTCONFIG 1)
else()
# Read out what the checksum was previously
file(READ
${merge_config_files_checksum_file}
merge_config_files_checksum_prev
)
set(CREATE_NEW_DOTCONFIG 1)
if(
${merge_config_files_checksum} STREQUAL
${merge_config_files_checksum_prev}
)
set(CREATE_NEW_DOTCONFIG 0)
endif()
endif()
if(CREATE_NEW_DOTCONFIG)
set(merge_fragments ${merge_config_files})
else()
set(merge_fragments ${DOTCONFIG})
endif()
execute_process(
COMMAND
${PYTHON_EXECUTABLE}
${ZEPHYR_BASE}/scripts/kconfig/kconfig.py
${KCONFIG_ROOT}
${PROJECT_BINARY_DIR}/.config
${PROJECT_BINARY_DIR}/include/generated/autoconf.h
${merge_fragments}
WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR}
# The working directory is set to the app dir such that the user
# can use relative paths in CONF_FILE, e.g. CONF_FILE=nrf5.conf
RESULT_VARIABLE ret
)
if(NOT "${ret}" STREQUAL "0")
message(FATAL_ERROR "command failed with return code: ${ret}")
endif()
if(CREATE_NEW_DOTCONFIG)
file(WRITE
${merge_config_files_checksum_file}
${merge_config_files_checksum}
)
endif()
# Force CMAKE configure when the configuration files changes.
foreach(merge_config_input ${merge_config_files} ${DOTCONFIG})
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${merge_config_input})
endforeach()
add_custom_target(config-sanitycheck DEPENDS ${DOTCONFIG})
# Parse the lines prefixed with CONFIG_ in the .config file from Kconfig
import_kconfig(${DOTCONFIG})