
Hello Simon,
Am 15.05.2017 um 13:47 schrieb Simon Glass:
Some CONFIG options can be implied by others and this can help to reduce the size of the defconfig files. For example, CONFIG_X86 implies CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to each of the x86 defconfig files.
Add a -i option which searches for such options.
Signed-off-by: Simon Glass sjg@chromium.org
tools/moveconfig.py | 215 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 214 insertions(+), 1 deletion(-)
Thanks!
Reviewed-by: Heiko Schocher hs@denx.de
bye, Heiko
diff --git a/tools/moveconfig.py b/tools/moveconfig.py index ed576f4b83..f33203d51b 100755 --- a/tools/moveconfig.py +++ b/tools/moveconfig.py @@ -128,6 +128,69 @@ To process CONFIG_CMD_FPGAD only for a subset of configs based on path match: ./tools/moveconfig.py -Cy CONFIG_CMD_FPGAD -d -
+Finding implied CONFIGs +-----------------------
+Some CONFIG options can be implied by others and this can help to reduce +the size of the defconfig files. For example, CONFIG_X86 implies +CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and +all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to +each of the x86 defconfig files.
+This tool can help find such configs. To use it, first build a database:
- ./tools/moveconfig.py -b
+Then try to query it:
- ./tools/moveconfig.py -i CONFIG_CMD_IRQ
- CONFIG_CMD_IRQ found in 311/2384 defconfigs
- 44 : CONFIG_SYS_FSL_ERRATUM_IFC_A002769
- 41 : CONFIG_SYS_FSL_ERRATUM_A007075
- 31 : CONFIG_SYS_FSL_DDR_VER_44
- 28 : CONFIG_ARCH_P1010
- 28 : CONFIG_SYS_FSL_ERRATUM_P1010_A003549
- 28 : CONFIG_SYS_FSL_ERRATUM_SEC_A003571
- 28 : CONFIG_SYS_FSL_ERRATUM_IFC_A003399
- 25 : CONFIG_SYS_FSL_ERRATUM_A008044
- 22 : CONFIG_ARCH_P1020
- 21 : CONFIG_SYS_FSL_DDR_VER_46
- 20 : CONFIG_MAX_PIRQ_LINKS
- 20 : CONFIG_HPET_ADDRESS
- 20 : CONFIG_X86
- 20 : CONFIG_PCIE_ECAM_SIZE
- 20 : CONFIG_IRQ_SLOT_COUNT
- 20 : CONFIG_I8259_PIC
- 20 : CONFIG_CPU_ADDR_BITS
- 20 : CONFIG_RAMBASE
- 20 : CONFIG_SYS_FSL_ERRATUM_A005871
- 20 : CONFIG_PCIE_ECAM_BASE
- 20 : CONFIG_X86_TSC_TIMER
- 20 : CONFIG_I8254_TIMER
- 20 : CONFIG_CMD_GETTIME
- 19 : CONFIG_SYS_FSL_ERRATUM_A005812
- 18 : CONFIG_X86_RUN_32BIT
- 17 : CONFIG_CMD_CHIP_CONFIG
- ...
+This shows a list of config options which might imply CONFIG_CMD_EEPROM along +with how many defconfigs they cover. From this you can see that CONFIG_X86 +implies CONFIG_CMD_EEPROM. Therefore, instead of adding CONFIG_CMD_EEPROM to +the defconfig of every x86 board, you could add a single imply line to the +Kconfig file:
- config X86
bool "x86 architecture"
...
imply CMD_EEPROM
+That will cover 20 defconfigs. Many of the options listed are not suitable as +they are not related. E.g. it would be odd for CONFIG_CMD_GETTIME to imply +CMD_EEPROM.
+Using this search you can reduce the size of moveconfig patches.
Available options
@@ -191,6 +254,7 @@ To see the complete list of supported options, run
"""
+import collections import copy import difflib import filecmp @@ -1395,6 +1459,148 @@ def move_config(configs, options, db_queue): slots.show_failed_boards() slots.show_suspicious_boards()
+def imply_config(config_list, find_superset=False):
- """Find CONFIG options which imply those in the list
- Some CONFIG options can be implied by others and this can help to reduce
- the size of the defconfig files. For example, CONFIG_X86 implies
- CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
- all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
- each of the x86 defconfig files.
- This function uses the moveconfig database to find such options. It
- displays a list of things that could possibly imply those in the list.
- The algorithm ignores any that start with CONFIG_TARGET since these
- typically refer to only a few defconfigs (often one). It also does not
- display a config with less than 5 defconfigs.
- The algorithm works using sets. For each target config in config_list:
- Get the set 'defconfigs' which use that target config
- For each config (from a list of all configs):
- Get the set 'imply_defconfig' of defconfigs which use that config
-
- If imply_defconfigs contains anything not in defconfigs then
this config does not imply the target config
- Params:
config_list: List of CONFIG options to check (each a string)
find_superset: True to look for configs which are a superset of those
already found. So for example if CONFIG_EXYNOS5 implies an option,
but CONFIG_EXYNOS covers a larger set of defconfigs and also
implies that option, this will drop the former in favour of the
latter. In practice this option has not proved very used.
- Note the terminoloy:
config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
- """
- # key is defconfig name, value is dict of (CONFIG_xxx, value)
- config_db = {}
- # Holds a dict containing the set of defconfigs that contain each config
- # key is config, value is set of defconfigs using that config
- defconfig_db = collections.defaultdict(set)
- # Set of all config options we have seen
- all_configs = set()
- # Set of all defconfigs we have seen
- all_defconfigs = set()
- # Read in the database
- configs = {}
- with open(CONFIG_DATABASE) as fd:
for line in fd.readlines():
line = line.rstrip()
if not line: # Separator between defconfigs
config_db[defconfig] = configs
all_defconfigs.add(defconfig)
configs = {}
elif line[0] == ' ': # CONFIG line
config, value = line.strip().split('=', 1)
configs[config] = value
defconfig_db[config].add(defconfig)
all_configs.add(config)
else: # New defconfig
defconfig = line
- # Work through each target config option in tern, independently
- for config in config_list:
defconfigs = defconfig_db.get(config)
if not defconfigs:
print '%s not found in any defconfig' % config
continue
# Get the set of defconfigs without this one (since a config cannot
# imply itself)
non_defconfigs = all_defconfigs - defconfigs
num_defconfigs = len(defconfigs)
print '%s found in %d/%d defconfigs' % (config, num_defconfigs,
len(all_configs))
# This will hold the results: key=config, value=defconfigs containing it
imply_configs = {}
rest_configs = all_configs - set([config])
# Look at every possible config, except the target one
for imply_config in rest_configs:
if 'CONFIG_TARGET' in imply_config:
continue
# Find set of defconfigs that have this config
imply_defconfig = defconfig_db[imply_config]
# Get the intersection of this with defconfigs containing the
# target config
common_defconfigs = imply_defconfig & defconfigs
# Get the set of defconfigs containing this config which DO NOT
# also contain the taret config. If this set is non-empty it means
# that this config affects other defconfigs as well as (possibly)
# the ones affected by the target config. This means it implies
# things we don't want to imply.
not_common_defconfigs = imply_defconfig & non_defconfigs
if not_common_defconfigs:
continue
# If there are common defconfigs, imply_config may be useful
if common_defconfigs:
skip = False
if find_superset:
for prev in imply_configs.keys():
prev_count = len(imply_configs[prev])
count = len(common_defconfigs)
if (prev_count > count and
(imply_configs[prev] & common_defconfigs ==
common_defconfigs)):
# skip imply_config because prev is a superset
skip = True
break
elif count > prev_count:
# delete prev because imply_config is a superset
del imply_configs[prev]
if not skip:
imply_configs[imply_config] = common_defconfigs
# Now we have a dict imply_configs of configs which imply each config
# The value of each dict item is the set of defconfigs containing that
# config. Rank them so that we print the configs that imply the largest
# number of defconfigs first.
ranked_configs = sorted(imply_configs,
key=lambda k: len(imply_configs[k]), reverse=True)
for config in ranked_configs:
num_common = len(imply_configs[config])
# Don't bother if there are less than 5 defconfigs affected.
if num_common < 5:
continue
missing = defconfigs - imply_configs[config]
missing_str = ', '.join(missing) if missing else 'all'
missing_str = ''
print ' %d : %-30s%s' % (num_common, config.ljust(30),
missing_str)
- def main(): try: cpu_count = multiprocessing.cpu_count()
@@ -1413,6 +1619,8 @@ def main(): help='a file containing a list of defconfigs to move, ' "one per line (for example 'snow_defconfig') " "or '-' to read from stdin")
- parser.add_option('-i', '--imply', action='store_true', default=False,
help='find options which imply others') parser.add_option('-n', '--dry-run', action='store_true', default=False, help='perform a trial run (show log with no changes)') parser.add_option('-e', '--exit-on-error', action='store_true',
@@ -1437,7 +1645,8 @@ def main():
(options, configs) = parser.parse_args()
- if len(configs) == 0 and not any((options.force_sync, options.build_db)):
- if len(configs) == 0 and not any((options.force_sync, options.build_db,
options.imply)): parser.print_usage() sys.exit(1)
@@ -1447,6 +1656,10 @@ def main():
check_top_directory()
- if options.imply:
imply_config(configs)
return
config_db = {} db_queue = Queue.Queue() t = DatabaseThread(config_db, db_queue)