arch(cmake): add native Kconfig support to cmake (#7934)

This commit is contained in:
David Truan
2025-04-03 14:42:18 +02:00
committed by GitHub
parent e63f345fec
commit 371c21bbda
19 changed files with 1001 additions and 46 deletions

3
.gitignore vendored
View File

@@ -16,3 +16,6 @@ build/
.idea
*.bak
.vs/
.config*
config.h
defconfig

View File

@@ -50,6 +50,51 @@
"cacheVariables": {
"BUILD_SHARED_LIBS": "ON"
}
},
{
"name": "linux-kconfig",
"inherits": "linux-base",
"displayName": "Linux/Kconfig",
"description": "Setup WSL or native linux, using Kconfig",
"generator": "Ninja Multi-Config",
"binaryDir": "${sourceDir}/build/${presetName}",
"installDir": "${sourceDir}/build/${presetName}",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
},
"vendor": {
"microsoft.com/VisualStudioRemoteSettings/CMake/1.0": {
"sourceDir": "$env{HOME}/.vs/$ms{projectDirName}"
}
},
"cacheVariables": {
"BUILD_SHARED_LIBS": "ON",
"LV_USE_KCONFIG": "ON"
}
},
{
"name": "windows-kconfig",
"inherits": "_base",
"generator": "Visual Studio 17 2022",
"binaryDir": "${sourceDir}/build/${presetName}",
"installDir": "${sourceDir}/build/install/${presetName}",
"cacheVariables": {
"CMAKE_C_COMPILER": "cl.exe",
"CMAKE_CXX_COMPILER": "cl.exe",
"BUILD_SHARED_LIBS": "ON",
"LV_USE_KCONFIG": "ON"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"architecture": {
"value": "x64",
"strategy": "external"
}
}
],
"buildPresets": [

39
Kconfig
View File

@@ -147,17 +147,6 @@ menu "LVGL configuration"
bool "255: CUSTOM"
endchoice
config LV_USE_OS
int
default 0 if LV_OS_NONE
default 1 if LV_OS_PTHREAD
default 2 if LV_OS_FREERTOS
default 3 if LV_OS_CMSIS_RTOS2
default 4 if LV_OS_RTTHREAD
default 5 if LV_OS_WINDOWS
default 6 if LV_OS_MQX
default 255 if LV_OS_CUSTOM
config LV_OS_CUSTOM_INCLUDE
string "Custom OS include header"
default "stdint.h"
@@ -1077,6 +1066,34 @@ menu "LVGL configuration"
config LV_CALENDAR_WEEK_STARTS_MONDAY
bool "Calendar week starts monday"
depends on LV_USE_CALENDAR
menu "Days name configuration"
depends on LV_USE_CALENDAR
config LV_MONDAY_STR
string "Shortened string for Monday"
default "Mo"
config LV_TUESDAY_STR
string "Shortened string for Tuesday"
default "Tu"
config LV_WEDNESDAY_STR
string "Shortened string for Wednesday"
default "We"
config LV_THURSDAY_STR
string "Shortened string for Thursday"
default "Th"
config LV_FRIDAY_STR
string "Shortened string for Friday"
default "Fr"
config LV_SATURDAY_STR
string "Shortened string for Saturday"
default "Sa"
config LV_SUNDAY_STR
string "Shortened string for Sunday"
default "Su"
endmenu
config LV_USE_CALENDAR_HEADER_ARROW
bool "Use calendar header arrow"
depends on LV_USE_CALENDAR

View File

@@ -0,0 +1 @@
CONFIG_LV_CONF_MINIMAL=y

View File

@@ -19,7 +19,7 @@ main Makefile:
include $(LVGL_DIR)/$(LVGL_DIR_NAME)/lvgl.mk
For integration with CMake take a look this section of the
:ref:`Documentation <build_cmake>`.
:ref:`Documentation <integrating_lvgl_cmake>`.
Managed builds

View File

@@ -46,12 +46,6 @@ options. For example ``"-DLV_COLOR_DEPTH=32 -DLV_USE_BUTTON=1"``. Unset
options will get a default value which is the same as the content of
``lv_conf_template.h``.
LVGL also can be used via ``Kconfig`` and ``menuconfig``. You can use
``lv_conf.h`` together with Kconfig as well, but keep in mind that the values
from ``lv_conf.h`` or build settings (``-D...``) override the values
set in Kconfig. To ignore the configs from ``lv_conf.h`` simply remove
its content, or define :c:macro:`LV_CONF_SKIP`.
.. _configuration_settings:
@@ -91,5 +85,70 @@ For example:
Kconfig
*******
TODO: Add how to use LVGL with Kconfig.
LVGL can also be configured using Kconfig. For now, this is only available using cmake.
Under the hood, it uses ``kconfiglib`` Kconfig python port to be able to use it across different platforms.
The ``kconfiglib`` offers the python API and some CLI commands. Here is a list of some useful commands:
- ``menuconfig``: Opens a console menu interface to modify the configuration values.
- ``guiconfig`` (needs ``tkinter``): Opens a graphical interface to modify the configuration values.
- ``savedefconfig``: Saves the current .config as a defconfig, listing only non-default values.
- ``alldefconfig``: Creates a .config with all default values.
- ``genconfig``: Generates a C header from the config, following ``autoconf.h`` format.
Prerequisites
-------------
Install the prerequisites using ``scripts/install_prerequisites.sh/bat``.
Create the configuration (.config)
----------------------------------
At this point, the ``menuconfig`` command should be available:
.. code-block:: shell
cd <lvgl_repo>
menuconfig
Make changes to the config and exit using `Esc` or `Q`, and save your configuration. The ``.config`` file is
now created and list the configuration values.
Configuring with cmake
----------------------
Once the ``.config`` is created, run cmake with the ``-DLV_USE_KCONFIG=ON`` flag:
.. code-block:: shell
cd <lvgl_repo>
cmake -B build -DLV_USE_KCONFIG=ON
cmake --build build
To use a ``defconfig`` file, one can use the ``-DLV_DEFCONFIG_PATH=<path_to_defconfig>`` flag:
.. code-block:: shell
cd <lvgl_repo>
cmake -B build -DLV_USE_KCONFIG=ON -DLV_DEFCONFIG_PATH=<path_to_defconfig>
cmake --build build
Some defconfigs are available in ``configs/defconfigs`` folder.
Saving a defconfig
------------------
One can save a defconfig using the ``savedefconfig`` command:
.. code-block:: shell
cd <lvgl_repo>
menuconfig # make your changes to the default config
savedefconfig
cp defconfig configs/defconfigs/my_custom_defconfig # save it where you want
# Then use it to build LVGL
cmake -B build -DLV_USE_KCONFIG=ON -DLV_DEFCONFIG_PATH=configs/defconfigs/my_custom_defconfig
cmake --build build

View File

@@ -7,6 +7,12 @@ CMake
Overview
********
CMake is a cross-platform build system generator. It is used to easily integrate a project/library into another project.
It also offer the possibility to configure the build with different options, to enable or disable components, or to
integrate custom scripts executions during the configuration/build phase.
LVGL includes CMake natively, which means that one can use it to configure and build LVGL directly or integrate it into an higher
level cmake build.
This project uses CMakePresets to ensure an easy build.
Find out more on Cmake Presets here: https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html
@@ -19,22 +25,67 @@ You need to install
- CMake
- Ninja (for Linux builds). Be sure to Add ninja to your PATH!
- The prerequisites listed in ``scripts/install-prerequisites.sh/bat``
How to build this project using cmake
-------------------------------------
The recommended way to build this project is to use the provided CMakePresets.json. This file contains 2 configurations
- a windows (msvc) build using Visual Studio
- a linux (gcc) build using Ninja
Build with Command line
~~~~~~~~~~~~~~~~~~~~~~~
The simplest way to build LVGL using cmake is to use the command line calls:
.. code-block:: bash
# Method 1
cd <lvgl_repo>
mkdir build
cd build
cmake .. # Configure phase
cmake --build . # Build phase
# Method 2
cd <lvgl_repo>
cmake -B build # Configure phase
cmake --build build # build phase
More configurations will be added once available.
Build with cmake presets
~~~~~~~~~~~~~~~~~~~~~~~~
Another way to build this project is to use the provided CMakePresets.json or passing option using the command line.
The CMakePresets.json file describes some cmake configurations and build phase. It is a way to quickly use a set of
predefined cmake options.
For now, these configuration presets are available:
- ``windows-base``: A Windows configuration, using VS MSVC. Uses ``lv_conf.h`` as the configuration system.
- ``windows-kconfig``: A Windows configuration, using VS MSVC. Uses Kconfig as the configuration system.
- ``linux-base``: A Linux configuration, using Ninja and GCC. Uses ``lv_conf.h`` as the configuration system.
- ``linux-kconfig``: A Linux configuration, using Ninja and GCC. Uses Kconfig as the configuration system.
And these build presets:
- ``windows-base_dbg``: Windows Debug build.
- ``windows-base_rel``: Windows Release build.
- ``linux-base_dbg``: Linux Debug build.
- ``linux-base_rel``: Linux Release build.
Here is how to build using the presets:
.. code-block:: bash
cmake --preset windows-base
cmake --build --preset windows-base_dbg
ctest --preset windows-base_dbg
Build with IDE
--------------
~~~~~~~~~~~~~~
The recommend way for consuming CMakePresets is a CMakePresets aware IDE such as
@@ -47,7 +98,7 @@ Simply load this project into your IDE and select your desired preset and you ar
Build with CMake GUI
--------------------
~~~~~~~~~~~~~~~~~~~~
Open this project with CMake GUI and select your desired preset. When hitting the generate button,
CMake will create solution files (for VS) or Ninja Files (for Linux Ninja Build)
@@ -61,20 +112,75 @@ The following targets are available.
All optional targets can be disabled by setting the proper cache variables.
If you use cmake to install lvgl 3 folders will be created.
If you use cmake to install lvgl, 3 folders will be created.
- include/lvgl (contains all public headers)
- bin (contains all binaries (\*.dll))
- lib (contains all precompiled source files (\*.lib))
Build with Command line
-----------------------
.. _integrating_lvgl_cmake:
You can also build your project using the command line. Run the following commands
Integrate LVGL to your project using cmake
------------------------------------------
- ``cmake --preset windows-base``
- ``cmake --build --preset windows-base_dbg``
- ``ctest --preset windows-base_dbg``
The LVGL cmake system is made to be integrated into higher level projects. To do so, simply add this to your
project's ``CMakeLists.txt``.
This snippet adds LVGL and needs an ``lv_conf.h`` file present next to the lvgl folder:
.. code-block:: cmake
set(LV_CONF_INCLUDE_SIMPLE OFF)
add_subdirectory(lvgl)
This snippet adds LVGL and needs an ``lv_conf.h`` file present in lvgl/src folder:
.. code-block:: cmake
add_subdirectory(lvgl)
This snippet adds LVGL and specify a ``lv_conf.h`` to use:
.. code-block:: cmake
set(LV_CONF_PATH path/to/my_lv_conf.h)
add_subdirectory(lvgl)
This snippet adds LVGL and specify to use Kconfig as the configuration system:
.. code-block:: cmake
set(LV_USE_KCONFIG ON)
add_subdirectory(lvgl)
This snippet adds LVGL and specify to use Kconfig as the configuration system and to use a specific defconfig:
.. code-block:: cmake
set(LV_USE_KCONFIG ON)
set(LV_DEFCONFIG_PATH path/to/my_defconfig)
add_subdirectory(lvgl)
To disable the demo/example set these options:
.. code-block:: cmake
set(LV_CONF_BUILD_DISABLE_EXAMPLES ON)
set(LV_CONF_BUILD_DISABLE_DEMOS ON)
add_subdirectory(lvgl)
These cmake options are available to configure LVGL:
- ``LV_CONF_PATH`` (STRING): Specify a custom path for ``lv_conf.h``.
- ``LV_CONF_INCLUDE_SIMPLE`` (BOOLEAN): Use ``#include "lv_conf.h"`` instead of ``#include "../../lv_conf.h"``
- ``LV_USE_KCONFIG`` (BOOLEAN): Use Kconfig as the configuration source.
- ``LV_DEFCONFIG_PATH`` (STRING): Specify to use a defconfig file instead of the current .config in a Kconfig setup.
- ``LV_CONF_BUILD_DISABLE_EXAMPLES`` (BOOLEAN): Disable building the examples if set.
- ``LV_CONF_BUILD_DISABLE_DEMOS`` (BOOLEAN): Disable building the demos if set.
- ``LV_CONF_BUILD_DISABLE_THORVG_INTERNAL``: Disable the internal compilation of ThorVG.

View File

@@ -8,6 +8,13 @@ option(LV_LVGL_H_INCLUDE_SIMPLE
option(LV_CONF_INCLUDE_SIMPLE
"Use #include \"lv_conf.h\" instead of #include \"../../lv_conf.h\"" ON)
set(LV_KCONFIG_IGNORE "Don't use Kconfig. Kconfig is not used by default." ON)
find_package(Python REQUIRED)
option(LV_CONF_SKIP "Skip including lv_conf.h during configuration" OFF)
option(LV_USE_KCONFIG "Use Kconfig to configure LVGL" OFF)
# Option LV_CONF_PATH, which should be the path for lv_conf.h
# If set parent path LV_CONF_DIR is added to includes
if( LV_CONF_PATH )
@@ -27,12 +34,63 @@ file(GLOB_RECURSE THORVG_SOURCES ${LVGL_ROOT_DIR}/src/libs/thorvg/*.cpp ${LVGL_R
add_library(lvgl ${SOURCES})
add_library(lvgl::lvgl ALIAS lvgl)
if ( LV_USE_KCONFIG )
# kconfig.cmake will generate the .config
# and autoconf.h, which will be used by lv_conf_kconfig.h
include(${CMAKE_CURRENT_LIST_DIR}/kconfig.cmake)
set(LV_KCONFIG_IGNORE OFF)
# Set the flag to specify we are using kconfig, needed for the
# generate_cmake_variables.py script
set(KCONFIG_FLAG --kconfig)
# If using Kconfig, we need to define additional definitions
set(PCPP_ADDITIONAL_DEFS "--defs" "LV_CONF_SKIP" "LV_CONF_KCONFIG_EXTERNAL_INCLUDE=\"${LV_CONF_KCONFIG_EXTERNAL_INCLUDE}\"")
else()
if (LV_CONF_PATH)
set(PCPP_ADDITIONAL_DEFS "--defs" "LV_CONF_PATH=\"${LV_CONF_PATH}\"")
endif()
endif()
target_compile_definitions(
lvgl PUBLIC $<$<BOOL:${LV_LVGL_H_INCLUDE_SIMPLE}>:LV_LVGL_H_INCLUDE_SIMPLE>
$<$<BOOL:${LV_CONF_INCLUDE_SIMPLE}>:LV_CONF_INCLUDE_SIMPLE>)
$<$<BOOL:${LV_CONF_INCLUDE_SIMPLE}>:LV_CONF_INCLUDE_SIMPLE>
$<$<BOOL:${LV_KCONFIG_IGNORE}>:LV_KCONFIG_IGNORE>
$<$<BOOL:${LV_CONF_SKIP}>:LV_CONF_SKIP>
)
# Use the portable pcpp to preprocess lv_conf_internal.h
execute_process(
COMMAND ${Python_EXECUTABLE} ${LVGL_ROOT_DIR}/scripts/preprocess_lv_conf_internal.py
--input ${LVGL_ROOT_DIR}/src/lv_conf_internal.h
--tmp_file ${CMAKE_CURRENT_BINARY_DIR}/tmp.h
--output ${CMAKE_CURRENT_BINARY_DIR}/lv_conf_expanded.h
${PCPP_ADDITIONAL_DEFS}
--include ${LVGL_ROOT_DIR} ${LVGL_ROOT_DIR}/.. ${LVGL_ROOT_DIR}/src ${LV_CONF_DIR}
RESULT_VARIABLE ret
)
if(NOT "${ret}" STREQUAL "0")
message(FATAL_ERROR "preprocess_lv_conf_internal.py failed with return code: ${ret}")
endif()
# Convert the expanded lv_conf_expanded.h to cmake variables
execute_process(
COMMAND ${Python_EXECUTABLE}
${LVGL_ROOT_DIR}/scripts/generate_cmake_variables.py
--input ${CMAKE_CURRENT_BINARY_DIR}/lv_conf_expanded.h
--output ${CMAKE_CURRENT_BINARY_DIR}/lv_conf.cmake
${KCONFIG_FLAG}
RESULT_VARIABLE ret
)
if(NOT "${ret}" STREQUAL "0")
message(FATAL_ERROR "generate_cmake_variables.py command failed with return code: ${ret}")
endif()
# This will set all CONFIG_LV_USE_* variables in cmake
include(${CMAKE_BINARY_DIR}/lv_conf.cmake)
# Add definition of LV_CONF_PATH only if needed
if(LV_CONF_PATH)
# Do not redefine it if already defined in tests/CMakeLists.txt
if(LV_CONF_PATH AND NOT LV_BUILD_TEST)
target_compile_definitions(lvgl PUBLIC LV_CONF_PATH=${LV_CONF_PATH})
endif()

View File

@@ -0,0 +1,42 @@
set(PROJECT_ROOT ${CMAKE_CURRENT_SOURCE_DIR})
set(KCONFIG_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/Kconfig)
set(AUTOCONF_H ${CMAKE_CURRENT_BINARY_DIR}/autoconf.h)
set(OUTPUT_DOTCONFIG ${CMAKE_CURRENT_SOURCE_DIR}/.config)
set(KCONFIG_LIST_OUT ${CMAKE_CURRENT_BINARY_DIR}/kconfig_list)
set(AUTO_CONF_DIR ${CMAKE_CURRENT_BINARY_DIR})
# Check if the user want to use a defconfig, using the -DLV_DEFCONFIG_PATH option
if(LV_DEFCONFIG_PATH)
set(DOTCONFIG ${LV_DEFCONFIG_PATH})
else()
set(DOTCONFIG ${CMAKE_CURRENT_SOURCE_DIR}/.config)
endif()
execute_process(
COMMAND ${Python_EXECUTABLE}
${LVGL_ROOT_DIR}/scripts/kconfig.py
${LVGL_ROOT_DIR}/Kconfig
${OUTPUT_DOTCONFIG}
${AUTOCONF_H}
${KCONFIG_LIST_OUT}
${DOTCONFIG}
WORKING_DIRECTORY ${LVGL_ROOT_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()
# Re-configure (Re-execute all CMakeLists.txt code) when autoconf.h changes
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${AUTOCONF_H})
# Set compile definitions for lvgl build
add_compile_definitions(LV_CONF_KCONFIG_EXTERNAL_INCLUDE="${AUTOCONF_H}")
# Set the variable that can be used by the CMakeLists.txt including this file
set(LV_CONF_KCONFIG_EXTERNAL_INCLUDE "${AUTOCONF_H}")
# Ensure LV_DEFCONFIG_PATH is not set in the path, to be able to call it without
# the -DLV_DEFCONFIG_PATH after the first configuration, and to work with the .config
unset(LV_DEFCONFIG_PATH CACHE)

View File

@@ -1247,7 +1247,11 @@
#define LV_USE_ST7796 0
#define LV_USE_ILI9341 0
#define LV_USE_GENERIC_MIPI (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341)
#if (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341)
#define LV_USE_GENERIC_MIPI 1
#else
#define LV_USE_GENERIC_MIPI 0
#endif
/** Driver for Renesas GLCD */
#define LV_USE_RENESAS_GLCDC 0

View File

@@ -0,0 +1,109 @@
#!/usr/bin/env python3
#
# Generate the cmake variables CONFIG_LV_USE_* from the
# preprocessed lv_conf_internal.h
#
# Author: David TRUAN (david.truan@edgemtech.ch)
#
import os
import argparse
def fatal(msg):
print()
print("ERROR! " + msg)
exit(1)
def get_args():
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, description=""
"Convert the expanded lv_conf_internal.h to cmake variables."
"It converts all LV_USE_* configurations."
)
parser.add_argument('--input', type=str, required=True, nargs='?',
help='Path of the macro expanded lv_conf_internal.h, which should be generated during a cmake build')
parser.add_argument('--output', type=str, required=True, nargs='?',
help='Path of the output file, where the cmake variables declaration will be written (ex: build/lv_conf.cmake)')
parser.add_argument("--kconfig", action="store_true", help="Enable kconfig flag")
args = parser.parse_args()
# The input must exist
if not os.path.exists(args.input):
fatal(f"Input {args.input} not found")
return args
def generate_cmake_variables(path_input: str, path_output: str, kconfig: bool):
fin = open(path_input)
fout = open(path_output, "w", newline='')
# If we use Kconfig, we must check the CONFIG_LV_USE_* defines
if kconfig:
CONFIG_PATTERN="#define CONFIG_LV_USE"
CONFIG_PREFIX=""
# Otherwise check the LV_USE_* defines
else:
CONFIG_PATTERN="#define LV_USE"
CONFIG_PREFIX="CONFIG_"
# Using the expanded lv_conf_internal, we don't have to deal with regexp,
# as all the #define will be aligned on the left with a single space before the value
for line in fin.read().splitlines():
# Treat the LV_USE_STDLIB_* configs in a special way, as we need
# to convert the define to full config with 1 value when enabled
if line.startswith(f'{CONFIG_PATTERN}_STDLIB'):
parts = line.split()
if len(parts) < 3:
continue
name = parts[1]
value = parts[2].strip()
type = value.split("LV_STDLIB_")[1]
name = name.replace("STDLIB", type)
fout.write(f'set({CONFIG_PREFIX}{name} 1)\n')
# Treat the LV_USE_OS config in a special way, as we need
# to convert the define to full config with 1 value when enabled
if line.startswith(f'{CONFIG_PATTERN}_OS'):
parts = line.split()
if len(parts) < 3:
continue
name = parts[1]
value = parts[2].strip()
type = value.split("LV_OS")[1]
name += type
fout.write(f'set({CONFIG_PREFIX}{name} 1)\n')
# For the rest of config, simply add CONFIG_ and write
# all LV_USE_* configs, as these are the one needed in cmake
elif line.startswith(f'{CONFIG_PATTERN}'):
parts = line.split()
if len(parts) < 3:
continue
name = parts[1]
value = parts[2].strip()
fout.write(f'set({CONFIG_PREFIX}{name} {value})\n')
if __name__ == '__main__':
args = get_args()
generate_cmake_variables(args.input, args.output, args.kconfig)

View File

@@ -1,4 +1,4 @@
vcpkg install vcpkg-tool-ninja libpng freetype opengl glfw3 glew
if %errorlevel% neq 0 exit /b %errorlevel%
pip install pypng lz4 kconfiglib
pip install pypng lz4 kconfiglib pcpp
if %errorlevel% neq 0 exit /b %errorlevel%

View File

@@ -14,4 +14,4 @@ sudo apt install gcc gcc-multilib g++-multilib ninja-build \
ruby-full gcovr cmake python3 libinput-dev libxkbcommon-dev \
libdrm-dev pkg-config wayland-protocols libwayland-dev libwayland-bin \
libwayland-dev:i386 libxkbcommon-dev:i386 libudev-dev
pip3 install pypng lz4 kconfiglib
pip3 install pypng lz4 kconfiglib pcpp

346
scripts/kconfig.py Executable file
View File

@@ -0,0 +1,346 @@
#!/usr/bin/env python3
# Originally modified from:
# https://github.com/zephyrproject-rtos/zephyr/blob/main/scripts/kconfig/kconfig.py
# SPDX-License-Identifier: ISC
# Writes/updates the lvgl/.config configuration file by merging configuration
# files passed as arguments
#
# When fragments haven't changed, lvgl/.config is both the input and the
# output, which just updates it. This is handled in the CMake files.
#
# Also does various checks (most via Kconfiglib warnings).
import argparse
import os
import re
import sys
import textwrap
# Lvgl doesn't use tristate symbols. They're supported here just to make the
# script a bit more generic.
from kconfiglib import (
Kconfig,
split_expr,
expr_value,
expr_str,
BOOL,
TRISTATE,
TRI_TO_STR,
AND,
OR,
)
def main():
print(sys.argv)
args = parse_args()
print("Parsing " + args.kconfig_file)
kconf = Kconfig(args.kconfig_file, warn_to_stderr=False, suppress_traceback=True)
if args.handwritten_input_configs:
# Warn for assignments to undefined symbols, but only for handwritten
# fragments, to avoid warnings-turned-errors when using an old
# configuration file together with updated Kconfig files
kconf.warn_assign_undef = True
# prj.conf may override settings from the board configuration, so
# disable warnings about symbols being assigned more than once
kconf.warn_assign_override = False
kconf.warn_assign_redun = False
if args.forced_input_configs:
# Do not warn on a redundant config.
# The reason is that a regular .config will be followed by the forced
# config which under normal circumstances should be identical to the
# configured setting.
# Only if user has modified to a value that gets overruled by the forced
# a warning shall be issued.
kconf.warn_assign_redun = False
# Load files
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))
if args.handwritten_input_configs:
# Check that there are no assignments to promptless symbols, which
# have no effect.
#
# This only makes sense when loading handwritten fragments and not when
# loading lvgl/.config, because lvgl/.config is configuration
# output and also assigns promptless symbols.
check_no_promptless_assign(kconf)
# Print warnings for symbols that didn't get the assigned value. Only
# do this for handwritten input too, to avoid likely unhelpful warnings
# when using an old configuration and updating Kconfig files.
check_assigned_sym_values(kconf)
check_assigned_choice_values(kconf)
if kconf.syms.get("WARN_DEPRECATED", kconf.y).tri_value == 2:
check_deprecated(kconf)
if kconf.syms.get("WARN_EXPERIMENTAL", kconf.y).tri_value == 2:
check_experimental(kconf)
# Hack: Force all symbols to be evaluated, to catch warnings generated
# during evaluation. Wait till the end to write the actual output files, so
# that we don't generate any output if there are warnings-turned-errors.
#
# Kconfiglib caches calculated symbol values internally, so this is still
# fast.
kconf.write_config(os.devnull)
warn_only = r"warning:.*set more than once."
if kconf.warnings:
if args.forced_input_configs:
error_out = False
else:
error_out = True
# Put a blank line between warnings to make them easier to read
for warning in kconf.warnings:
print("\n" + warning, file=sys.stderr)
if not error_out and not re.search(warn_only, warning):
# The warning is not a warn_only, fail the Kconfig.
error_out = True
# Turn all warnings into errors, so that e.g. assignments to undefined
# Kconfig symbols become errors.
#
# A warning is generated by this script whenever a symbol gets a
# different value than the one it was assigned. Keep that one as just a
# warning for now.
if error_out:
err("Aborting due to Kconfig warnings")
# Write the merged configuration and the C header
print(kconf.write_config(args.config_out))
print(kconf.write_autoconf(args.header_out))
# Write the list of parsed Kconfig files to a file
write_kconfig_filenames(kconf, args.kconfig_list_out)
def check_no_promptless_assign(kconf):
# Checks that no promptless symbols are assigned
for sym in kconf.unique_defined_syms:
if sym.user_value is not None and promptless(sym):
err(
f"""\
{sym.name_and_loc} is assigned in a configuration file, but is not directly
user-configurable (has no prompt). It gets its value indirectly from other
symbols. """
+ SYM_INFO_HINT.format(sym)
)
def check_assigned_sym_values(kconf):
# Verifies that the values assigned to symbols "took" (matches the value
# the symbols actually got), printing warnings otherwise. Choice symbols
# are checked separately, in check_assigned_choice_values().
for sym in kconf.unique_defined_syms:
if sym.choice:
continue
user_value = sym.user_value
if user_value is None:
continue
# Tristate values are represented as 0, 1, 2. Having them as "n", "m",
# "y" is more convenient here, so convert.
if sym.type in (BOOL, TRISTATE):
user_value = TRI_TO_STR[user_value]
if user_value != sym.str_value:
msg = (
f"{sym.name_and_loc} was assigned the value '{user_value}'"
f" but got the value '{sym.str_value}'. "
)
# List any unsatisfied 'depends on' dependencies in the warning
mdeps = missing_deps(sym)
if mdeps:
expr_strs = []
for expr in mdeps:
estr = expr_str(expr)
if isinstance(expr, tuple):
# Add () around dependencies that aren't plain symbols.
# Gives '(FOO || BAR) (=n)' instead of
# 'FOO || BAR (=n)', which might be clearer.
estr = f"({estr})"
expr_strs.append(f"{estr} " f"(={TRI_TO_STR[expr_value(expr)]})")
msg += (
"Check these unsatisfied dependencies: "
+ ", ".join(expr_strs)
+ ". "
)
warn(msg + SYM_INFO_HINT.format(sym))
def missing_deps(sym):
# check_assigned_sym_values() helper for finding unsatisfied dependencies.
#
# Given direct dependencies
#
# depends on <expr> && <expr> && ... && <expr>
#
# on 'sym' (which can also come from e.g. a surrounding 'if'), returns a
# list of all <expr>s with a value less than the value 'sym' was assigned
# ("less" instead of "not equal" just to be general and handle tristates,
# even though lvgl doesn't use them).
#
# For string/int/hex symbols, just looks for <expr> = n.
#
# Note that <expr>s can be something more complicated than just a symbol,
# like 'FOO || BAR' or 'FOO = "string"'.
deps = split_expr(sym.direct_dep, AND)
if sym.type in (BOOL, TRISTATE):
return [dep for dep in deps if expr_value(dep) < sym.user_value]
# string/int/hex
return [dep for dep in deps if expr_value(dep) == 0]
def check_assigned_choice_values(kconf):
# Verifies that any choice symbols that were selected (by setting them to
# y) ended up as the selection, printing warnings otherwise.
#
# We check choice symbols separately to avoid warnings when two different
# choice symbols within the same choice are set to y. This might happen if
# a choice selection from a board defconfig is overridden in a prj.conf,
# for example. The last choice symbol set to y becomes the selection (and
# all other choice symbols get the value n).
#
# Without special-casing choices, we'd detect that the first symbol set to
# y ended up as n, and print a spurious warning.
for choice in kconf.unique_choices:
if choice.user_selection and choice.user_selection is not choice.selection:
warn(
f"""\
The choice symbol {choice.user_selection.name_and_loc} was selected (set =y),
but {choice.selection.name_and_loc if choice.selection else "no symbol"} ended
up as the choice selection. """
+ SYM_INFO_HINT.format(choice.user_selection)
)
# Hint on where to find symbol information. Used like
# SYM_INFO_HINT.format(sym).
SYM_INFO_HINT = """\
See https://docs.lvgl.io/master/intro/add-lvgl-to-your-project/configuration.html
look up {0.name} in the menuconfig/guiconfig interface. The Application
Development Primer, Setting Configuration Values, and Kconfig - Tips and Best
Practices sections of the manual might be helpful too.\
"""
def check_deprecated(kconf):
deprecated = kconf.syms.get("DEPRECATED")
dep_expr = kconf.n if deprecated is None else deprecated.rev_dep
if dep_expr is not kconf.n:
selectors = [s for s in split_expr(dep_expr, OR) if expr_value(s) == 2]
for selector in selectors:
selector_name = split_expr(selector, AND)[0].name
warn(f"Deprecated symbol {selector_name} is enabled.")
def check_experimental(kconf):
experimental = kconf.syms.get("EXPERIMENTAL")
dep_expr = kconf.n if experimental is None else experimental.rev_dep
if dep_expr is not kconf.n:
selectors = [s for s in split_expr(dep_expr, OR) if expr_value(s) == 2]
for selector in selectors:
selector_name = split_expr(selector, AND)[0].name
warn(f"Experimental symbol {selector_name} is enabled.")
def promptless(sym):
# Returns True if 'sym' has no prompt. Since the symbol might be defined in
# multiple locations, we need to check all locations.
return not any(node.prompt for node in sym.nodes)
def write_kconfig_filenames(kconf, kconfig_list_path):
# Writes a sorted list with the absolute paths of all parsed Kconfig files
# to 'kconfig_list_path'. The paths are realpath()'d, and duplicates are
# removed. This file is used by CMake to look for changed Kconfig files. It
# needs to be deterministic.
with open(kconfig_list_path, "w") as out:
for path in sorted(
{
os.path.realpath(os.path.join(kconf.srctree, path))
for path in kconf.kconfig_filenames
}
):
print(path, file=out)
def parse_args():
parser = argparse.ArgumentParser(allow_abbrev=False)
parser.add_argument(
"--handwritten-input-configs",
action="store_true",
help="Assume the input configuration fragments are "
"handwritten fragments and do additional checks "
"on them, like no promptless symbols being "
"assigned",
)
parser.add_argument(
"--forced-input-configs",
action="store_true",
help="Indicate the input configuration files are "
"followed by an forced configuration file."
"The forced configuration is used to forcefully "
"set specific configuration settings to a "
"pre-defined value and thereby remove any user "
" adjustments.",
)
parser.add_argument("kconfig_file", help="Top-level Kconfig file")
parser.add_argument("config_out", help="Output configuration file")
parser.add_argument("header_out", help="Output header file")
parser.add_argument(
"kconfig_list_out", help="Output file for list of parsed Kconfig files"
)
parser.add_argument(
"configs_in",
nargs="+",
help="Input configuration fragments. Will be merged " "together.",
)
return parser.parse_args()
def warn(msg):
# Use a large fill() width to try to avoid linebreaks in the symbol
# reference link, and add some extra newlines to set the message off from
# surrounding text (this usually gets printed as part of spammy CMake
# output)
print("\n" + textwrap.fill("warning: " + msg, 100) + "\n", file=sys.stderr)
def err(msg):
sys.exit("\n" + textwrap.fill("error: " + msg, 100) + "\n")
if __name__ == "__main__":
main()

