Introduction
The libc project supports a number of config options which are primarily present to give the users knobs to control:
-
The inclusion of a certain functionality which is otherwise not included by default.
-
Or, exclusion of a certain other functionality. The user could have various reasons to do so, for example, to reduce code size.
-
To set limits, sizes, bounds etc. For example, the default thread stack size.
Most config options today are only available as pre-processor switches which enable or disable parts of the libc source code. For example, there exists a macro named LIBC_COPT_PRINTF_DISABLE_FLOAT to disable the code handling printing of floating point numbers in printf and friends.
As the libc project is growing, so are the number of config options. However, a systematic approach to introducing the config options and making them available to the users is missing. For example, many of the options introduced do not have a corresponding CMake var to control the option value at libc config time. Another consequence of the lack of a systematic approach is that platform maintainers do not have a clean way to specify/codify the config parameters that best suit their platform. Often, they have to resort to adding messy conditionals in a number of CMakeLists.txt files.
A previous RFC by @gchatelet introduced a systematic naming system for the pre-processor macros which control libc config, which has now been documented and rolled out. In this RFC, we further some of the fundamental ideas proposed in that RFC with a proposal for a systematic procedure to introduce and use libc config options. The core idea of this proposal is an introduction of a uniform config listing system and a common pattern for config application.
The config listing system
The config listing is driven by a set of hierarchical JSON files. At the top of the hierarchy is a JSON file by name config.json in the config directory. This JSON file lists the libc options which affect all platforms. Along with each option is also its value. For example:
{
"printf": {
"LIBC_CONF_PRINTF_DISABLE_FLOAT": false
...
}
}
The above config indicates that the option LIBC_CONF_PRINTF_DISABLE_FLOAT has a value of false. A platform, say the baremetal platform, can choose to override this value in its config.json file in the config/baremetal directory with the following contents:
{
"printf": {
"LIBC_CONF_PRINTF_DISABLE_FLOAT": true
...
}
}
As you can see, the config for the baremetal platform, overrides the common false value of the LIBC_CONF_PRINTF_DISABLE_FLOAT option with the true value.
Format
Named tags
As can be noted in the above examples, config.json file contains a top-level dictionary. The keys of this dictionary are the names of grouping-tags. A grouping-tag is nothing but a named tag to refer to a group of libc options. In the above example, a tag named printf was used to refer to all libc options which affect the behavior of printf and friends.
Tag values
The value corresponding to each grouping tag is also a dictionary called the option-dictionary. The keys of the option-dictionary are the names of the libc options belonging to that grouping tag. For the printf tag in the above example, the option-dictionary is:
{
"LIBC_CONF_PRINTF_DISABLE_FLOAT": true
...
}
The value corresponding to an option key in an option-dictionary is the value to be used for that option when configuring the libc build. Options which are of ON/OFF kind take boolean values true/false. Other types of options can take an integral or string value as suitable for that option. In the above option-dictionary, the option-key LIBC_CONF_PRINTF_DISABLE_FLOAT is of boolean type with value true.
Option name format
The option names, or the keys in a option-dictionary, have the following format:
LIBC_CONF_<UPPER_CASE_TAG_NAME>_<ACTION_INDICATING_THE_INTENDED_SEMANTICS>
The option name used in the above examples, LIBC_CONF_PRINTF_DISABLE_FLOAT to disable printing of floating point numbers, follows this format: It has the prefix LIBC_CONF_, followed by the grouping-tag name in upper case, followed by the action to disable floating point number printing.
Mechanics of config application
Config reading
At libc config time, three different config.json files are read in the following order:
-
config/config.json -
config/<platform or OS>/config.jsonif present. -
config/<platform or OS>/<target arch>/config.jsonif present.
Each successive config.json file overrides the option values set by previously read config.json files. Likewise, a similarly named command line option to the cmake command will override the option values specified in all of these config.json files. That is, users will be able to override the config options from the command line.
Config application
Local to the directory where an option group is relevant, a convenience function to produce the actual compile and link options from the option values, is to be implemented. For the printf tag for example, convenience function to collect all printf related compile options should be implemented:
function(get_common_printf_compile_options opt_list)
# This function should look up the option values for the printf tag
# and populate opt_list var with compile options corresponding to
# the option values.
...
endfunction()
Option groups which affect linker options can have a similar convenience function to generate common linker options affecting that group. Once generated, the compile and linker options can be used as follows:
add_object_library(
...
COMPILE_OPTIONS
${common_printf_compile_options}
... # Other compile options affecting this target irrespective of the libc config options
)
CMake and Bazel
The JSON format was chosen for the config files because the build systems of interest for the libc project, CMake and Bazel, both support JSON parsing. However, it is not convenient to read JSON files and deduce compile options in Bazel. Nevertheless, Bazel supports Python like dictionaries as first-class data types. Hence, the configs in Bazel will be listed as Python dictionaries in .bzl files. Consequently, some amount of duplication of config listings is anticipated between Bazel and config.json files used by CMake. Specifically, the Bazel listings should mirror the listing in the config.json files.
Developer workflow around adding and maintaining libc options
The libc config options usually take effect as one or more preprocessor macros which include or exclude parts of the libc source code. In order not to make options a direct reflection of the source code mechanics, the libc developers should introduce options in the following manner:
-
First step is to concretely define the semantics of an option without mixing it up with the source code mechanics.
-
Once an option is coined and its semantics defined concretely, it should either be added to an existing option group or to a newly created option group.
-
Likewise, as necessary/required, the appropriate
config.jsonfiles should be updated with suitable values for the new option. -
The necessary code changes, by introducing appropriate preprocessor macros, should be done to make the options actually take effect in source code.
-
The
docs/libc_config.rstdocument should be updated with information about the newly introduced option. Note thatdocs/libc_config.rstdoes not exist today. It will be added as part of the rollout of this proposal.