Appropriate toolchain are necessary to generate include/autoconf.mk
for all the architectures supported by U-Boot. Most of them are available
-at the kernel.org site, some are not provided by kernel.org.
-
-The default per-arch CROSS_COMPILE used by this tool is specified by
-the list below, CROSS_COMPILE. You may wish to update the list to
-use your own. Instead of modifying the list directly, you can give
-them via environments.
+at the kernel.org site, some are not provided by kernel.org. This tool uses
+the same tools as buildman, so see that tool for setup (e.g. --fetch-arch).
Tips and trips
Using this search you can reduce the size of moveconfig patches.
+You can automatically add 'imply' statements in the Kconfig with the -a
+option:
+
+ ./tools/moveconfig.py -s -i CONFIG_SCSI \
+ -a CONFIG_ARCH_LS1021A,CONFIG_ARCH_LS1043A
+
+This will add 'imply SCSI' to the two CONFIG options mentioned, assuming that
+the database indicates that they do actually imply CONFIG_SCSI and do not
+already have an 'imply SCSI'.
+
+The output shows where the imply is added:
+
+ 18 : CONFIG_ARCH_LS1021A arch/arm/cpu/armv7/ls102xa/Kconfig:1
+ 13 : CONFIG_ARCH_LS1043A arch/arm/cpu/armv8/fsl-layerscape/Kconfig:11
+ 12 : CONFIG_ARCH_LS1046A arch/arm/cpu/armv8/fsl-layerscape/Kconfig:31
+
+The first number is the number of boards which can avoid having a special
+CONFIG_SCSI option in their defconfig file if this 'imply' is added.
+The location at the right is the Kconfig file and line number where the config
+appears. For example, adding 'imply CONFIG_SCSI' to the 'config ARCH_LS1021A'
+in arch/arm/cpu/armv7/ls102xa/Kconfig at line 1 will help 18 boards to reduce
+the size of their defconfig files.
+
+If you want to add an 'imply' to every imply config in the list, you can use
+
+ ./tools/moveconfig.py -s -i CONFIG_SCSI -a all
+
+To control which ones are displayed, use -I <list> where list is a list of
+options (use '-I help' to see possible options and their meaning).
+
+To skip showing you options that already have an 'imply' attached, use -A.
+
+When you have finished adding 'imply' options you can regenerate the
+defconfig files for affected boards with something like:
+
+ git show --stat | ./tools/moveconfig.py -s -d -
+
+This will regenerate only those defconfigs changed in the current commit.
+If you start with (say) 100 defconfigs being changed in the commit, and add
+a few 'imply' options as above, then regenerate, hopefully you can reduce the
+number of defconfigs changed in the commit.
+
Available options
-----------------
import threading
import time
+sys.path.append(os.path.join(os.path.dirname(__file__), 'buildman'))
+sys.path.append(os.path.join(os.path.dirname(__file__), 'patman'))
+import bsettings
+import kconfiglib
+import toolchain
+
SHOW_GNU_MAKE = 'scripts/show-gnu-make'
SLEEP_TIME=0.03
-# Here is the list of cross-tools I use.
-# Most of them are available at kernel.org
-# (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the following:
-# arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
-# nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
-# nios2: https://sourcery.mentor.com/GNUToolchain/subscription42545
-# sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu
-CROSS_COMPILE = {
- 'arc': 'arc-linux-',
- 'aarch64': 'aarch64-linux-',
- 'arm': 'arm-unknown-linux-gnueabi-',
- 'm68k': 'm68k-linux-',
- 'microblaze': 'microblaze-linux-',
- 'mips': 'mips-linux-',
- 'nds32': 'nds32le-linux-',
- 'nios2': 'nios2-linux-gnu-',
- 'powerpc': 'powerpc-linux-',
- 'sh': 'sh-linux-gnu-',
- 'x86': 'i386-linux-',
- 'xtensa': 'xtensa-linux-'
-}
-
STATE_IDLE = 0
STATE_DEFCONFIG = 1
STATE_AUTOCONF = 2
AUTO_CONF_PATH = 'include/config/auto.conf'
CONFIG_DATABASE = 'moveconfig.db'
+CONFIG_LEN = len('CONFIG_')
### helper functions ###
def get_devnull():
line = line.strip()
if not line:
continue # skip blank lines silently
+ if ' ' in line:
+ line = line.split(' ')[0] # handle 'git log' input
matched = get_matched_defconfig(line)
if not matched:
print >> sys.stderr, "warning: %s:%d: no defconfig matched '%s'" % \
else:
print line,
-def update_cross_compile(color_enabled):
- """Update per-arch CROSS_COMPILE via environment variables
-
- The default CROSS_COMPILE values are available
- in the CROSS_COMPILE list above.
-
- You can override them via environment variables
- CROSS_COMPILE_{ARCH}.
-
- For example, if you want to override toolchain prefixes
- for ARM and PowerPC, you can do as follows in your shell:
-
- export CROSS_COMPILE_ARM=...
- export CROSS_COMPILE_POWERPC=...
-
- Then, this function checks if specified compilers really exist in your
- PATH environment.
- """
- archs = []
-
- for arch in os.listdir('arch'):
- if os.path.exists(os.path.join('arch', arch, 'Makefile')):
- archs.append(arch)
-
- # arm64 is a special case
- archs.append('aarch64')
-
- for arch in archs:
- env = 'CROSS_COMPILE_' + arch.upper()
- cross_compile = os.environ.get(env)
- if not cross_compile:
- cross_compile = CROSS_COMPILE.get(arch, '')
-
- for path in os.environ["PATH"].split(os.pathsep):
- gcc_path = os.path.join(path, cross_compile + 'gcc')
- if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
- break
- else:
- print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
- 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
- % (cross_compile, arch))
- cross_compile = None
-
- CROSS_COMPILE[arch] = cross_compile
-
def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
extend_post):
"""Extend matched lines if desired patterns are found before/after already
print ' %d defconfigs out of %d\r' % (self.current, self.total),
sys.stdout.flush()
+
+class KconfigScanner:
+ """Kconfig scanner."""
+
+ def __init__(self):
+ """Scan all the Kconfig files and create a Config object."""
+ # Define environment variables referenced from Kconfig
+ os.environ['srctree'] = os.getcwd()
+ os.environ['UBOOTVERSION'] = 'dummy'
+ os.environ['KCONFIG_OBJDIR'] = ''
+ self.conf = kconfiglib.Config()
+
+
class KconfigParser:
"""A parser of .config and include/autoconf.mk."""
self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
self.defconfig = os.path.join(build_dir, 'defconfig')
- def get_cross_compile(self):
- """Parse .config file and return CROSS_COMPILE.
+ def get_arch(self):
+ """Parse .config file and return the architecture.
Returns:
- A string storing the compiler prefix for the architecture.
- Return a NULL string for architectures that do not require
- compiler prefix (Sandbox and native build is the case).
- Return None if the specified compiler is missing in your PATH.
- Caller should distinguish '' and None.
+ Architecture name (e.g. 'arm').
"""
arch = ''
cpu = ''
if arch == 'arm' and cpu == 'armv8':
arch = 'aarch64'
- return CROSS_COMPILE.get(arch, None)
+ return arch
def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
"""Parse .config, defconfig, include/autoconf.mk for one config.
for faster processing.
"""
- def __init__(self, configs, options, progress, devnull, make_cmd,
- reference_src_dir, db_queue):
+ def __init__(self, toolchains, configs, options, progress, devnull,
+ make_cmd, reference_src_dir, db_queue):
"""Create a new process slot.
Arguments:
+ toolchains: Toolchains object containing toolchains.
configs: A list of CONFIGs to move.
options: option flags.
progress: A progress indicator.
source tree.
db_queue: output queue to write config info for the database
"""
+ self.toolchains = toolchains
self.options = options
self.progress = progress
self.build_dir = tempfile.mkdtemp()
def do_autoconf(self):
"""Run 'make AUTO_CONF_PATH'."""
- self.cross_compile = self.parser.get_cross_compile()
- if self.cross_compile is None:
+ arch = self.parser.get_arch()
+ try:
+ toolchain = self.toolchains.Select(arch)
+ except ValueError:
self.log += color_text(self.options.color, COLOR_YELLOW,
- "Compiler is missing. Do nothing.\n")
+ "Tool chain for '%s' is missing. Do nothing.\n" % arch)
self.finish(False)
return
+ env = toolchain.MakeEnvironment(False)
cmd = list(self.make_cmd)
- if self.cross_compile:
- cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
cmd.append(AUTO_CONF_PATH)
- self.ps = subprocess.Popen(cmd, stdout=self.devnull,
+ self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
stderr=subprocess.PIPE,
cwd=self.current_src_dir)
self.state = STATE_AUTOCONF
"""Controller of the array of subprocess slots."""
- def __init__(self, configs, options, progress, reference_src_dir, db_queue):
+ def __init__(self, toolchains, configs, options, progress,
+ reference_src_dir, db_queue):
"""Create a new slots controller.
Arguments:
+ toolchains: Toolchains object containing toolchains.
configs: A list of CONFIGs to move.
options: option flags.
progress: A progress indicator.
devnull = get_devnull()
make_cmd = get_make_cmd()
for i in range(options.jobs):
- self.slots.append(Slot(configs, options, progress, devnull,
- make_cmd, reference_src_dir, db_queue))
+ self.slots.append(Slot(toolchains, configs, options, progress,
+ devnull, make_cmd, reference_src_dir,
+ db_queue))
def add(self, defconfig):
"""Add a new subprocess if a vacant slot is found.
return self.src_dir
-def move_config(configs, options, db_queue):
+def move_config(toolchains, configs, options, db_queue):
"""Move config options to defconfig files.
Arguments:
defconfigs = get_all_defconfigs()
progress = Progress(len(defconfigs))
- slots = Slots(configs, options, progress, reference_src_dir, db_queue)
+ slots = Slots(toolchains, configs, options, progress, reference_src_dir,
+ db_queue)
# Main loop to process defconfig files:
# Add a new subprocess into a vacant slot.
slots.show_failed_boards()
slots.show_suspicious_boards()
-def imply_config(config_list, find_superset=False):
+def find_kconfig_rules(kconf, config, imply_config):
+ """Check whether a config has a 'select' or 'imply' keyword
+
+ Args:
+ kconf: Kconfig.Config object
+ config: Name of config to check (without CONFIG_ prefix)
+ imply_config: Implying config (without CONFIG_ prefix) which may or
+ may not have an 'imply' for 'config')
+
+ Returns:
+ Symbol object for 'config' if found, else None
+ """
+ sym = kconf.get_symbol(imply_config)
+ if sym:
+ for sel in sym.get_selected_symbols() | sym.get_implied_symbols():
+ if sel.get_name() == config:
+ return sym
+ return None
+
+def check_imply_rule(kconf, config, imply_config):
+ """Check if we can add an 'imply' option
+
+ This finds imply_config in the Kconfig and looks to see if it is possible
+ to add an 'imply' for 'config' to that part of the Kconfig.
+
+ Args:
+ kconf: Kconfig.Config object
+ config: Name of config to check (without CONFIG_ prefix)
+ imply_config: Implying config (without CONFIG_ prefix) which may or
+ may not have an 'imply' for 'config')
+
+ Returns:
+ tuple:
+ filename of Kconfig file containing imply_config, or None if none
+ line number within the Kconfig file, or 0 if none
+ message indicating the result
+ """
+ sym = kconf.get_symbol(imply_config)
+ if not sym:
+ return 'cannot find sym'
+ locs = sym.get_def_locations()
+ if len(locs) != 1:
+ return '%d locations' % len(locs)
+ fname, linenum = locs[0]
+ cwd = os.getcwd()
+ if cwd and fname.startswith(cwd):
+ fname = fname[len(cwd) + 1:]
+ file_line = ' at %s:%d' % (fname, linenum)
+ with open(fname) as fd:
+ data = fd.read().splitlines()
+ if data[linenum - 1] != 'config %s' % imply_config:
+ return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
+ return fname, linenum, 'adding%s' % file_line
+
+def add_imply_rule(config, fname, linenum):
+ """Add a new 'imply' option to a Kconfig
+
+ Args:
+ config: config option to add an imply for (without CONFIG_ prefix)
+ fname: Kconfig filename to update
+ linenum: Line number to place the 'imply' before
+
+ Returns:
+ Message indicating the result
+ """
+ file_line = ' at %s:%d' % (fname, linenum)
+ data = open(fname).read().splitlines()
+ linenum -= 1
+
+ for offset, line in enumerate(data[linenum:]):
+ if line.strip().startswith('help') or not line:
+ data.insert(linenum + offset, '\timply %s' % config)
+ with open(fname, 'w') as fd:
+ fd.write('\n'.join(data) + '\n')
+ return 'added%s' % file_line
+
+ return 'could not insert%s'
+
+(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
+ 1, 2, 4, 8)
+
+IMPLY_FLAGS = {
+ 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
+ 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
+ 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
+ 'non-arch-board': [
+ IMPLY_NON_ARCH_BOARD,
+ 'Allow Kconfig options outside arch/ and /board/ to imply'],
+};
+
+def do_imply_config(config_list, add_imply, imply_flags, skip_added,
+ check_kconfig=True, 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
Params:
config_list: List of CONFIG options to check (each a string)
+ add_imply: Automatically add an 'imply' for each config.
+ imply_flags: Flags which control which implying configs are allowed
+ (IMPLY_...)
+ skip_added: Don't show options which already have an imply added.
+ check_kconfig: Check if implied symbols already have an 'imply' or
+ 'select' for the target config, and show this information if so.
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
config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
"""
+ kconf = KconfigScanner().conf if check_kconfig else None
+ if add_imply and add_imply != 'all':
+ add_imply = add_imply.split()
+
# key is defconfig name, value is dict of (CONFIG_xxx, value)
config_db = {}
# Look at every possible config, except the target one
for imply_config in rest_configs:
- if 'CONFIG_TARGET' in imply_config:
+ if 'ERRATUM' in imply_config:
continue
+ if not (imply_flags & IMPLY_CMD):
+ if 'CONFIG_CMD' in imply_config:
+ continue
+ if not (imply_flags & IMPLY_TARGET):
+ if 'CONFIG_TARGET' in imply_config:
+ continue
# Find set of defconfigs that have this config
imply_defconfig = defconfig_db[imply_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,
+ ranked_iconfigs = sorted(imply_configs,
key=lambda k: len(imply_configs[k]), reverse=True)
- for config in ranked_configs:
- num_common = len(imply_configs[config])
+ kconfig_info = ''
+ cwd = os.getcwd()
+ add_list = collections.defaultdict(list)
+ for iconfig in ranked_iconfigs:
+ num_common = len(imply_configs[iconfig])
# Don't bother if there are less than 5 defconfigs affected.
- if num_common < 5:
+ if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
continue
- missing = defconfigs - imply_configs[config]
+ missing = defconfigs - imply_configs[iconfig]
missing_str = ', '.join(missing) if missing else 'all'
missing_str = ''
- print ' %d : %-30s%s' % (num_common, config.ljust(30),
- missing_str)
+ show = True
+ if kconf:
+ sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
+ iconfig[CONFIG_LEN:])
+ kconfig_info = ''
+ if sym:
+ locs = sym.get_def_locations()
+ if len(locs) == 1:
+ fname, linenum = locs[0]
+ if cwd and fname.startswith(cwd):
+ fname = fname[len(cwd) + 1:]
+ kconfig_info = '%s:%d' % (fname, linenum)
+ if skip_added:
+ show = False
+ else:
+ sym = kconf.get_symbol(iconfig[CONFIG_LEN:])
+ fname = ''
+ if sym:
+ locs = sym.get_def_locations()
+ if len(locs) == 1:
+ fname, linenum = locs[0]
+ if cwd and fname.startswith(cwd):
+ fname = fname[len(cwd) + 1:]
+ in_arch_board = not sym or (fname.startswith('arch') or
+ fname.startswith('board'))
+ if (not in_arch_board and
+ not (imply_flags & IMPLY_NON_ARCH_BOARD)):
+ continue
+
+ if add_imply and (add_imply == 'all' or
+ iconfig in add_imply):
+ fname, linenum, kconfig_info = (check_imply_rule(kconf,
+ config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
+ if fname:
+ add_list[fname].append(linenum)
+
+ if show and kconfig_info != 'skip':
+ print '%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
+ kconfig_info, missing_str)
+
+ # Having collected a list of things to add, now we add them. We process
+ # each file from the largest line number to the smallest so that
+ # earlier additions do not affect our line numbers. E.g. if we added an
+ # imply at line 20 it would change the position of each line after
+ # that.
+ for fname, linenums in add_list.iteritems():
+ for linenum in sorted(linenums, reverse=True):
+ add_imply_rule(config[CONFIG_LEN:], fname, linenum)
def main():
parser = optparse.OptionParser()
# Add options here
+ parser.add_option('-a', '--add-imply', type='string', default='',
+ help='comma-separated list of CONFIG options to add '
+ "an 'imply' statement to for the CONFIG in -i")
+ parser.add_option('-A', '--skip-added', action='store_true', default=False,
+ help="don't show options which are already marked as "
+ 'implying others')
parser.add_option('-b', '--build-db', action='store_true', default=False,
help='build a CONFIG database')
parser.add_option('-c', '--color', action='store_true', default=False,
"or '-' to read from stdin")
parser.add_option('-i', '--imply', action='store_true', default=False,
help='find options which imply others')
+ parser.add_option('-I', '--imply-flags', type='string', default='',
+ help="control the -i option ('help' for help")
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',
check_top_directory()
if options.imply:
- imply_config(configs)
+ imply_flags = 0
+ if options.imply_flags == 'all':
+ imply_flags = -1
+
+ elif options.imply_flags:
+ for flag in options.imply_flags.split(','):
+ bad = flag not in IMPLY_FLAGS
+ if bad:
+ print "Invalid flag '%s'" % flag
+ if flag == 'help' or bad:
+ print "Imply flags: (separate with ',')"
+ for name, info in IMPLY_FLAGS.iteritems():
+ print ' %-15s: %s' % (name, info[1])
+ parser.print_usage()
+ sys.exit(1)
+ imply_flags |= IMPLY_FLAGS[flag][0]
+
+ do_imply_config(configs, options.add_imply, imply_flags,
+ options.skip_added)
return
config_db = {}
if not options.cleanup_headers_only:
check_clean_directory()
- update_cross_compile(options.color)
- move_config(configs, options, db_queue)
+ bsettings.Setup('')
+ toolchains = toolchain.Toolchains()
+ toolchains.GetSettings()
+ toolchains.Scan(verbose=False)
+ move_config(toolchains, configs, options, db_queue)
db_queue.join()
if configs:
if options.build_db:
with open(CONFIG_DATABASE, 'w') as fd:
for defconfig, configs in config_db.iteritems():
- print >>fd, '%s' % defconfig
+ fd.write('%s\n' % defconfig)
for config in sorted(configs.keys()):
- print >>fd, ' %s=%s' % (config, configs[config])
- print >>fd
+ fd.write(' %s=%s\n' % (config, configs[config]))
+ fd.write('\n')
if __name__ == '__main__':
main()