View File

@@ -214,12 +214,21 @@ LV_EXPORT_CONST_INT(LV_DRAW_BUF_ALIGN);
#define LV_USE_MEM_MONITOR 0
#endif /*LV_USE_SYSMON*/
#ifndef LV_USE_LZ4
#define LV_USE_LZ4 (LV_USE_LZ4_INTERNAL || LV_USE_LZ4_EXTERNAL)
#if (LV_USE_LZ4_INTERNAL || LV_USE_LZ4_EXTERNAL)
#define LV_USE_LZ4 1
#else
#define LV_USE_LZ4 0
#endif
#endif
#ifndef LV_USE_THORVG
#define LV_USE_THORVG (LV_USE_THORVG_INTERNAL || LV_USE_THORVG_EXTERNAL)
#if (LV_USE_THORVG_INTERNAL || LV_USE_THORVG_EXTERNAL)
#define LV_USE_THORVG 1
#else
#define LV_USE_THORVG 0
#endif
#endif
#if LV_USE_OS

View File

@@ -0,0 +1,96 @@
#!/usr/bin/env python3
#
# Preprocess the lv_conf_internal.h to generate a header file
# containing the evaluated definitions. This output will be used to
# generate the cmake variables
#
# Author: David TRUAN (david.truan@edgemtech.ch)
#
import subprocess
import os
import argparse
import re
def get_args():
parser = argparse.ArgumentParser(description="Preprocess a C header file and remove indentation.")
parser.add_argument("--input", help="Path to the input C header file", required=True)
parser.add_argument("--tmp_file", help="Path to save the preprocessed output", required=True)
parser.add_argument("--output", help="Path to save the cleaned output with removed indentation", required=True)
parser.add_argument(
"--defs",
nargs='+',
default=[],
help="Definitions to be added to pcpp (flag -D)"
)
parser.add_argument(
"--include",
nargs='+',
default=[],
help="Paths to include directories for the preprocessor (flag -I)"
)
return parser.parse_args()
def preprocess_file(input_file, tmp_file, output_file, include_dirs, defs):
try:
pcpp_command = ["pcpp", "-o", tmp_file, "--passthru-defines", "--line-directive=", input_file]
for include_path in include_dirs:
pcpp_command.append(f"-I{include_path}")
for definition in defs:
pcpp_command.append(f"-D{definition}")
subprocess.run(pcpp_command, check=True)
print(f"Preprocessing completed. Output saved to {tmp_file}")
except subprocess.CalledProcessError as e:
print(f"Error during preprocessing: {e}")
exit(1)
def remove_indentation(tmp_file, output_file):
try:
with open(tmp_file, "r") as f:
lines = f.readlines()
clean_lines = []
for line in lines:
stripped = line.lstrip()
# Remove extra spaces after #
if stripped.startswith("#"):
stripped = re.sub(r"^#\s+", "#", stripped)
clean_lines.append(stripped)
with open(output_file, "w") as f:
f.writelines(clean_lines)
print(f"Indentation removed. Cleaned output saved to {output_file}")
os.remove(tmp_file)
print(f"Temporary preprocessed file {tmp_file} removed.")
except Exception as e:
print(f"Error during indentation removal: {e}")
exit(1)
def main():
args = get_args()
preprocess_file(args.input, args.tmp_file, args.output, args.include, args.defs)
remove_indentation(args.tmp_file, args.output)
if __name__ == "__main__":
main()

