Skip to content

Commit 0f1507a

Browse files
author
Siva Chandra Reddy
committed
[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
1 parent 5647f29 commit 0f1507a

File tree

5 files changed

+217
-8
lines changed

5 files changed

+217
-8
lines changed

libc/CMakeLists.txt

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,46 @@ set(LIBC_ENABLE_HERMETIC_TESTS ${LLVM_LIBC_FULL_BUILD})
9393
# Defines LIBC_TARGET_ARCHITECTURE and associated macros.
9494
include(LLVMLibCArchitectures)
9595

96+
include(LibcConfig)
97+
# Config loading happens in three steps:
98+
# 1. Load the config file config/config.json and set up config vars.
99+
# 2. Load config/${LIBC_TARGET_OS}/config.json if available and override
100+
# vars as suitable.
101+
# 3. Load config/${LIBC_TARGET_OS}/${LIBC_TARGET_ARCH}/config.json is
102+
# available and override vars as suitable.
103+
# All the three steps will not override options already set from the
104+
# CMake command line. That is, the CMake command line option values take
105+
# precedence over the values in config.json files.
106+
set(main_config_file ${LIBC_SOURCE_DIR}/config/config.json)
107+
read_libc_config(${main_config_file} global_config)
108+
foreach(opt IN LISTS global_config)
109+
string(JSON opt_name ERROR_VARIABLE json_error MEMBER ${opt} 0)
110+
if(json_error)
111+
message(FATAL_ERROR ${json_error})
112+
endif()
113+
if(DEFINED ${opt_name})
114+
# The option is already defined from the command line so we ignore it here.
115+
# We still make note of it so that further config load can also ignore
116+
# this option.
117+
message(STATUS "${opt_name}: ${${opt_name}} (from command line)")
118+
list(APPEND cmd_line_conf ${opt_name})
119+
continue()
120+
endif()
121+
122+
string(JSON opt_object ERROR_VARIABLE json_error GET ${opt} ${opt_name})
123+
if(json_error)
124+
message(FATAL_ERROR "Error reading info of option '${opt_name}': ${json_error}")
125+
endif()
126+
string(JSON opt_value ERROR_VARIABLE json_error GET ${opt_object} "value")
127+
if(json_error)
128+
message(FATAL_ERROR ${json_error})
129+
endif()
130+
message(STATUS "${opt_name}: ${opt_value}")
131+
set(${opt_name} ${opt_value})
132+
endforeach()
133+
load_libc_config(${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/config.json ${cmd_line_conf})
134+
load_libc_config(${LIBC_SOURCE_DIR}/config/${LIBC_TARGET_OS}/${LIBC_TARGET_ARCHITECTURE}/config.json ${cmd_line_conf})
135+
96136
if(LIBC_TARGET_ARCHITECTURE_IS_GPU)
97137
set(LIBC_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/include)
98138
set(LIBC_INSTALL_INCLUDE_DIR ${CMAKE_INSTALL_INCLUDEDIR}/gpu-none-llvm)

libc/cmake/modules/LibcConfig.cmake

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
# This cmake module contains utilities to read and load libc config options
2+
# listed in config.json files.
3+
#
4+
# The JSON parsing commands that CMake provides are rather tedious to use.
5+
# Below is a quick reference which tries to map the CMake JSON parsing
6+
# commands to the Python dictionary API.
7+
#
8+
# * There is no way to iterate over the JSON items. One will first
9+
# have to find the number of items using string(JSON ... LENGTH ...)
10+
# command, and then iterate over the items using foreach(... RANGE ...).
11+
# * The way to get the key from the JSON dictionary is to use the index
12+
# of the item and the string(JSON ... MEMBER ... $<index>) function.
13+
# * Once you have the key, you can use the string(JSON ... GET ... $<key>)
14+
# function to get the value corresponding to the key.
15+
16+
# Fill |opt_list| with all options listed in |config_file|. For each option,
17+
# the item added to |opt_list| is the dictionary of the form:
18+
# {
19+
# "<option name>": {
20+
# "value: <option value>,
21+
# "doc": "<option doc string>",
22+
# }
23+
# }
24+
# Each of the above items can be parsed again with the string(JSON ...)
25+
# command.
26+
# This function does nothing if |config_file| is missing.
27+
function(read_libc_config config_file opt_list)
28+
if(NOT EXISTS ${config_file})
29+
return()
30+
endif()
31+
# We will assume that a config file is loaded only once and that
32+
# each config file loaded will affect config information. Since
33+
# we want a change to config information to trigger reconfiguration,
34+
# we add the |config_file| to the list of files the configure itself
35+
# should depend on.
36+
set_property(
37+
DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
38+
PROPERTY CMAKE_CONFIGURE_DEPENDS ${config_file})
39+
40+
file(READ ${config_file} json_config)
41+
string(JSON group_count ERROR_VARIABLE json_error LENGTH ${json_config})
42+
if(json_error)
43+
message(FATAL_ERROR "${config_file}: ${json_error}")
44+
endif()
45+
if(${group_count} EQUAL 0)
46+
# This "if" conditions becomes active if there are no config options
47+
# to load. If there are no config options, it is better to remove that
48+
# config.json file instead of including an empty file.
49+
message(FATAL_ERROR "${config_file}: Does not contain any config option groups")
50+
return()
51+
endif()
52+
math(EXPR group_count_1 "${group_count} - 1")
53+
54+
set(optname_list)
55+
foreach(group_num RANGE ${group_count_1})
56+
# The group names are the keys of the global dictionary. So, we first
57+
# lookup the group name or the key for each item in the dictionary.
58+
string(JSON group_name ERROR_VARIABLE json_error MEMBER ${json_config} ${group_num})
59+
if(json_error)
60+
message(FATAL_ERROR "${config_file}: ${json_error}")
61+
endif()
62+
63+
# Once we have the group name, we GET the option map for that group, which
64+
# is the value corresponding to the group name key.
65+
string(JSON option_map ERROR_VARIABLE json_error GET ${json_config} ${group_name})
66+
if(json_error)
67+
message(FATAL_ERROR ${json_error})
68+
endif()
69+
string(JSON option_count ERROR_VARIABLE jsor_error LENGTH ${option_map})
70+
if(json_error)
71+
message(FATAL_ERROR ${json_error})
72+
endif()
73+
if(${option_count} EQUAL 0)
74+
message(FATAL_ERROR "${config_file}: No options listed against the config option group '${group_name}'")
75+
endif()
76+
77+
math(EXPR option_count_1 "${option_count} - 1")
78+
foreach(opt_num RANGE ${option_count_1})
79+
string(JSON option_name ERROR_VARIABLE json_error MEMBER ${option_map} ${opt_num})
80+
if(json_error)
81+
message(FATAL_ERROR ${json_error})
82+
endif()
83+
list(FIND optname_list ${option_name} optname_exists)
84+
if(${optname_exists} GREATER -1)
85+
message(FATAL_ERROR "${config_file}: Found duplicate option name: ${option_name}")
86+
endif()
87+
list(APPEND optname_list ${option_name})
88+
89+
string(JSON optdata ERROR_VARIABLE json_error GET ${option_map} ${option_name})
90+
if(json_error)
91+
message(FATAL_ERROR ${json_error})
92+
endif()
93+
set(opt "{\"${option_name}\": ${optdata}}")
94+
list(APPEND all_opts ${opt})
95+
endforeach()
96+
endforeach()
97+
set(${opt_list} ${all_opts} PARENT_SCOPE)
98+
endfunction()
99+
100+
# Loads the config options listed in |config_file| in the following way:
101+
# * For each option listed in the |config_file|, it looks for existence of a
102+
# var with the same name. It is an error if the var is not already defined.
103+
# If a var with the option name is found, then its value is overwritten
104+
# with the value specified in |config_file|.
105+
# * If there are options which are not to be overriden, then the list of
106+
# such options can be passed to this function after the |config_file|
107+
# argument. Typically, these will be the options specified on the CMake
108+
# command line.
109+
function(load_libc_config config_file)
110+
read_libc_config(${config_file} file_opts)
111+
foreach(opt IN LISTS file_opts)
112+
string(JSON opt_name ERROR_VARIABLE json_error MEMBER ${opt} 0)
113+
if(json_error)
114+
message(FATAL_ERROR ${json_error})
115+
endif()
116+
if(NOT DEFINED ${opt_name})
117+
message(FATAL_ERROR: " Option ${opt_name} defined in ${config_file} is invalid.")
118+
endif()
119+
if(ARGN)
120+
list(FIND ARGN ${opt_name} optname_exists)
121+
if(${optname_exists} GREATER -1)
122+
# This option is not to be overridden so just skip further processing.
123+
continue()
124+
endif()
125+
endif()
126+
string(JSON opt_object ERROR_VARIABLE json_error GET ${opt} ${opt_name})
127+
if(json_error)
128+
message(FATAL_ERROR ${json_error})
129+
endif()
130+
string(JSON opt_value ERROR_VARIABLE jsor_error GET ${opt_object} "value")
131+
if(json_error)
132+
message(FATAL_ERROR ${json_error})
133+
endif()
134+
message(STATUS "Overriding - ${opt_name}: ${opt_value} (Previous value: ${${opt_name}})")
135+
set(${opt_name} ${opt_value} PARENT_SCOPE)
136+
endforeach()
137+
endfunction()

libc/config/baremetal/config.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
{
2+
"printf": {
3+
"LIBC_CONF_PRINTF_DISABLE_FLOAT": {
4+
"value": true
5+
},
6+
"LIBC_CONF_PRINTF_DISABLE_INDEX_MODE": {
7+
"value": true
8+
},
9+
"LIBC_CONF_PRINTF_DISABLE_WRITE_INT": {
10+
"value": true
11+
}
12+
}
13+
}

libc/config/config.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"printf": {
3+
"LIBC_CONF_PRINTF_DISABLE_FLOAT": {
4+
"value": false,
5+
"doc": "Disable printing floating point values in printf and friends."
6+
},
7+
"LIBC_CONF_PRINTF_DISABLE_INDEX_MODE": {
8+
"value": false,
9+
"doc": "Disable index mode in the printf format string."
10+
},
11+
"LIBC_CONF_PRINTF_DISABLE_WRITE_INT": {
12+
"value": false,
13+
"doc": "Disable handling of %n in printf format string."
14+
}
15+
}
16+
}

libc/src/stdio/CMakeLists.txt

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,6 @@ if(NOT LIBC_TARGET_ARCHITECTURE_IS_GPU)
2626
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/generic)
2727
endif()
2828

