Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / libc / cmake / modules / LibcConfig.cmake
blob7a3e6066b3cc06e2a9e0bfdb6df26ab371ea6d3d
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:
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})
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   endif()
51   math(EXPR group_count_1 "${group_count} - 1")
53   set(optname_list)
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})
58     if(json_error)
59       message(FATAL_ERROR "${config_file}: ${json_error}")
60     endif()
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})
65     if(json_error)
66       message(FATAL_ERROR ${json_error})
67     endif()
68     string(JSON option_count ERROR_VARIABLE jsor_error LENGTH ${option_map})
69     if(json_error)
70       message(FATAL_ERROR ${json_error})
71     endif()
72     if(${option_count} EQUAL 0)
73       message(FATAL_ERROR "${config_file}: No options listed against the config option group '${group_name}'")
74     endif()
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})
79       if(json_error)
80         message(FATAL_ERROR ${json_error})
81       endif()
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}")
85       endif()
86       list(APPEND optname_list ${option_name})
88       string(JSON optdata ERROR_VARIABLE json_error GET ${option_map} ${option_name})
89       if(json_error)
90         message(FATAL_ERROR ${json_error})
91       endif()
92       set(opt "{\"${option_name}\": ${optdata}}")
93       list(APPEND all_opts ${opt})
94     endforeach()
95   endforeach()
96   set(${opt_list} ${all_opts} PARENT_SCOPE)
97 endfunction()
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
107 #   command line.
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)
112     if(json_error)
113       message(FATAL_ERROR ${json_error})
114     endif()
115     if(NOT DEFINED ${opt_name})
116       message(FATAL_ERROR: " Option ${opt_name} defined in ${config_file} is invalid.")
117     endif()
118     if(ARGN)
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.
122         continue()
123       endif()
124     endif()
125     string(JSON opt_object ERROR_VARIABLE json_error GET ${opt} ${opt_name})
126     if(json_error)
127       message(FATAL_ERROR ${json_error})
128     endif()
129     string(JSON opt_value ERROR_VARIABLE jsor_error GET ${opt_object} "value")
130     if(json_error)
131       message(FATAL_ERROR ${json_error})
132     endif()
133     message(STATUS "Overriding - ${opt_name}: ${opt_value} (Previous value: ${${opt_name}})")
134     set(${opt_name} ${opt_value} PARENT_SCOPE)
135   endforeach()
136 endfunction()
138 function(generate_config_doc config_file doc_file)
139   if(NOT EXISTS ${config_file})
140     message(FATAL_ERROR "${config_file} does not exist")
141   endif()
142   file(READ ${config_file} json_config)
143   string(JSON group_count ERROR_VARIABLE json_error LENGTH ${json_config})
144   if(json_error)
145     message(FATAL_ERROR "${config_file}: ${json_error}")
146   endif()
147   if(${group_count} EQUAL 0)
148     message(FATAL_ERROR "${config_file}: Does not contain any config option groups")
149   endif()
150   math(EXPR group_count_1 "${group_count} - 1")
152   set(doc_string ".. _configure:\n"
153                  "..\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"
156                  "\n"
157                  "==========================\n"
158                  "Configure Options\n"
159                  "==========================\n"
160                  "\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"
164                  "\n"
165                  ".. code-block:: sh\n"
166                  "\n"
167                  "  $> cmake <other build options> -D<libc config option name>=<option value> <more options>\n"
168                  "\n"
169                  "For example:\n"
170                  "\n"
171                  ".. code-block:: sh\n"
172                  "\n"
173                  "  $> cmake <other build options> -DLIBC_CONF_PRINTF_DISABLE_FLOAT=ON <more options>\n"
174                  "\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"
178                  "\n")
180   foreach(group_num RANGE ${group_count_1})
181     string(JSON group_name ERROR_VARIABLE json_error MEMBER ${json_config} ${group_num})
182     if(json_error)
183       message(FATAL_ERROR "${config_file}: ${json_error}")
184     endif()
185     string(APPEND doc_string "* **\"${group_name}\" options**\n")
186     string(JSON option_map ERROR_VARIABLE json_error GET ${json_config} ${group_name})
187     if(json_error)
188       message(FATAL_ERROR ${json_error})
189     endif()
190     string(JSON option_count ERROR_VARIABLE jsor_error LENGTH ${option_map})
191     if(json_error)
192       message(FATAL_ERROR ${json_error})
193     endif()
194     if(${option_count} EQUAL 0)
195       message(FATAL_ERROR "${config_file}: No options listed against the config option group '${group_name}'")
196     endif()
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})
201       if(json_error)
202         message(FATAL_ERROR ${json_error})
203       endif()
204       string(JSON opt_object ERROR_VARIABLE json_error GET ${option_map} ${option_name})
205       if(json_error)
206         message(FATAL_ERROR "Error generating ${doc_file}: ${json_error}\n${opt_object}")
207       endif()
208       string(JSON opt_doc ERROR_VARIABLE json_error GET ${opt_object} "doc")
209       if(json_error)
210         message(FATAL_ERROR "Error generating ${doc_file}: ${json_error}")
211       endif()
212       string(APPEND doc_string "    - ``${option_name}``: ${opt_doc}\n")
213     endforeach()
214   endforeach()
215   message(STATUS "Writing config doc to ${doc_file}")
216   file(WRITE ${doc_file} ${doc_string})
217 endfunction()