View File

@@ -3994,11 +3994,25 @@
#endif
#endif
#ifndef LV_USE_GENERIC_MIPI
#ifdef CONFIG_LV_USE_GENERIC_MIPI
#define LV_USE_GENERIC_MIPI CONFIG_LV_USE_GENERIC_MIPI
#else
#define LV_USE_GENERIC_MIPI (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341)
#if (LV_USE_ST7735 | LV_USE_ST7789 | LV_USE_ST7796 | LV_USE_ILI9341)
#ifndef LV_USE_GENERIC_MIPI
#ifdef LV_KCONFIG_PRESENT
#ifdef CONFIG_LV_USE_GENERIC_MIPI
#define LV_USE_GENERIC_MIPI CONFIG_LV_USE_GENERIC_MIPI
#else
#define LV_USE_GENERIC_MIPI 0
#endif
#else
#define LV_USE_GENERIC_MIPI 1
#endif
#endif
#else
#ifndef LV_USE_GENERIC_MIPI
#ifdef CONFIG_LV_USE_GENERIC_MIPI
#define LV_USE_GENERIC_MIPI CONFIG_LV_USE_GENERIC_MIPI
#else
#define LV_USE_GENERIC_MIPI 0
#endif
#endif
#endif
@@ -4337,12 +4351,21 @@ LV_EXPORT_CONST_INT(LV_DRAW_BUF_ALIGN);
#define LV_USE_MEM_MONITOR 0
#endif /*LV_USE_SYSMON*/
#ifndef LV_USE_LZ4
#define LV_USE_LZ4 (LV_USE_LZ4_INTERNAL || LV_USE_LZ4_EXTERNAL)
#if (LV_USE_LZ4_INTERNAL || LV_USE_LZ4_EXTERNAL)
#define LV_USE_LZ4 1
#else
#define LV_USE_LZ4 0
#endif
#endif
#ifndef LV_USE_THORVG
#define LV_USE_THORVG (LV_USE_THORVG_INTERNAL || LV_USE_THORVG_EXTERNAL)
#if (LV_USE_THORVG_INTERNAL || LV_USE_THORVG_EXTERNAL)
#define LV_USE_THORVG 1
#else
#define LV_USE_THORVG 0
#endif
#endif
#if LV_USE_OS

