1 # This cmake module contains utilities to read and load libc config options
2 # listed in config.json files.
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.
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.
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:
20 # "value: <option value>,
21 # "doc": "<option doc string>",
24 # Each of the above items can be parsed again with the string(JSON ...)
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})
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
37 DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
38 PROPERTY CMAKE_CONFIGURE_DEPENDS ${config_file})
40 file(READ ${config_file} json_config)
41 string(JSON group_count ERROR_VARIABLE json_error LENGTH ${json_config})
43 message(FATAL_ERROR "${config_file}: ${json_error}")
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")
51 math(EXPR group_count_1 "${group_count} - 1")
54 foreach(group_num RANGE ${group_count_1})
55 # The group names are the keys of the global dictionary. So, we first
56 # lookup the group name or the key for each item in the dictionary.
57 string(JSON group_name ERROR_VARIABLE json_error MEMBER ${json_config} ${group_num})
59 message(FATAL_ERROR "${config_file}: ${json_error}")
62 # Once we have the group name, we GET the option map for that group, which
63 # is the value corresponding to the group name key.
64 string(JSON option_map ERROR_VARIABLE json_error GET ${json_config} ${group_name})
66 message(FATAL_ERROR ${json_error})
68 string(JSON option_count ERROR_VARIABLE jsor_error LENGTH ${option_map})
70 message(FATAL_ERROR ${json_error})
72 if(${option_count} EQUAL 0)
73 message(FATAL_ERROR "${config_file}: No options listed against the config option group '${group_name}'")
76 math(EXPR option_count_1 "${option_count} - 1")
77 foreach(opt_num RANGE ${option_count_1})
78 string(JSON option_name ERROR_VARIABLE json_error MEMBER ${option_map} ${opt_num})
80 message(FATAL_ERROR ${json_error})
82 list(FIND optname_list ${option_name} optname_exists)
83 if(${optname_exists} GREATER -1)
84 message(FATAL_ERROR "${config_file}: Found duplicate option name: ${option_name}")
86 list(APPEND optname_list ${option_name})
88 string(JSON optdata ERROR_VARIABLE json_error GET ${option_map} ${option_name})
90 message(FATAL_ERROR ${json_error})
92 set(opt "{\"${option_name}\": ${optdata}}")
93 list(APPEND all_opts ${opt})
96 set(${opt_list} ${all_opts} PARENT_SCOPE)
99 # Loads the config options listed in |config_file| in the following way:
100 # * For each option listed in the |config_file|, it looks for existence of a
101 # var with the same name. It is an error if the var is not already defined.
102 # If a var with the option name is found, then its value is overwritten
103 # with the value specified in |config_file|.
104 # * If there are options which are not to be overriden, then the list of
105 # such options can be passed to this function after the |config_file|
106 # argument. Typically, these will be the options specified on the CMake
108 function(load_libc_config config_file)
109 read_libc_config(${config_file} file_opts)
110 foreach(opt IN LISTS file_opts)
111 string(JSON opt_name ERROR_VARIABLE json_error MEMBER ${opt} 0)
113 message(FATAL_ERROR ${json_error})
115 if(NOT DEFINED ${opt_name})
116 message(FATAL_ERROR: " Option ${opt_name} defined in ${config_file} is invalid.")
119 list(FIND ARGN ${opt_name} optname_exists)
120 if(${optname_exists} GREATER -1)
121 # This option is not to be overridden so just skip further processing.
125 string(JSON opt_object ERROR_VARIABLE json_error GET ${opt} ${opt_name})
127 message(FATAL_ERROR ${json_error})
129 string(JSON opt_value ERROR_VARIABLE jsor_error GET ${opt_object} "value")
131 message(FATAL_ERROR ${json_error})
133 message(STATUS "Overriding - ${opt_name}: ${opt_value} (Previous value: ${${opt_name}})")
134 set(${opt_name} ${opt_value} PARENT_SCOPE)
138 function(generate_config_doc config_file doc_file)
139 if(NOT EXISTS ${config_file})
140 message(FATAL_ERROR "${config_file} does not exist")
142 file(READ ${config_file} json_config)
143 string(JSON group_count ERROR_VARIABLE json_error LENGTH ${json_config})
145 message(FATAL_ERROR "${config_file}: ${json_error}")
147 if(${group_count} EQUAL 0)
148 message(FATAL_ERROR "${config_file}: Does not contain any config option groups")
150 math(EXPR group_count_1 "${group_count} - 1")
152 set(doc_string ".. _configure:\n"
154 " Do not edit this file directly. CMake will auto generate it.\n"
155 " If the changes are intended, add this file to your commit.\n"
157 "==========================\n"
158 "Configure Options\n"
159 "==========================\n"
161 "Below is the full set of options one can use to configure the libc build.\n"
162 "An option can be given an explicit value on the CMake command line using\n"
163 "the following syntax:\n"
165 ".. code-block:: sh\n"
167 " $> cmake <other build options> -D<libc config option name>=<option value> <more options>\n"
171 ".. code-block:: sh\n"
173 " $> cmake <other build options> -DLIBC_CONF_PRINTF_DISABLE_FLOAT=ON <more options>\n"
175 "See the main ``config/config.json``, and the platform and architecture specific\n"
176 "overrides in ``config/<platform>/config.json`` and ``config/<platform>/<arch>/config.json,``\n"
177 "to learn about the defaults for your platform and target.\n"
180 foreach(group_num RANGE ${group_count_1})
181 string(JSON group_name ERROR_VARIABLE json_error MEMBER ${json_config} ${group_num})
183 message(FATAL_ERROR "${config_file}: ${json_error}")
185 string(APPEND doc_string "* **\"${group_name}\" options**\n")
186 string(JSON option_map ERROR_VARIABLE json_error GET ${json_config} ${group_name})
188 message(FATAL_ERROR ${json_error})
190 string(JSON option_count ERROR_VARIABLE jsor_error LENGTH ${option_map})
192 message(FATAL_ERROR ${json_error})
194 if(${option_count} EQUAL 0)
195 message(FATAL_ERROR "${config_file}: No options listed against the config option group '${group_name}'")
198 math(EXPR option_count_1 "${option_count} - 1")
199 foreach(opt_num RANGE ${option_count_1})
200 string(JSON option_name ERROR_VARIABLE json_error MEMBER ${option_map} ${opt_num})
202 message(FATAL_ERROR ${json_error})
204 string(JSON opt_object ERROR_VARIABLE json_error GET ${option_map} ${option_name})
206 message(FATAL_ERROR "Error generating ${doc_file}: ${json_error}\n${opt_object}")
208 string(JSON opt_doc ERROR_VARIABLE json_error GET ${opt_object} "doc")
210 message(FATAL_ERROR "Error generating ${doc_file}: ${json_error}")
212 string(APPEND doc_string " - ``${option_name}``: ${opt_doc}\n")
215 message(STATUS "Writing config doc to ${doc_file}")
216 file(WRITE ${doc_file} ${doc_string})