[libc-commits] [libc] 0f1507a - [libc] Add a JSON based config option system.
Siva Chandra Reddy via libc-commits
libc-commits at lists.llvm.org
Tue Sep 5 07:19:29 PDT 2023
Author: Siva Chandra Reddy
Date: 2023-09-05T14:19:18Z
New Revision: 0f1507af411b08c66e9807684858893f2e921ee2
URL: https://github.com/llvm/llvm-project/commit/0f1507af411b08c66e9807684858893f2e921ee2
DIFF: https://github.com/llvm/llvm-project/commit/0f1507af411b08c66e9807684858893f2e921ee2.diff
LOG: [libc] Add a JSON based config option system.
Few printf config options have been setup using this new config system
along with their baremetal overrides. A follow up patch will add generation
of doc/config.rst, which will contain the full list of libc config options
and short description explaining how they affect the libc.
Reviewed By: gchatelet
Differential Revision: https://reviews.llvm.org/D159158
Added:
libc/cmake/modules/LibcConfig.cmake
libc/config/baremetal/config.json
libc/config/config.json
Modified:
libc/CMakeLists.txt
libc/src/stdio/CMakeLists.txt
Removed:
################################################################################
diff --git a/libc/CMakeLists.txt b/libc/CMakeLists.txt
index c885080cc33e9ad..549d1fbc3a55fe5 100644
--- a/libc/CMakeLists.txt
+++ b/libc/CMakeLists.txt
@@ -93,6 +93,46 @@ set(LIBC_ENABLE_HERMETIC_TESTS ${LLVM_LIBC_FULL_BUILD})
# Defines LIBC_TARGET_ARCHITECTURE and associated macros.
include(LLVMLibCArchitectures)
+include(LibcConfig)
+# Config loading happens in three steps:
+# 1. Load the config file config/config.json and set up config vars.
+# 2. Load config/${LIBC_TARGET_OS}/config.json if available and override
+# vars as suitable.
+# 3. Load config/${LIBC_TARGET_OS}/${LIBC_TARGET_ARCH}/config.json is
+# available and override vars as suitable.
+# All the three steps will not override options already set from the
+# CMake command line. That is, the CMake command line option values take
+# precedence over the values in config.json files.
+set(main_config_file ${LIBC_SOURCE_DIR}/config/config.json)
+read_libc_config(${main_config_file} global_config)
+foreach(opt IN LISTS global_config)
+ string(JSON opt_name ERROR_VARIABLE json_error MEMBER ${opt} 0)
+ if(json_error)
+ message(FATAL_ERROR ${json_error})
+ endif()
+ if(DEFINED ${opt_name})
+ # The option is already defined from the command line so we ignore it here.
+ # We still make note of it so that further config load can also ignore
+ # this option.
+ message(STATUS "${opt_name}: ${${opt_name}} (from command line)")
+ list(APPEND cmd_line_conf ${opt_name})
+ continue()
+ endif()
+
+ string(JSON opt_object ERROR_VARIABLE json_error GET ${opt} ${opt_name})
+ if(json_error)
+ message(FATAL_ERROR "Error reading info of option '${opt_name}': ${json_error}")
+ endif()
+ string(JSON opt_value ERROR_VARIABLE json_error GET ${opt_object} "value")
+ if(json_error)
+ message(FATAL_ERROR ${json_error})
+ endif()
+ message(STATUS "${opt_name}: ${opt_value}")
+ set(${opt_name} ${opt_value})
+endforeach()
+load_libc_config(${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/config.json ${cmd_line_conf})
+load_libc_config(${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_ARCHITECTURE}/config.json ${cmd_line_conf})
+
if(LIBC_TARGET_ARCHITECTURE_IS_GPU)
set(LIBC_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/include)
set(LIBC_INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}/gpu-none-llvm)
diff --git a/libc/cmake/modules/LibcConfig.cmake b/libc/cmake/modules/LibcConfig.cmake
new file mode 100644
index 000000000000000..a9c69de8a310a9b
--- /dev/null
+++ b/libc/cmake/modules/LibcConfig.cmake
@@ -0,0 +1,137 @@
+# This cmake module contains utilities to read and load libc config options
+# listed in config.json files.
+#
+# The JSON parsing commands that CMake provides are rather tedious to use.
+# Below is a quick reference which tries to map the CMake JSON parsing
+# commands to the Python dictionary API.
+#
+# * There is no way to iterate over the JSON items. One will first
+# have to find the number of items using string(JSON ... LENGTH ...)
+# command, and then iterate over the items using foreach(... RANGE ...).
+# * The way to get the key from the JSON dictionary is to use the index
+# of the item and the string(JSON ... MEMBER ... $<index>) function.
+# * Once you have the key, you can use the string(JSON ... GET ... $<key>)
+# function to get the value corresponding to the key.
+
+# Fill |opt_list| with all options listed in |config_file|. For each option,
+# the item added to |opt_list| is the dictionary of the form:
+# {
+# "<option name>": {
+# "value: <option value>,
+# "doc": "<option doc string>",
+# }
+# }
+# Each of the above items can be parsed again with the string(JSON ...)
+# command.
+# This function does nothing if |config_file| is missing.
+function(read_libc_config config_file opt_list)
+ if(NOT EXISTS ${config_file})
+ return()
+ endif()
+ # We will assume that a config file is loaded only once and that
+ # each config file loaded will affect config information. Since
+ # we want a change to config information to trigger reconfiguration,
+ # we add the |config_file| to the list of files the configure itself
+ # should depend on.
+ set_property(
+ DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
+ PROPERTY CMAKE_CONFIGURE_DEPENDS ${config_file})
+
+ file(READ ${config_file} json_config)
+ string(JSON group_count ERROR_VARIABLE json_error LENGTH ${json_config})
+ if(json_error)
+ message(FATAL_ERROR "${config_file}: ${json_error}")
+ endif()
+ if(${group_count} EQUAL 0)
+ # This "if" conditions becomes active if there are no config options
+ # to load. If there are no config options, it is better to remove that
+ # config.json file instead of including an empty file.
+ message(FATAL_ERROR "${config_file}: Does not contain any config option groups")
+ return()
+ endif()
+ math(EXPR group_count_1 "${group_count} - 1")
+
+ set(optname_list)
+ foreach(group_num RANGE ${group_count_1})
+ # The group names are the keys of the global dictionary. So, we first
+ # lookup the group name or the key for each item in the dictionary.
+ string(JSON group_name ERROR_VARIABLE json_error MEMBER ${json_config} ${group_num})
+ if(json_error)
+ message(FATAL_ERROR "${config_file}: ${json_error}")
+ endif()
+
+ # Once we have the group name, we GET the option map for that group, which
+ # is the value corresponding to the group name key.
+ string(JSON option_map ERROR_VARIABLE json_error GET ${json_config} ${group_name})
+ if(json_error)
+ message(FATAL_ERROR ${json_error})
+ endif()
+ string(JSON option_count ERROR_VARIABLE jsor_error LENGTH ${option_map})
+ if(json_error)
+ message(FATAL_ERROR ${json_error})
+ endif()
+ if(${option_count} EQUAL 0)
+ message(FATAL_ERROR "${config_file}: No options listed against the config option group '${group_name}'")
+ endif()
+
+ math(EXPR option_count_1 "${option_count} - 1")
+ foreach(opt_num RANGE ${option_count_1})
+ string(JSON option_name ERROR_VARIABLE json_error MEMBER ${option_map} ${opt_num})
+ if(json_error)
+ message(FATAL_ERROR ${json_error})
+ endif()
+ list(FIND optname_list ${option_name} optname_exists)
+ if(${optname_exists} GREATER -1)
+ message(FATAL_ERROR "${config_file}: Found duplicate option name: ${option_name}")
+ endif()
+ list(APPEND optname_list ${option_name})
+
+ string(JSON optdata ERROR_VARIABLE json_error GET ${option_map} ${option_name})
+ if(json_error)
+ message(FATAL_ERROR ${json_error})
+ endif()
+ set(opt "{\"${option_name}\": ${optdata}}")
+ list(APPEND all_opts ${opt})
+ endforeach()
+ endforeach()
+ set(${opt_list} ${all_opts} PARENT_SCOPE)
+endfunction()
+
+# Loads the config options listed in |config_file| in the following way:
+# * For each option listed in the |config_file|, it looks for existence of a
+# var with the same name. It is an error if the var is not already defined.
+# If a var with the option name is found, then its value is overwritten
+# with the value specified in |config_file|.
+# * If there are options which are not to be overriden, then the list of
+# such options can be passed to this function after the |config_file|
+# argument. Typically, these will be the options specified on the CMake
+# command line.
+function(load_libc_config config_file)
+ read_libc_config(${config_file} file_opts)
+ foreach(opt IN LISTS file_opts)
+ string(JSON opt_name ERROR_VARIABLE json_error MEMBER ${opt} 0)
+ if(json_error)
+ message(FATAL_ERROR ${json_error})
+ endif()
+ if(NOT DEFINED ${opt_name})
+ message(FATAL_ERROR: " Option ${opt_name} defined in ${config_file} is invalid.")
+ endif()
+ if(ARGN)
+ list(FIND ARGN ${opt_name} optname_exists)
+ if(${optname_exists} GREATER -1)
+ # This option is not to be overridden so just skip further processing.
+ continue()
+ endif()
+ endif()
+ string(JSON opt_object ERROR_VARIABLE json_error GET ${opt} ${opt_name})
+ if(json_error)
+ message(FATAL_ERROR ${json_error})
+ endif()
+ string(JSON opt_value ERROR_VARIABLE jsor_error GET ${opt_object} "value")
+ if(json_error)
+ message(FATAL_ERROR ${json_error})
+ endif()
+ message(STATUS "Overriding - ${opt_name}: ${opt_value} (Previous value: ${${opt_name}})")
+ set(${opt_name} ${opt_value} PARENT_SCOPE)
+ endforeach()
+endfunction()
diff --git a/libc/config/baremetal/config.json b/libc/config/baremetal/config.json
new file mode 100644
index 000000000000000..a65eaa8911e6c44
--- /dev/null
+++ b/libc/config/baremetal/config.json
@@ -0,0 +1,13 @@
+{
+ "printf": {
+ "LIBC_CONF_PRINTF_DISABLE_FLOAT": {
+ "value": true
+ },
+ "LIBC_CONF_PRINTF_DISABLE_INDEX_MODE": {
+ "value": true
+ },
+ "LIBC_CONF_PRINTF_DISABLE_WRITE_INT": {
+ "value": true
+ }
+ }
+}
diff --git a/libc/config/config.json b/libc/config/config.json
new file mode 100644
index 000000000000000..cd68b81028bff7f
--- /dev/null
+++ b/libc/config/config.json
@@ -0,0 +1,16 @@
+{
+ "printf": {
+ "LIBC_CONF_PRINTF_DISABLE_FLOAT": {
+ "value": false,
+ "doc": "Disable printing floating point values in printf and friends."
+ },
+ "LIBC_CONF_PRINTF_DISABLE_INDEX_MODE": {
+ "value": false,
+ "doc": "Disable index mode in the printf format string."
+ },
+ "LIBC_CONF_PRINTF_DISABLE_WRITE_INT": {
+ "value": false,
+ "doc": "Disable handling of %n in printf format string."
+ }
+ }
+}
diff --git a/libc/src/stdio/CMakeLists.txt b/libc/src/stdio/CMakeLists.txt
index fc8588d75ad416b..79863f83c1e5e7d 100644
--- a/libc/src/stdio/CMakeLists.txt
+++ b/libc/src/stdio/CMakeLists.txt
@@ -26,14 +26,6 @@ if(NOT LIBC_TARGET_ARCHITECTURE_IS_GPU)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/generic)
endif()
-if(${LIBC_TARGET_OS} STREQUAL "baremetal")
- list(APPEND printf_copts
- "-DLIBC_COPT_PRINTF_DISABLE_FLOAT"
- "-DLIBC_COPT_PRINTF_DISABLE_INDEX_MODE"
- "-DLIBC_COPT_PRINTF_DISABLE_WRITE_INT"
- )
-endif()
-
add_subdirectory(printf_core)
add_subdirectory(scanf_core)
@@ -426,6 +418,17 @@ list(APPEND printf_deps
libc.src.__support.arg_list
libc.src.stdio.printf_core.vfprintf_internal
)
+
+if(LIBC_CONF_PRINTF_DISABLE_FLOAT)
+ list(APPEND printf_copts "-DLIBC_COPT_PRINTF_DISABLE_FLOAT")
+endif()
+if(LIBC_CONF_PRINTF_DISABLE_INDEX_MODE)
+ list(APPEND printf_copts "-DLIBC_COPT_PRINTF_DISABLE_INDEX_MODE")
+endif()
+if(LIBC_CONF_PRINTF_DISABLE_WRITE_INT)
+ list(APPEND printf_copts "-DLIBC_COPT_PRINTF_DISABLE_WRITE_INT")
+endif()
+
if(LLVM_LIBC_FULL_BUILD)
list(APPEND printf_deps
libc.src.__support.File.file
More information about the libc-commits
mailing list