kconfig/cmake: Improve reconfiguration behavior
There are some issues with the behavior when rerunning CMake in an already initialized build directory: 1. The check for assignments to promptless symbols in configuration fragments isn't run when reconfiguring, because it only runs if zephyr/.config doesn't exist 2. As outlined in https://github.com/zephyrproject-rtos/zephyr/issues/9573, you can get into situations where zephyr/.config is invalid (e.g. due to being outdated), but menuconfig/guiconfig can't be run to fix it 3. If kconfig.py fails while merging fragments during reconfiguration, it will ignore the fragments during the next reconfiguration and use the existing zephyr/.config instead, because the fragment checksum is calculated and saved before running kconfig.py (Footnote: The input configuration file(s) to kconfig.py can be either a list of configuration fragments, when merging fragments, or just zephyr/.config, if the merged configuration is up-to-date. The output configuration file is always zephyr/.config.) To fix the first two issues, explicitly tell kconfig.py when it's dealing with handwritten configuration input (fragments), via a new --handwritten-input-configs flag. This is more robust than checking whether zephyr/.config exists, which was the old logic. When dealing with handwritten input, there should be no assignments to promptless symbols. Assignments to promptless symbols is expected in zephyr/.config however, because it doubles as configuration output. When running menuconfig/guiconfig, the input configuration is zephyr/.config rather than configuration fragments, so this change also makes sure that menuconfig can always be run as long as zephyr/.config exists and is up-to-date. To fix the last issue, only write the checksum for the configuration fragments if kconfig.py succeeds (which means it wrote a zephyr/.config). Also improve naming a bit, add help texts for the command-line parameters to kconfig.py, and simplify write_kconfig_filenames() by moving logic into it. Partial fix for https://github.com/zephyrproject-rtos/zephyr/issues/9573, without the part in #issuecomment-469701831. Can still run into issues when e.g. when CMake files can't make sense of settings. Signed-off-by: Ulf Magnusson <Ulf.Magnusson@nordicsemi.no>
This commit is contained in:
parent
e1052a0f8d
commit
45050dda48
2 changed files with 63 additions and 48 deletions
|
@ -189,25 +189,22 @@ if(EXISTS ${DOTCONFIG} AND EXISTS ${merge_config_files_checksum_file})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CREATE_NEW_DOTCONFIG)
|
if(CREATE_NEW_DOTCONFIG)
|
||||||
file(WRITE
|
set(input_configs_are_handwritten --handwritten-input-configs)
|
||||||
${merge_config_files_checksum_file}
|
set(input_configs ${merge_config_files})
|
||||||
${merge_config_files_checksum}
|
|
||||||
)
|
|
||||||
|
|
||||||
set(merge_fragments ${merge_config_files})
|
|
||||||
else()
|
else()
|
||||||
set(merge_fragments ${DOTCONFIG})
|
set(input_configs ${DOTCONFIG})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
execute_process(
|
execute_process(
|
||||||
COMMAND
|
COMMAND
|
||||||
${PYTHON_EXECUTABLE}
|
${PYTHON_EXECUTABLE}
|
||||||
${ZEPHYR_BASE}/scripts/kconfig/kconfig.py
|
${ZEPHYR_BASE}/scripts/kconfig/kconfig.py
|
||||||
|
${input_configs_are_handwritten}
|
||||||
${KCONFIG_ROOT}
|
${KCONFIG_ROOT}
|
||||||
${DOTCONFIG}
|
${DOTCONFIG}
|
||||||
${AUTOCONF_H}
|
${AUTOCONF_H}
|
||||||
${PARSED_KCONFIG_SOURCES_TXT}
|
${PARSED_KCONFIG_SOURCES_TXT}
|
||||||
${merge_fragments}
|
${input_configs}
|
||||||
WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR}
|
WORKING_DIRECTORY ${APPLICATION_SOURCE_DIR}
|
||||||
# The working directory is set to the app dir such that the user
|
# The working directory is set to the app dir such that the user
|
||||||
# can use relative paths in CONF_FILE, e.g. CONF_FILE=nrf5.conf
|
# can use relative paths in CONF_FILE, e.g. CONF_FILE=nrf5.conf
|
||||||
|
@ -217,6 +214,14 @@ if(NOT "${ret}" STREQUAL "0")
|
||||||
message(FATAL_ERROR "command failed with return code: ${ret}")
|
message(FATAL_ERROR "command failed with return code: ${ret}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(CREATE_NEW_DOTCONFIG)
|
||||||
|
# Write the new configuration fragment checksum. Only do this if kconfig.py
|
||||||
|
# succeeds, to avoid marking zephyr/.config as up-to-date when it hasn't been
|
||||||
|
# regenerated.
|
||||||
|
file(WRITE ${merge_config_files_checksum_file}
|
||||||
|
${merge_config_files_checksum})
|
||||||
|
endif()
|
||||||
|
|
||||||
# Read out the list of 'Kconfig' sources that were used by the engine.
|
# Read out the list of 'Kconfig' sources that were used by the engine.
|
||||||
file(STRINGS ${PARSED_KCONFIG_SOURCES_TXT} PARSED_KCONFIG_SOURCES_LIST)
|
file(STRINGS ${PARSED_KCONFIG_SOURCES_TXT} PARSED_KCONFIG_SOURCES_LIST)
|
||||||
|
|
||||||
|
|
|
@ -28,31 +28,34 @@ def fatal(warning):
|
||||||
def main():
|
def main():
|
||||||
args = parse_args()
|
args = parse_args()
|
||||||
|
|
||||||
print("Parsing Kconfig tree in " + args.kconfig_root)
|
print("Parsing " + args.kconfig_file)
|
||||||
kconf = Kconfig(args.kconfig_root, warn_to_stderr=False,
|
kconf = Kconfig(args.kconfig_file, warn_to_stderr=False,
|
||||||
suppress_traceback=True)
|
suppress_traceback=True)
|
||||||
|
|
||||||
# Warn for assignments to undefined symbols
|
if args.handwritten_input_configs:
|
||||||
kconf.warn_assign_undef = True
|
# Warn for assignments to undefined symbols, but only for handwritten
|
||||||
|
# fragments, to avoid warnings-turned-errors when using an old
|
||||||
|
# configuration file together with updated Kconfig files
|
||||||
|
kconf.warn_assign_undef = True
|
||||||
|
|
||||||
# prj.conf may override settings from the board configuration, so disable
|
# prj.conf may override settings from the board configuration, so
|
||||||
# warnings about symbols being assigned more than once
|
# disable warnings about symbols being assigned more than once
|
||||||
kconf.warn_assign_override = False
|
kconf.warn_assign_override = False
|
||||||
kconf.warn_assign_redun = False
|
kconf.warn_assign_redun = False
|
||||||
|
|
||||||
print(kconf.load_config(args.conf_fragments[0]))
|
# Load configuration files
|
||||||
for config in args.conf_fragments[1:]:
|
print(kconf.load_config(args.configs_in[0]))
|
||||||
|
for config in args.configs_in[1:]:
|
||||||
# replace=False creates a merged configuration
|
# replace=False creates a merged configuration
|
||||||
print(kconf.load_config(config, replace=False))
|
print(kconf.load_config(config, replace=False))
|
||||||
|
|
||||||
if not os.path.exists(args.autoconf):
|
if args.handwritten_input_configs:
|
||||||
# If zephyr/.config does not exist, it means we just merged
|
# Check that there are no assignments to promptless symbols, which
|
||||||
# configuration fragments. Check that there were no assignments to
|
# have no effect.
|
||||||
# promptless symbols in them. Such assignments have no effect.
|
|
||||||
#
|
#
|
||||||
# This won't work if zephyr/.config already exists (which means it's
|
# This only makes sense when loading handwritten fragments and not when
|
||||||
# being loaded), because zephyr/.config is a full configuration file
|
# loading zephyr/.config, because zephyr/.config is configuration
|
||||||
# that includes values for promptless symbols.
|
# output and also assigns promptless symbols.
|
||||||
check_no_promptless_assign(kconf)
|
check_no_promptless_assign(kconf)
|
||||||
|
|
||||||
# Print warnings for symbols whose actual value doesn't match the assigned
|
# Print warnings for symbols whose actual value doesn't match the assigned
|
||||||
|
@ -97,11 +100,11 @@ point to an actual problem, you can add it to the whitelist at the top of {}.\
|
||||||
""".format(warning, sys.argv[0]))
|
""".format(warning, sys.argv[0]))
|
||||||
|
|
||||||
# Write the merged configuration and the C header
|
# Write the merged configuration and the C header
|
||||||
print(kconf.write_config(args.dotconfig))
|
print(kconf.write_config(args.config_out))
|
||||||
kconf.write_autoconf(args.autoconf)
|
kconf.write_autoconf(args.header_out)
|
||||||
|
|
||||||
# Write the list of processed Kconfig sources to a file
|
# Write the list of parsed Kconfig files to a file
|
||||||
write_kconfig_filenames(kconf.kconfig_filenames, kconf.srctree, args.sources)
|
write_kconfig_filenames(kconf, args.kconfig_list_out)
|
||||||
|
|
||||||
|
|
||||||
def check_no_promptless_assign(kconf):
|
def check_no_promptless_assign(kconf):
|
||||||
|
@ -173,32 +176,39 @@ def promptless(sym):
|
||||||
return not any(node.prompt for node in sym.nodes)
|
return not any(node.prompt for node in sym.nodes)
|
||||||
|
|
||||||
|
|
||||||
def write_kconfig_filenames(paths, root_path, output_file_path):
|
def write_kconfig_filenames(kconf, kconfig_list_path):
|
||||||
# 'paths' is a list of paths. The list has duplicates and the
|
# Writes a sorted list with the absolute paths of all parsed Kconfig files
|
||||||
# paths are either absolute or relative to 'root_path'.
|
# to 'kconfig_list_path'. The paths are realpath()'d, and duplicates are
|
||||||
|
# removed. This file is used by CMake to look for changed Kconfig files. It
|
||||||
|
# needs to be deterministic.
|
||||||
|
|
||||||
# We need to write this list, in a format that CMake can easily
|
with open(kconfig_list_path, 'w') as out:
|
||||||
# parse, to the output file at 'output_file_path'.
|
for path in sorted({os.path.realpath(os.path.join(kconf.srctree, path))
|
||||||
|
for path in kconf.kconfig_filenames}):
|
||||||
# The written list has sorted real (absolute) paths, and it does not have
|
|
||||||
# duplicates. The list is sorted to be deterministic. It is realpath()'d
|
|
||||||
# to ensure that different representations of the same path does not end
|
|
||||||
# up with two entries, as that could cause the build system to fail.
|
|
||||||
|
|
||||||
with open(output_file_path, 'w') as out:
|
|
||||||
for path in sorted({os.path.realpath(os.path.join(root_path, path))
|
|
||||||
for path in paths}):
|
|
||||||
print(path, file=out)
|
print(path, file=out)
|
||||||
|
|
||||||
|
|
||||||
def parse_args():
|
def parse_args():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
|
|
||||||
parser.add_argument("kconfig_root")
|
parser.add_argument("--handwritten-input-configs",
|
||||||
parser.add_argument("dotconfig")
|
action="store_true",
|
||||||
parser.add_argument("autoconf")
|
help="Assume the input configuration fragments are "
|
||||||
parser.add_argument("sources")
|
"handwritten fragments and do additional checks "
|
||||||
parser.add_argument("conf_fragments", nargs='+')
|
"on them, like no promptless symbols being "
|
||||||
|
"assigned")
|
||||||
|
parser.add_argument("kconfig_file",
|
||||||
|
help="Top-level Kconfig file")
|
||||||
|
parser.add_argument("config_out",
|
||||||
|
help="Output configuration file")
|
||||||
|
parser.add_argument("header_out",
|
||||||
|
help="Output header file")
|
||||||
|
parser.add_argument("kconfig_list_out",
|
||||||
|
help="Output file for list of parsed Kconfig files")
|
||||||
|
parser.add_argument("configs_in",
|
||||||
|
nargs="+",
|
||||||
|
help="Input configuration fragments. Will be merged "
|
||||||
|
"together.")
|
||||||
|
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue