|
| 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() |
0 commit comments