From 0592f46199ad633be6612389fe355da56bf99aac Mon Sep 17 00:00:00 2001 From: Jim Huang Date: Thu, 25 Jul 2024 20:03:29 +0800 Subject: [PATCH] Make build system fully configurable With the inclusion of kconfiglib [1], the build system is now fully configurable like "menuconfig" when building the Linux kernel. It allows users to select the desired backend, choose whether to build applications, and specify supported image loaders. This flexibility is also useful for developers to integrate additional backends such as Linux framebuffer and DRM device support. [1] https://pypi.org/project/kconfiglib/ --- .github/workflows/main.yml | 5 +- .gitignore | 5 + Makefile | 49 +- README.md | 5 + apps/dummy.c | 1 + apps/main.c | 17 + configs/Kconfig | 67 + configs/defconfig | 22 + tools/kconfig/.gitignore | 4 + tools/kconfig/LICENSE | 13 + tools/kconfig/README.md | 5 + tools/kconfig/defconfig.py | 43 + tools/kconfig/genconfig.py | 154 + tools/kconfig/kconfiglib.py | 7173 ++++++++++++++++++++++++++++++++ tools/kconfig/menuconfig.py | 3278 +++++++++++++++ tools/kconfig/oldconfig.py | 246 ++ tools/kconfig/olddefconfig.py | 28 + tools/kconfig/savedefconfig.py | 49 + tools/kconfig/setconfig.py | 92 + 19 files changed, 11240 insertions(+), 16 deletions(-) create mode 100644 apps/dummy.c create mode 100644 configs/Kconfig create mode 100644 configs/defconfig create mode 100644 tools/kconfig/.gitignore create mode 100644 tools/kconfig/LICENSE create mode 100644 tools/kconfig/README.md create mode 100755 tools/kconfig/defconfig.py create mode 100755 tools/kconfig/genconfig.py create mode 100644 tools/kconfig/kconfiglib.py create mode 100755 tools/kconfig/menuconfig.py create mode 100755 tools/kconfig/oldconfig.py create mode 100755 tools/kconfig/olddefconfig.py create mode 100755 tools/kconfig/savedefconfig.py create mode 100755 tools/kconfig/setconfig.py diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 94c088b..988f92e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -45,7 +45,10 @@ jobs: sudo apt install libsdl2-dev libjpeg-dev libpng-dev shell: bash - name: default build - run: make + run: | + tools/kconfig/defconfig.py --kconfig configs/Kconfig configs/defconfig + tools/kconfig/genconfig.py configs/Kconfig + make coding-style: needs: [detect-code-related-file-changes] diff --git a/.gitignore b/.gitignore index d5222f1..a5f2a3a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,11 @@ libbackend.a .libbackend.a libapps.a .libapps.a +.config +.config.old +config.h +__pycache__ +*.pyc # Tools tools/ttf/twin-ttf diff --git a/Makefile b/Makefile index 42c7e4d..0599854 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,11 @@ -# FIXME: make these entries configurable -CONFIG_BACKEND_SDL ?= y -CONFIG_LOADER_JPEG ?= y -CONFIG_LOADER_PNG ?= y +-include .config + +check_goal := $(strip $(MAKECMDGOALS)) +ifneq ($(check_goal), config) +ifneq "$(CONFIG_CONFIGURED)" "y" +$(error You must first run 'make config') +endif +endif # Rules @@ -9,9 +13,9 @@ target-y := target.o-y := TARGET_LIBS := -target.a-y = \ - libapps.a \ - libtwin.a +target.a-y := +target.a-$(CONFIG_DEMO_APPLICATIONS) += libapps.a +target.a-y += libtwin.a # Core library @@ -68,17 +72,18 @@ endif # Applications -libapps.a_files-y = \ - apps/calc.c \ - apps/spline.c \ - apps/clock.c \ - apps/line.c \ - apps/hello.c \ - apps/demo.c +libapps.a_files-y := apps/dummy.c +libapps.a_files-$(CONFIG_DEMO_MULTI) += apps/demo.c +libapps.a_files-$(CONFIG_DEMO_HELLO) += apps/hello.c +libapps.a_files-$(CONFIG_DEMO_CLOCK) += apps/clock.c +libapps.a_files-$(CONFIG_DEMO_CALCULATOR) += apps/calc.c +libapps.a_files-$(CONFIG_DEMO_LINE) += apps/line.c +libapps.a_files-$(CONFIG_DEMO_SPLINE) += apps/spline.c libapps.a_includes-y := include # Graphical backends + BACKEND := none ifeq ($(CONFIG_BACKEND_SDL), y) @@ -88,7 +93,9 @@ libtwin.a_cflags-y += $(shell sdl2-config --cflags) TARGET_LIBS += $(shell sdl2-config --libs) endif -# Platform-specific executables +# Standalone application + +ifeq ($(CONFIG_DEMO_APPLICATIONS), y) target-y += demo-$(BACKEND) demo-$(BACKEND)_depends-y += $(target.a-y) demo-$(BACKEND)_files-y = apps/main.c @@ -96,5 +103,17 @@ demo-$(BACKEND)_includes-y := include demo-$(BACKEND)_ldflags-y := \ $(target.a-y) \ $(TARGET_LIBS) +endif + +CFLAGS += -include config.h +check_goal := $(strip $(MAKECMDGOALS)) +ifneq ($(check_goal), config) include mk/common.mk +endif + +# Menuconfig +.PHONY: config +config: configs/Kconfig + @tools/kconfig/menuconfig.py $< + @tools/kconfig/genconfig.py $< diff --git a/README.md b/README.md index 10a49bc..a3ee6fc 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,11 @@ have the [SDL2 library](https://www.libsdl.org/), [libjpeg](https://www.ijg.org/ * macOS: `brew install sdl2 jpeg libpng` * Ubuntu Linux / Debian: `sudo apt install libsdl2-dev libjpeg-dev libpng-dev` +Configure via [Kconfiglib](https://pypi.org/project/kconfiglib/) +```shell +$ make config +``` + Build the library and demo program. ```shell $ make diff --git a/apps/dummy.c b/apps/dummy.c new file mode 100644 index 0000000..e116ab0 --- /dev/null +++ b/apps/dummy.c @@ -0,0 +1 @@ +/* Placeholder */ diff --git a/apps/main.c b/apps/main.c index 59a7994..8157f3f 100644 --- a/apps/main.c +++ b/apps/main.c @@ -1,5 +1,6 @@ /* * Twin - A Tiny Window System + * Copyright (c) 2024 National Cheng Kung University, Taiwan * Copyright (c) 2004 Keith Packard * All rights reserved. */ @@ -33,6 +34,7 @@ static twin_pixmap_t *load_background(twin_screen_t *screen, const char *filename) { +#if defined(CONFIG_LOADER_PNG) twin_pixmap_t *raw_background = twin_png_to_pixmap(filename, TWIN_ARGB32); if (!raw_background) /* Fallback to a default pattern */ return twin_make_pattern(); @@ -65,6 +67,9 @@ static twin_pixmap_t *load_background(twin_screen_t *screen, twin_pixmap_destroy(raw_background); return scaled_background; +#else + return twin_make_pattern(); +#endif } static twin_context_t *tx = NULL; @@ -99,12 +104,24 @@ int main(void) twin_screen_set_background( tx->screen, load_background(tx->screen, ASSET_PATH "/tux.png")); +#if defined(CONFIG_DEMO_MULTI) apps_demo_start(tx->screen, "Demo", 100, 100, 400, 400); +#endif +#if defined(CONFIG_DEMO_HELLO) apps_hello_start(tx->screen, "Hello, World", 0, 0, 200, 200); +#endif +#if defined(CONFIG_DEMO_CLOCK) apps_clock_start(tx->screen, "Clock", 10, 10, 200, 200); +#endif +#if defined(CONFIG_DEMO_CALCULATOR) apps_calc_start(tx->screen, "Calculator", 100, 100, 200, 200); +#endif +#if defined(CONFIG_DEMO_LINE) apps_line_start(tx->screen, "Line", 0, 0, 200, 200); +#endif +#if defined(CONFIG_DEMO_SPLINE) apps_spline_start(tx->screen, "Spline", 20, 20, 400, 400); +#endif twin_dispatch(); diff --git a/configs/Kconfig b/configs/Kconfig new file mode 100644 index 0000000..da5a390 --- /dev/null +++ b/configs/Kconfig @@ -0,0 +1,67 @@ +mainmenu "Mado System Configuration" + +config CONFIGURED + bool + default y + +choice + prompt "Backend Selection" + default BACKEND_SDL + +config BACKEND_FBDEV + bool "Linux framebuffer support" + +config BACKEND_SDL + bool "SDL video output support" + +endchoice + +menu "Image Loaders" + +config LOADER_PNG + bool "Enable PNG loader" + default y + +config LOADER_JPEG + bool "Enable JPEG loader" + default y + +endmenu + +menu "Demo Applications" + +config DEMO_APPLICATIONS + bool "Build demo applications" + default y + +config DEMO_MULTI + bool "Build multi demo" + default y + depends on DEMO_APPLICATIONS + +config DEMO_HELLO + bool "Build Hello world demo" + default y + depends on DEMO_APPLICATIONS + +config DEMO_CLOCK + bool "Build clock demo" + default y + depends on DEMO_APPLICATIONS + +config DEMO_CALCULATOR + bool "Build calculator demo" + default y + depends on DEMO_APPLICATIONS + +config DEMO_LINE + bool "Build line demo" + default y + depends on DEMO_APPLICATIONS + +config DEMO_SPLINE + bool "Build spline demp" + default y + depends on DEMO_APPLICATIONS + +endmenu diff --git a/configs/defconfig b/configs/defconfig new file mode 100644 index 0000000..8f03694 --- /dev/null +++ b/configs/defconfig @@ -0,0 +1,22 @@ +CONFIG_CONFIGURED=y +# CONFIG_BACKEND_FBDEV is not set +CONFIG_BACKEND_SDL=y + +# +# Image Loaders +# +CONFIG_LOADER_PNG=y +CONFIG_LOADER_JPEG=y +# end of Image Loaders + +# +# Demo Applications +# +CONFIG_DEMO_APPLICATIONS=y +CONFIG_DEMO_MULTI=y +CONFIG_DEMO_HELLO=y +CONFIG_DEMO_CLOCK=y +CONFIG_DEMO_CALCULATOR=y +CONFIG_DEMO_LINE=y +CONFIG_DEMO_SPLINE=y +# end of Demo Applications diff --git a/tools/kconfig/.gitignore b/tools/kconfig/.gitignore new file mode 100644 index 0000000..4a18d79 --- /dev/null +++ b/tools/kconfig/.gitignore @@ -0,0 +1,4 @@ +*.py[co] +build/ +*.egg-info/ +dist/ diff --git a/tools/kconfig/LICENSE b/tools/kconfig/LICENSE new file mode 100644 index 0000000..94fc977 --- /dev/null +++ b/tools/kconfig/LICENSE @@ -0,0 +1,13 @@ +Copyright (c) 2011-2019, Ulf Magnusson + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/tools/kconfig/README.md b/tools/kconfig/README.md new file mode 100644 index 0000000..6ff921b --- /dev/null +++ b/tools/kconfig/README.md @@ -0,0 +1,5 @@ +# Kconfiglib + +A flexible Python 2/3 Kconfig implementation and library. + +Taken from https://github.com/ulfalizer/Kconfiglib diff --git a/tools/kconfig/defconfig.py b/tools/kconfig/defconfig.py new file mode 100755 index 0000000..b179273 --- /dev/null +++ b/tools/kconfig/defconfig.py @@ -0,0 +1,43 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2019, Ulf Magnusson +# SPDX-License-Identifier: ISC + +""" +Reads a specified configuration file, then writes a new configuration file. +This can be used to initialize the configuration from e.g. an arch-specific +configuration file. This input configuration file would usually be a minimal +configuration file, as generated by e.g. savedefconfig. + +The default output filename is '.config'. A different filename can be passed in +the KCONFIG_CONFIG environment variable. +""" +import argparse + +import kconfiglib + + +def main(): + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__) + + parser.add_argument( + "--kconfig", + default="Kconfig", + help="Top-level Kconfig file (default: Kconfig)") + + parser.add_argument( + "config", + metavar="CONFIGURATION", + help="Input configuration file") + + args = parser.parse_args() + + kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True) + print(kconf.load_config(args.config)) + print(kconf.write_config()) + + +if __name__ == "__main__": + main() diff --git a/tools/kconfig/genconfig.py b/tools/kconfig/genconfig.py new file mode 100755 index 0000000..62f065b --- /dev/null +++ b/tools/kconfig/genconfig.py @@ -0,0 +1,154 @@ +#!/usr/bin/env python3 + +# Copyright (c) 2018-2019, Ulf Magnusson +# SPDX-License-Identifier: ISC + +""" +Generates a header file with #defines from the configuration, matching the +format of include/generated/autoconf.h in the Linux kernel. + +Optionally, also writes the configuration output as a .config file. See +--config-out. + +The --sync-deps, --file-list, and --env-list options generate information that +can be used to avoid needless rebuilds/reconfigurations. + +Before writing a header or configuration file, Kconfiglib compares the old +contents of the file against the new contents. If there's no change, the write +is skipped. This avoids updating file metadata like the modification time, and +might save work depending on your build setup. + +By default, the configuration is generated from '.config'. A different +configuration file can be passed in the KCONFIG_CONFIG environment variable. + +A custom header string can be inserted at the beginning of generated +configuration and header files by setting the KCONFIG_CONFIG_HEADER and +KCONFIG_AUTOHEADER_HEADER environment variables, respectively (this also works +for other scripts). The string is not automatically made a comment (this is by +design, to allow anything to be added), and no trailing newline is added, so +add '/* */', '#', and newlines as appropriate. + +See https://www.gnu.org/software/make/manual/make.html#Multi_002dLine for a +handy way to define multi-line variables in makefiles, for use with custom +headers. Remember to export the variable to the environment. +""" +import argparse +import os +import sys + +import kconfiglib + + +DEFAULT_SYNC_DEPS_PATH = "deps/" + + +def main(): + parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=__doc__) + + parser.add_argument( + "--header-path", + metavar="HEADER_FILE", + help=""" +Path to write the generated header file to. If not specified, the path in the +environment variable KCONFIG_AUTOHEADER is used if it is set, and 'config.h' +otherwise. +""") + + parser.add_argument( + "--config-out", + metavar="CONFIG_FILE", + help=""" +Write the configuration to CONFIG_FILE. This is useful if you include .config +files in Makefiles, as the generated configuration file will be a full .config +file even if .config is outdated. The generated configuration matches what +olddefconfig would produce. If you use sync-deps, you can include +deps/auto.conf instead. --config-out is meant for cases where incremental build +information isn't needed. +""") + + parser.add_argument( + "--sync-deps", + metavar="OUTPUT_DIR", + nargs="?", + const=DEFAULT_SYNC_DEPS_PATH, + help=""" +Enable generation of symbol dependency information for incremental builds, +optionally specifying the output directory (default: {}). See the docstring of +Kconfig.sync_deps() in Kconfiglib for more information. +""".format(DEFAULT_SYNC_DEPS_PATH)) + + parser.add_argument( + "--file-list", + metavar="OUTPUT_FILE", + help=""" +Write a list of all Kconfig files to OUTPUT_FILE, with one file per line. The +paths are relative to $srctree (or to the current directory if $srctree is +unset). Files appear in the order they're 'source'd. +""") + + parser.add_argument( + "--env-list", + metavar="OUTPUT_FILE", + help=""" +Write a list of all environment variables referenced in Kconfig files to +OUTPUT_FILE, with one variable per line. Each line has the format NAME=VALUE. +Only environment variables referenced with the preprocessor $(VAR) syntax are +included, and not variables referenced with the older $VAR syntax (which is +only supported for backwards compatibility). +""") + + parser.add_argument( + "kconfig", + metavar="KCONFIG", + nargs="?", + default="Kconfig", + help="Top-level Kconfig file (default: Kconfig)") + + args = parser.parse_args() + + + kconf = kconfiglib.Kconfig(args.kconfig, suppress_traceback=True) + kconf.load_config() + + if args.header_path is None: + if "KCONFIG_AUTOHEADER" in os.environ: + kconf.write_autoconf() + else: + # Kconfiglib defaults to include/generated/autoconf.h to be + # compatible with the C tools. 'config.h' is used here instead for + # backwards compatibility. It's probably a saner default for tools + # as well. + kconf.write_autoconf("config.h") + else: + kconf.write_autoconf(args.header_path) + + if args.config_out is not None: + kconf.write_config(args.config_out, save_old=False) + + if args.sync_deps is not None: + kconf.sync_deps(args.sync_deps) + + if args.file_list is not None: + with _open_write(args.file_list) as f: + for path in kconf.kconfig_filenames: + f.write(path + "\n") + + if args.env_list is not None: + with _open_write(args.env_list) as f: + for env_var in kconf.env_vars: + f.write("{}={}\n".format(env_var, os.environ[env_var])) + + +def _open_write(path): + # Python 2/3 compatibility. io.open() is available on both, but makes + # write() expect 'unicode' strings on Python 2. + + if sys.version_info[0] < 3: + return open(path, "w") + return open(path, "w", encoding="utf-8") + + +if __name__ == "__main__": + main() diff --git a/tools/kconfig/kconfiglib.py b/tools/kconfig/kconfiglib.py new file mode 100644 index 0000000..2962e86 --- /dev/null +++ b/tools/kconfig/kconfiglib.py @@ -0,0 +1,7173 @@ +# Copyright (c) 2011-2019, Ulf Magnusson +# SPDX-License-Identifier: ISC + +""" +Overview +======== + +Kconfiglib is a Python 2/3 library for scripting and extracting information +from Kconfig (https://www.kernel.org/doc/Documentation/kbuild/kconfig-language.txt) +configuration systems. + +See the homepage at https://github.com/ulfalizer/Kconfiglib for a longer +overview. + +Since Kconfiglib 12.0.0, the library version is available in +kconfiglib.VERSION, which is a (, , ) tuple, e.g. +(12, 0, 0). + + +Using Kconfiglib on the Linux kernel with the Makefile targets +============================================================== + +For the Linux kernel, a handy interface is provided by the +scripts/kconfig/Makefile patch, which can be applied with either 'git am' or +the 'patch' utility: + + $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | git am + $ wget -qO- https://raw.githubusercontent.com/ulfalizer/Kconfiglib/master/makefile.patch | patch -p1 + +Warning: Not passing -p1 to patch will cause the wrong file to be patched. + +Please tell me if the patch does not apply. It should be trivial to apply +manually, as it's just a block of text that needs to be inserted near the other +*conf: targets in scripts/kconfig/Makefile. + +Look further down for a motivation for the Makefile patch and for instructions +on how you can use Kconfiglib without it. + +If you do not wish to install Kconfiglib via pip, the Makefile patch is set up +so that you can also just clone Kconfiglib into the kernel root: + + $ git clone git://github.com/ulfalizer/Kconfiglib.git + $ git am Kconfiglib/makefile.patch (or 'patch -p1 < Kconfiglib/makefile.patch') + +Warning: The directory name Kconfiglib/ is significant in this case, because +it's added to PYTHONPATH by the new targets in makefile.patch. + +The targets added by the Makefile patch are described in the following +sections. + + +make kmenuconfig +---------------- + +This target runs the curses menuconfig interface with Python 3. As of +Kconfiglib 12.2.0, both Python 2 and Python 3 are supported (previously, only +Python 3 was supported, so this was a backport). + + +make guiconfig +-------------- + +This target runs the Tkinter menuconfig interface. Both Python 2 and Python 3 +are supported. To change the Python interpreter used, pass +PYTHONCMD= to 'make'. The default is 'python'. + + +make [ARCH=] iscriptconfig +-------------------------------- + +This target gives an interactive Python prompt where a Kconfig instance has +been preloaded and is available in 'kconf'. To change the Python interpreter +used, pass PYTHONCMD= to 'make'. The default is 'python'. + +To get a feel for the API, try evaluating and printing the symbols in +kconf.defined_syms, and explore the MenuNode menu tree starting at +kconf.top_node by following 'next' and 'list' pointers. + +The item contained in a menu node is found in MenuNode.item (note that this can +be one of the constants kconfiglib.MENU and kconfiglib.COMMENT), and all +symbols and choices have a 'nodes' attribute containing their menu nodes +(usually only one). Printing a menu node will print its item, in Kconfig +format. + +If you want to look up a symbol by name, use the kconf.syms dictionary. + + +make scriptconfig SCRIPT=