View File

@@ -83,6 +83,30 @@ extern "C" {
# define CONFIG_LV_USE_STDLIB_SPRINTF LV_STDLIB_CUSTOM
#endif
/*******************
* LV_USE_OS
*******************/
#ifdef CONFIG_LV_OS_NONE
# define CONFIG_LV_USE_OS LV_OS_NONE
#elif defined(CONFIG_LV_OS_PTHREAD)
# define CONFIG_LV_USE_OS LV_OS_PTHREAD
#elif defined(CONFIG_LV_OS_FREERTOS)
# define CONFIG_LV_USE_OS LV_OS_FREERTOS
#elif defined(CONFIG_LV_OS_CMSIS_RTOS2)
# define CONFIG_LV_USE_OS LV_OS_CMSIS_RTOS2
#elif defined (CONFIG_LV_OS_RTTHREAD)
# define CONFIG_LV_USE_OS LV_OS_RTTHREAD
#elif defined (CONFIG_LV_OS_WINDOWS)
# define CONFIG_LV_USE_OS LV_OS_WINDOWS
#elif defined (CONFIG_LV_OS_MQX)
# define CONFIG_LV_USE_OS LV_OS_MQX
#elif defined (CONFIG_LV_OS_SDL2)
# define CONFIG_LV_USE_OS LV_OS_SDL2
#elif defined (CONFIG_LV_OS_CUSTOM)
# define CONFIG_LV_USE_OS LV_OS_CUSTOM
#endif
/*******************
* LV_MEM_SIZE
*******************/
@@ -259,6 +283,15 @@ extern "C" {
# define CONFIG_LV_LINUX_FBDEV_RENDER_MODE LV_DISPLAY_RENDER_MODE_FULL
#endif
#ifdef CONFIG_LV_USE_CALENDAR
# ifdef CONFIG_LV_CALENDAR_WEEK_STARTS_MONDAY
# define CONFIG_LV_CALENDAR_DEFAULT_DAY_NAMES { CONFIG_LV_MONDAY_STR , CONFIG_LV_TUESDAY_STR, CONFIG_LV_WEDNESDAY_STR, CONFIG_LV_THURSDAY_STR, CONFIG_LV_FRIDAY_STR, CONFIG_LV_SATURDAY_STR, CONFIG_LV_SUNDAY_STR }
# else
# define CONFIG_LV_CALENDAR_DEFAULT_DAY_NAMES { CONFIG_LV_SUNDAY_STR, CONFIG_LV_MONDAY_STR , CONFIG_LV_TUESDAY_STR, CONFIG_LV_WEDNESDAY_STR, CONFIG_LV_THURSDAY_STR, CONFIG_LV_FRIDAY_STR, CONFIG_LV_SATURDAY_STR }
# endif
#endif
#ifdef __cplusplus
} /*extern "C"*/
#endif

View File

@@ -170,6 +170,10 @@ if ($ENV{NON_AMD64_BUILD})
set(CMAKE_LIBRARY_PATH "/usr/lib/i386-linux-gnu" CACHE PATH "search 32bit lib path firstly")
endif()
# Set LV_CONF_PATH so custom.cmake knows about it before compiling lvgl
# Used to preprocess lv_conf_internal.h with the test config
set(LV_CONF_PATH "${LVGL_TEST_DIR}/src/lv_test_conf.h")
set(LV_BUILD_TEST ON)
# Options lvgl and examples are compiled with.
set(COMPILE_OPTIONS
-DLV_CONF_PATH="${LVGL_TEST_DIR}/src/lv_test_conf.h"