29-
if(${LIBC_TARGET_OS} STREQUAL "baremetal")
30-
list(APPEND printf_copts
31-
"-DLIBC_COPT_PRINTF_DISABLE_FLOAT"
32-
"-DLIBC_COPT_PRINTF_DISABLE_INDEX_MODE"
33-
"-DLIBC_COPT_PRINTF_DISABLE_WRITE_INT"
34-
)
35-
endif()
36-
3729
add_subdirectory(printf_core)
3830
add_subdirectory(scanf_core)
3931

@@ -426,6 +418,17 @@ list(APPEND printf_deps
426418
libc.src.__support.arg_list
427419
libc.src.stdio.printf_core.vfprintf_internal
428420
)
421+
422+
if(LIBC_CONF_PRINTF_DISABLE_FLOAT)
423+
list(APPEND printf_copts "-DLIBC_COPT_PRINTF_DISABLE_FLOAT")
424+
endif()
425+
if(LIBC_CONF_PRINTF_DISABLE_INDEX_MODE)
426+
list(APPEND printf_copts "-DLIBC_COPT_PRINTF_DISABLE_INDEX_MODE")
427+
endif()
428+
if(LIBC_CONF_PRINTF_DISABLE_WRITE_INT)
429+
list(APPEND printf_copts "-DLIBC_COPT_PRINTF_DISABLE_WRITE_INT")
430+
endif()
431+
429432
if(LLVM_LIBC_FULL_BUILD)
430433
list(APPEND printf_deps
431434
libc.src.__support.File.file

0 commit comments

Comments
 (0)