blob: d8bf7fd0717fe1c405317e9e5ea95698a50ecefc [file] [log] [blame]
Simon Glass1f701862019-10-31 07:42:57 -06001#!/usr/bin/env python3
Tom Rini10e47792018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Masahiro Yamadab6160812015-05-20 11:36:07 +09003#
4# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5#
Masahiro Yamadab6160812015-05-20 11:36:07 +09006
7"""
8Move config options from headers to defconfig files.
9
10Since Kconfig was introduced to U-Boot, we have worked on moving
11config options from headers to Kconfig (defconfig).
12
13This tool intends to help this tremendous work.
14
15
16Usage
17-----
18
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090019First, you must edit the Kconfig to add the menu entries for the configs
Joe Hershberger166edec2015-05-19 13:21:17 -050020you are moving.
21
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090022And then run this tool giving CONFIG names you want to move.
23For example, if you want to move CONFIG_CMD_USB and CONFIG_SYS_TEXT_BASE,
24simply type as follows:
Masahiro Yamadab6160812015-05-20 11:36:07 +090025
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090026 $ tools/moveconfig.py CONFIG_CMD_USB CONFIG_SYS_TEXT_BASE
Masahiro Yamadab6160812015-05-20 11:36:07 +090027
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090028The tool walks through all the defconfig files and move the given CONFIGs.
Masahiro Yamadab6160812015-05-20 11:36:07 +090029
30The log is also displayed on the terminal.
31
Masahiro Yamada465b7c02016-05-19 15:52:02 +090032The log is printed for each defconfig as follows:
Masahiro Yamadab6160812015-05-20 11:36:07 +090033
Masahiro Yamada465b7c02016-05-19 15:52:02 +090034<defconfig_name>
35 <action1>
36 <action2>
37 <action3>
38 ...
Masahiro Yamadab6160812015-05-20 11:36:07 +090039
Masahiro Yamada465b7c02016-05-19 15:52:02 +090040<defconfig_name> is the name of the defconfig.
41
42<action*> shows what the tool did for that defconfig.
Masahiro Yamada7facf882016-08-21 16:12:36 +090043It looks like one of the following:
Masahiro Yamadab6160812015-05-20 11:36:07 +090044
45 - Move 'CONFIG_... '
46 This config option was moved to the defconfig
47
Masahiro Yamada5643d6e2016-05-19 15:51:56 +090048 - CONFIG_... is not defined in Kconfig. Do nothing.
Masahiro Yamada35204d92016-08-22 22:18:21 +090049 The entry for this CONFIG was not found in Kconfig. The option is not
50 defined in the config header, either. So, this case can be just skipped.
51
52 - CONFIG_... is not defined in Kconfig (suspicious). Do nothing.
53 This option is defined in the config header, but its entry was not found
54 in Kconfig.
Masahiro Yamada5643d6e2016-05-19 15:51:56 +090055 There are two common cases:
56 - You forgot to create an entry for the CONFIG before running
57 this tool, or made a typo in a CONFIG passed to this tool.
58 - The entry was hidden due to unmet 'depends on'.
Masahiro Yamada35204d92016-08-22 22:18:21 +090059 The tool does not know if the result is reasonable, so please check it
60 manually.
Masahiro Yamadab6160812015-05-20 11:36:07 +090061
Masahiro Yamada5643d6e2016-05-19 15:51:56 +090062 - 'CONFIG_...' is the same as the define in Kconfig. Do nothing.
63 The define in the config header matched the one in Kconfig.
64 We do not need to touch it.
Masahiro Yamadab6160812015-05-20 11:36:07 +090065
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +090066 - Compiler is missing. Do nothing.
67 The compiler specified for this architecture was not found
68 in your PATH environment.
69 (If -e option is passed, the tool exits immediately.)
70
71 - Failed to process.
Masahiro Yamadab6160812015-05-20 11:36:07 +090072 An error occurred during processing this defconfig. Skipped.
73 (If -e option is passed, the tool exits immediately on error.)
74
75Finally, you will be asked, Clean up headers? [y/n]:
76
77If you say 'y' here, the unnecessary config defines are removed
78from the config headers (include/configs/*.h).
79It just uses the regex method, so you should not rely on it.
80Just in case, please do 'git diff' to see what happened.
81
82
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090083How does it work?
84-----------------
Masahiro Yamadab6160812015-05-20 11:36:07 +090085
86This tool runs configuration and builds include/autoconf.mk for every
87defconfig. The config options defined in Kconfig appear in the .config
88file (unless they are hidden because of unmet dependency.)
89On the other hand, the config options defined by board headers are seen
90in include/autoconf.mk. The tool looks for the specified options in both
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090091of them to decide the appropriate action for the options. If the given
92config option is found in the .config, but its value does not match the
93one from the board header, the config option in the .config is replaced
94with the define in the board header. Then, the .config is synced by
95"make savedefconfig" and the defconfig is updated with it.
Masahiro Yamadab6160812015-05-20 11:36:07 +090096
97For faster processing, this tool handles multi-threading. It creates
98separate build directories where the out-of-tree build is run. The
99temporary build directories are automatically created and deleted as
100needed. The number of threads are chosen based on the number of the CPU
101cores of your system although you can change it via -j (--jobs) option.
102
103
104Toolchains
105----------
106
107Appropriate toolchain are necessary to generate include/autoconf.mk
108for all the architectures supported by U-Boot. Most of them are available
Simon Glass257f5232017-07-10 14:47:47 -0600109at the kernel.org site, some are not provided by kernel.org. This tool uses
110the same tools as buildman, so see that tool for setup (e.g. --fetch-arch).
Masahiro Yamadab6160812015-05-20 11:36:07 +0900111
Masahiro Yamadab6160812015-05-20 11:36:07 +0900112
Simon Glass2d79f702017-06-01 19:39:00 -0600113Tips and trips
114--------------
115
116To sync only X86 defconfigs:
117
118 ./tools/moveconfig.py -s -d <(grep -l X86 configs/*)
119
120or:
121
122 grep -l X86 configs/* | ./tools/moveconfig.py -s -d -
123
124To process CONFIG_CMD_FPGAD only for a subset of configs based on path match:
125
126 ls configs/{hrcon*,iocon*,strider*} | \
127 ./tools/moveconfig.py -Cy CONFIG_CMD_FPGAD -d -
128
129
Simon Glassc6e73cf2017-06-01 19:39:03 -0600130Finding implied CONFIGs
131-----------------------
132
133Some CONFIG options can be implied by others and this can help to reduce
134the size of the defconfig files. For example, CONFIG_X86 implies
135CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
136all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
137each of the x86 defconfig files.
138
139This tool can help find such configs. To use it, first build a database:
140
141 ./tools/moveconfig.py -b
142
143Then try to query it:
144
145 ./tools/moveconfig.py -i CONFIG_CMD_IRQ
146 CONFIG_CMD_IRQ found in 311/2384 defconfigs
147 44 : CONFIG_SYS_FSL_ERRATUM_IFC_A002769
148 41 : CONFIG_SYS_FSL_ERRATUM_A007075
149 31 : CONFIG_SYS_FSL_DDR_VER_44
150 28 : CONFIG_ARCH_P1010
151 28 : CONFIG_SYS_FSL_ERRATUM_P1010_A003549
152 28 : CONFIG_SYS_FSL_ERRATUM_SEC_A003571
153 28 : CONFIG_SYS_FSL_ERRATUM_IFC_A003399
154 25 : CONFIG_SYS_FSL_ERRATUM_A008044
155 22 : CONFIG_ARCH_P1020
156 21 : CONFIG_SYS_FSL_DDR_VER_46
157 20 : CONFIG_MAX_PIRQ_LINKS
158 20 : CONFIG_HPET_ADDRESS
159 20 : CONFIG_X86
160 20 : CONFIG_PCIE_ECAM_SIZE
161 20 : CONFIG_IRQ_SLOT_COUNT
162 20 : CONFIG_I8259_PIC
163 20 : CONFIG_CPU_ADDR_BITS
164 20 : CONFIG_RAMBASE
165 20 : CONFIG_SYS_FSL_ERRATUM_A005871
166 20 : CONFIG_PCIE_ECAM_BASE
167 20 : CONFIG_X86_TSC_TIMER
168 20 : CONFIG_I8254_TIMER
169 20 : CONFIG_CMD_GETTIME
170 19 : CONFIG_SYS_FSL_ERRATUM_A005812
171 18 : CONFIG_X86_RUN_32BIT
172 17 : CONFIG_CMD_CHIP_CONFIG
173 ...
174
175This shows a list of config options which might imply CONFIG_CMD_EEPROM along
176with how many defconfigs they cover. From this you can see that CONFIG_X86
177implies CONFIG_CMD_EEPROM. Therefore, instead of adding CONFIG_CMD_EEPROM to
178the defconfig of every x86 board, you could add a single imply line to the
179Kconfig file:
180
181 config X86
182 bool "x86 architecture"
183 ...
184 imply CMD_EEPROM
185
186That will cover 20 defconfigs. Many of the options listed are not suitable as
187they are not related. E.g. it would be odd for CONFIG_CMD_GETTIME to imply
188CMD_EEPROM.
189
190Using this search you can reduce the size of moveconfig patches.
191
Simon Glass44116332017-06-15 21:39:33 -0600192You can automatically add 'imply' statements in the Kconfig with the -a
193option:
194
195 ./tools/moveconfig.py -s -i CONFIG_SCSI \
196 -a CONFIG_ARCH_LS1021A,CONFIG_ARCH_LS1043A
197
198This will add 'imply SCSI' to the two CONFIG options mentioned, assuming that
199the database indicates that they do actually imply CONFIG_SCSI and do not
200already have an 'imply SCSI'.
201
202The output shows where the imply is added:
203
204 18 : CONFIG_ARCH_LS1021A arch/arm/cpu/armv7/ls102xa/Kconfig:1
205 13 : CONFIG_ARCH_LS1043A arch/arm/cpu/armv8/fsl-layerscape/Kconfig:11
206 12 : CONFIG_ARCH_LS1046A arch/arm/cpu/armv8/fsl-layerscape/Kconfig:31
207
208The first number is the number of boards which can avoid having a special
209CONFIG_SCSI option in their defconfig file if this 'imply' is added.
210The location at the right is the Kconfig file and line number where the config
211appears. For example, adding 'imply CONFIG_SCSI' to the 'config ARCH_LS1021A'
212in arch/arm/cpu/armv7/ls102xa/Kconfig at line 1 will help 18 boards to reduce
213the size of their defconfig files.
214
215If you want to add an 'imply' to every imply config in the list, you can use
216
217 ./tools/moveconfig.py -s -i CONFIG_SCSI -a all
218
219To control which ones are displayed, use -I <list> where list is a list of
220options (use '-I help' to see possible options and their meaning).
221
222To skip showing you options that already have an 'imply' attached, use -A.
223
224When you have finished adding 'imply' options you can regenerate the
225defconfig files for affected boards with something like:
226
227 git show --stat | ./tools/moveconfig.py -s -d -
228
229This will regenerate only those defconfigs changed in the current commit.
230If you start with (say) 100 defconfigs being changed in the commit, and add
231a few 'imply' options as above, then regenerate, hopefully you can reduce the
232number of defconfigs changed in the commit.
233
Simon Glassc6e73cf2017-06-01 19:39:03 -0600234
Masahiro Yamadab6160812015-05-20 11:36:07 +0900235Available options
236-----------------
237
238 -c, --color
239 Surround each portion of the log with escape sequences to display it
240 in color on the terminal.
241
Simon Glass8bf41c22016-09-12 23:18:21 -0600242 -C, --commit
243 Create a git commit with the changes when the operation is complete. A
244 standard commit message is used which may need to be edited.
245
Joe Hershbergerc6e043a2015-05-19 13:21:19 -0500246 -d, --defconfigs
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900247 Specify a file containing a list of defconfigs to move. The defconfig
Simon Glass2d79f702017-06-01 19:39:00 -0600248 files can be given with shell-style wildcards. Use '-' to read from stdin.
Joe Hershbergerc6e043a2015-05-19 13:21:19 -0500249
Masahiro Yamadab6160812015-05-20 11:36:07 +0900250 -n, --dry-run
Masahiro Yamadab903c4e2016-05-19 15:51:58 +0900251 Perform a trial run that does not make any changes. It is useful to
Masahiro Yamadab6160812015-05-20 11:36:07 +0900252 see what is going to happen before one actually runs it.
253
254 -e, --exit-on-error
255 Exit immediately if Make exits with a non-zero status while processing
256 a defconfig file.
257
Masahiro Yamada83c17672016-05-19 15:52:08 +0900258 -s, --force-sync
259 Do "make savedefconfig" forcibly for all the defconfig files.
260 If not specified, "make savedefconfig" only occurs for cases
261 where at least one CONFIG was moved.
262
Masahiro Yamada6d139172016-08-22 22:18:22 +0900263 -S, --spl
264 Look for moved config options in spl/include/autoconf.mk instead of
265 include/autoconf.mk. This is useful for moving options for SPL build
266 because SPL related options (mostly prefixed with CONFIG_SPL_) are
267 sometimes blocked by CONFIG_SPL_BUILD ifdef conditionals.
268
Joe Hershberger23475932015-05-19 13:21:20 -0500269 -H, --headers-only
270 Only cleanup the headers; skip the defconfig processing
271
Masahiro Yamadab6160812015-05-20 11:36:07 +0900272 -j, --jobs
273 Specify the number of threads to run simultaneously. If not specified,
274 the number of threads is the same as the number of CPU cores.
275
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500276 -r, --git-ref
277 Specify the git ref to clone for building the autoconf.mk. If unspecified
278 use the CWD. This is useful for when changes to the Kconfig affect the
279 default values and you want to capture the state of the defconfig from
280 before that change was in effect. If in doubt, specify a ref pre-Kconfig
281 changes (use HEAD if Kconfig changes are not committed). Worst case it will
282 take a bit longer to run, but will always do the right thing.
283
Joe Hershberger808b63f2015-05-19 13:21:24 -0500284 -v, --verbose
285 Show any build errors as boards are built
286
Simon Glass13e05a02016-09-12 23:18:20 -0600287 -y, --yes
288 Instead of prompting, automatically go ahead with all operations. This
Simon Glass2d79f702017-06-01 19:39:00 -0600289 includes cleaning up headers, CONFIG_SYS_EXTRA_OPTIONS, the config whitelist
290 and the README.
Simon Glass13e05a02016-09-12 23:18:20 -0600291
Masahiro Yamadab6160812015-05-20 11:36:07 +0900292To see the complete list of supported options, run
293
294 $ tools/moveconfig.py -h
295
296"""
297
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +0100298import asteval
Simon Glassc6e73cf2017-06-01 19:39:03 -0600299import collections
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900300import copy
Masahiro Yamada573b3902016-07-25 19:15:25 +0900301import difflib
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900302import filecmp
Masahiro Yamadab6160812015-05-20 11:36:07 +0900303import fnmatch
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900304import glob
Masahiro Yamadab6160812015-05-20 11:36:07 +0900305import multiprocessing
306import optparse
307import os
Simon Glass1f701862019-10-31 07:42:57 -0600308import queue
Masahiro Yamadab6160812015-05-20 11:36:07 +0900309import re
310import shutil
311import subprocess
312import sys
313import tempfile
Simon Glass43cf08f2017-06-01 19:39:02 -0600314import threading
Masahiro Yamadab6160812015-05-20 11:36:07 +0900315import time
316
Simon Glass44116332017-06-15 21:39:33 -0600317sys.path.append(os.path.join(os.path.dirname(__file__), 'buildman'))
Simon Glass257f5232017-07-10 14:47:47 -0600318sys.path.append(os.path.join(os.path.dirname(__file__), 'patman'))
319import bsettings
Simon Glass44116332017-06-15 21:39:33 -0600320import kconfiglib
Simon Glass257f5232017-07-10 14:47:47 -0600321import toolchain
Simon Glass44116332017-06-15 21:39:33 -0600322
Masahiro Yamadab6160812015-05-20 11:36:07 +0900323SHOW_GNU_MAKE = 'scripts/show-gnu-make'
324SLEEP_TIME=0.03
325
Masahiro Yamadab6160812015-05-20 11:36:07 +0900326STATE_IDLE = 0
327STATE_DEFCONFIG = 1
328STATE_AUTOCONF = 2
Joe Hershberger166edec2015-05-19 13:21:17 -0500329STATE_SAVEDEFCONFIG = 3
Masahiro Yamadab6160812015-05-20 11:36:07 +0900330
331ACTION_MOVE = 0
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900332ACTION_NO_ENTRY = 1
Masahiro Yamada35204d92016-08-22 22:18:21 +0900333ACTION_NO_ENTRY_WARN = 2
334ACTION_NO_CHANGE = 3
Masahiro Yamadab6160812015-05-20 11:36:07 +0900335
336COLOR_BLACK = '0;30'
337COLOR_RED = '0;31'
338COLOR_GREEN = '0;32'
339COLOR_BROWN = '0;33'
340COLOR_BLUE = '0;34'
341COLOR_PURPLE = '0;35'
342COLOR_CYAN = '0;36'
343COLOR_LIGHT_GRAY = '0;37'
344COLOR_DARK_GRAY = '1;30'
345COLOR_LIGHT_RED = '1;31'
346COLOR_LIGHT_GREEN = '1;32'
347COLOR_YELLOW = '1;33'
348COLOR_LIGHT_BLUE = '1;34'
349COLOR_LIGHT_PURPLE = '1;35'
350COLOR_LIGHT_CYAN = '1;36'
351COLOR_WHITE = '1;37'
352
Simon Glass8fb5bd02017-06-01 19:39:01 -0600353AUTO_CONF_PATH = 'include/config/auto.conf'
Simon Glass43cf08f2017-06-01 19:39:02 -0600354CONFIG_DATABASE = 'moveconfig.db'
Simon Glass8fb5bd02017-06-01 19:39:01 -0600355
Simon Glass44116332017-06-15 21:39:33 -0600356CONFIG_LEN = len('CONFIG_')
Simon Glass8fb5bd02017-06-01 19:39:01 -0600357
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200358SIZES = {
359 "SZ_1": 0x00000001, "SZ_2": 0x00000002,
360 "SZ_4": 0x00000004, "SZ_8": 0x00000008,
361 "SZ_16": 0x00000010, "SZ_32": 0x00000020,
362 "SZ_64": 0x00000040, "SZ_128": 0x00000080,
363 "SZ_256": 0x00000100, "SZ_512": 0x00000200,
364 "SZ_1K": 0x00000400, "SZ_2K": 0x00000800,
365 "SZ_4K": 0x00001000, "SZ_8K": 0x00002000,
366 "SZ_16K": 0x00004000, "SZ_32K": 0x00008000,
367 "SZ_64K": 0x00010000, "SZ_128K": 0x00020000,
368 "SZ_256K": 0x00040000, "SZ_512K": 0x00080000,
369 "SZ_1M": 0x00100000, "SZ_2M": 0x00200000,
370 "SZ_4M": 0x00400000, "SZ_8M": 0x00800000,
371 "SZ_16M": 0x01000000, "SZ_32M": 0x02000000,
372 "SZ_64M": 0x04000000, "SZ_128M": 0x08000000,
373 "SZ_256M": 0x10000000, "SZ_512M": 0x20000000,
374 "SZ_1G": 0x40000000, "SZ_2G": 0x80000000,
375 "SZ_4G": 0x100000000
376}
377
Masahiro Yamadab6160812015-05-20 11:36:07 +0900378### helper functions ###
379def get_devnull():
380 """Get the file object of '/dev/null' device."""
381 try:
382 devnull = subprocess.DEVNULL # py3k
383 except AttributeError:
384 devnull = open(os.devnull, 'wb')
385 return devnull
386
387def check_top_directory():
388 """Exit if we are not at the top of source directory."""
389 for f in ('README', 'Licenses'):
390 if not os.path.exists(f):
391 sys.exit('Please run at the top of source directory.')
392
Masahiro Yamada990e6772016-05-19 15:51:54 +0900393def check_clean_directory():
394 """Exit if the source tree is not clean."""
395 for f in ('.config', 'include/config'):
396 if os.path.exists(f):
397 sys.exit("source tree is not clean, please run 'make mrproper'")
398
Masahiro Yamadab6160812015-05-20 11:36:07 +0900399def get_make_cmd():
400 """Get the command name of GNU Make.
401
402 U-Boot needs GNU Make for building, but the command name is not
403 necessarily "make". (for example, "gmake" on FreeBSD).
404 Returns the most appropriate command name on your system.
405 """
406 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
407 ret = process.communicate()
408 if process.returncode:
409 sys.exit('GNU Make not found')
410 return ret[0].rstrip()
411
Simon Glass18774bc2017-06-01 19:38:58 -0600412def get_matched_defconfig(line):
413 """Get the defconfig files that match a pattern
414
415 Args:
416 line: Path or filename to match, e.g. 'configs/snow_defconfig' or
417 'k2*_defconfig'. If no directory is provided, 'configs/' is
418 prepended
419
420 Returns:
421 a list of matching defconfig files
422 """
423 dirname = os.path.dirname(line)
424 if dirname:
425 pattern = line
426 else:
427 pattern = os.path.join('configs', line)
428 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
429
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900430def get_matched_defconfigs(defconfigs_file):
Simon Glass8f3cf312017-06-01 19:38:59 -0600431 """Get all the defconfig files that match the patterns in a file.
432
433 Args:
434 defconfigs_file: File containing a list of defconfigs to process, or
435 '-' to read the list from stdin
436
437 Returns:
438 A list of paths to defconfig files, with no duplicates
439 """
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900440 defconfigs = []
Simon Glass8f3cf312017-06-01 19:38:59 -0600441 if defconfigs_file == '-':
442 fd = sys.stdin
443 defconfigs_file = 'stdin'
444 else:
445 fd = open(defconfigs_file)
446 for i, line in enumerate(fd):
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900447 line = line.strip()
448 if not line:
449 continue # skip blank lines silently
Simon Glass452fa8e2017-06-15 21:39:31 -0600450 if ' ' in line:
451 line = line.split(' ')[0] # handle 'git log' input
Simon Glass18774bc2017-06-01 19:38:58 -0600452 matched = get_matched_defconfig(line)
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900453 if not matched:
Simon Glass1f701862019-10-31 07:42:57 -0600454 print("warning: %s:%d: no defconfig matched '%s'" % \
455 (defconfigs_file, i + 1, line), file=sys.stderr)
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900456
457 defconfigs += matched
458
459 # use set() to drop multiple matching
460 return [ defconfig[len('configs') + 1:] for defconfig in set(defconfigs) ]
461
Masahiro Yamada58175e32016-07-25 19:15:28 +0900462def get_all_defconfigs():
463 """Get all the defconfig files under the configs/ directory."""
464 defconfigs = []
465 for (dirpath, dirnames, filenames) in os.walk('configs'):
466 dirpath = dirpath[len('configs') + 1:]
467 for filename in fnmatch.filter(filenames, '*_defconfig'):
468 defconfigs.append(os.path.join(dirpath, filename))
469
470 return defconfigs
471
Masahiro Yamadab6160812015-05-20 11:36:07 +0900472def color_text(color_enabled, color, string):
473 """Return colored string."""
474 if color_enabled:
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900475 # LF should not be surrounded by the escape sequence.
476 # Otherwise, additional whitespace or line-feed might be printed.
477 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
478 for s in string.split('\n') ])
Masahiro Yamadab6160812015-05-20 11:36:07 +0900479 else:
480 return string
481
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900482def show_diff(a, b, file_path, color_enabled):
Masahiro Yamada573b3902016-07-25 19:15:25 +0900483 """Show unidified diff.
484
485 Arguments:
486 a: A list of lines (before)
487 b: A list of lines (after)
488 file_path: Path to the file
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900489 color_enabled: Display the diff in color
Masahiro Yamada573b3902016-07-25 19:15:25 +0900490 """
491
492 diff = difflib.unified_diff(a, b,
493 fromfile=os.path.join('a', file_path),
494 tofile=os.path.join('b', file_path))
495
496 for line in diff:
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900497 if line[0] == '-' and line[1] != '-':
Simon Glass1f701862019-10-31 07:42:57 -0600498 print(color_text(color_enabled, COLOR_RED, line), end=' ')
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900499 elif line[0] == '+' and line[1] != '+':
Simon Glass1f701862019-10-31 07:42:57 -0600500 print(color_text(color_enabled, COLOR_GREEN, line), end=' ')
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900501 else:
Simon Glass1f701862019-10-31 07:42:57 -0600502 print(line, end=' ')
Masahiro Yamada573b3902016-07-25 19:15:25 +0900503
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900504def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
505 extend_post):
506 """Extend matched lines if desired patterns are found before/after already
507 matched lines.
508
509 Arguments:
510 lines: A list of lines handled.
511 matched: A list of line numbers that have been already matched.
512 (will be updated by this function)
513 pre_patterns: A list of regular expression that should be matched as
514 preamble.
515 post_patterns: A list of regular expression that should be matched as
516 postamble.
517 extend_pre: Add the line number of matched preamble to the matched list.
518 extend_post: Add the line number of matched postamble to the matched list.
519 """
520 extended_matched = []
521
522 j = matched[0]
523
524 for i in matched:
525 if i == 0 or i < j:
526 continue
527 j = i
528 while j in matched:
529 j += 1
530 if j >= len(lines):
531 break
532
533 for p in pre_patterns:
534 if p.search(lines[i - 1]):
535 break
536 else:
537 # not matched
538 continue
539
540 for p in post_patterns:
541 if p.search(lines[j]):
542 break
543 else:
544 # not matched
545 continue
546
547 if extend_pre:
548 extended_matched.append(i - 1)
549 if extend_post:
550 extended_matched.append(j)
551
552 matched += extended_matched
553 matched.sort()
554
Chris Packham85e15c52017-05-02 21:30:46 +1200555def confirm(options, prompt):
556 if not options.yes:
557 while True:
Simon Glass1f701862019-10-31 07:42:57 -0600558 choice = input('{} [y/n]: '.format(prompt))
Chris Packham85e15c52017-05-02 21:30:46 +1200559 choice = choice.lower()
Simon Glass1f701862019-10-31 07:42:57 -0600560 print(choice)
Chris Packham85e15c52017-05-02 21:30:46 +1200561 if choice == 'y' or choice == 'n':
562 break
563
564 if choice == 'n':
565 return False
566
567 return True
568
Chris Packham73f6c7c2019-01-30 20:23:16 +1300569def cleanup_empty_blocks(header_path, options):
570 """Clean up empty conditional blocks
571
572 Arguments:
573 header_path: path to the cleaned file.
574 options: option flags.
575 """
576 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
577 with open(header_path) as f:
578 data = f.read()
579
580 new_data = pattern.sub('\n', data)
581
582 show_diff(data.splitlines(True), new_data.splitlines(True), header_path,
583 options.color)
584
585 if options.dry_run:
586 return
587
588 with open(header_path, 'w') as f:
589 f.write(new_data)
590
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900591def cleanup_one_header(header_path, patterns, options):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900592 """Clean regex-matched lines away from a file.
593
594 Arguments:
595 header_path: path to the cleaned file.
596 patterns: list of regex patterns. Any lines matching to these
597 patterns are deleted.
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900598 options: option flags.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900599 """
600 with open(header_path) as f:
601 lines = f.readlines()
602
603 matched = []
604 for i, line in enumerate(lines):
Masahiro Yamada6d798ba2016-07-25 19:15:27 +0900605 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
606 matched.append(i)
607 continue
Masahiro Yamadab6160812015-05-20 11:36:07 +0900608 for pattern in patterns:
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900609 if pattern.search(line):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900610 matched.append(i)
611 break
612
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900613 if not matched:
614 return
615
616 # remove empty #ifdef ... #endif, successive blank lines
617 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
618 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
619 pattern_endif = re.compile(r'#\s*endif\W') # #endif
620 pattern_blank = re.compile(r'^\s*$') # empty line
621
622 while True:
623 old_matched = copy.copy(matched)
624 extend_matched_lines(lines, matched, [pattern_if],
625 [pattern_endif], True, True)
626 extend_matched_lines(lines, matched, [pattern_elif],
627 [pattern_elif, pattern_endif], True, False)
628 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
629 [pattern_blank], False, True)
630 extend_matched_lines(lines, matched, [pattern_blank],
631 [pattern_elif, pattern_endif], True, False)
632 extend_matched_lines(lines, matched, [pattern_blank],
633 [pattern_blank], True, False)
634 if matched == old_matched:
635 break
636
Masahiro Yamada573b3902016-07-25 19:15:25 +0900637 tolines = copy.copy(lines)
638
639 for i in reversed(matched):
640 tolines.pop(i)
641
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900642 show_diff(lines, tolines, header_path, options.color)
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900643
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900644 if options.dry_run:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900645 return
646
647 with open(header_path, 'w') as f:
Masahiro Yamada573b3902016-07-25 19:15:25 +0900648 for line in tolines:
649 f.write(line)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900650
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900651def cleanup_headers(configs, options):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900652 """Delete config defines from board headers.
653
654 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900655 configs: A list of CONFIGs to remove.
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900656 options: option flags.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900657 """
Chris Packham85e15c52017-05-02 21:30:46 +1200658 if not confirm(options, 'Clean up headers?'):
659 return
Masahiro Yamadab6160812015-05-20 11:36:07 +0900660
661 patterns = []
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900662 for config in configs:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900663 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
664 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
665
Joe Hershbergerb78ad422015-05-19 13:21:21 -0500666 for dir in 'include', 'arch', 'board':
667 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamada28a6d352016-07-25 19:15:22 +0900668 if dirpath == os.path.join('include', 'generated'):
669 continue
Joe Hershbergerb78ad422015-05-19 13:21:21 -0500670 for filename in filenames:
Tom Rinie90c0072019-11-10 21:19:37 -0500671 if not filename.endswith(('~', '.dts', '.dtsi')):
Chris Packham73f6c7c2019-01-30 20:23:16 +1300672 header_path = os.path.join(dirpath, filename)
Tom Rinie90c0072019-11-10 21:19:37 -0500673 # This file contains UTF-16 data and no CONFIG symbols
674 if header_path == 'include/video_font_data.h':
675 continue
Chris Packham73f6c7c2019-01-30 20:23:16 +1300676 cleanup_one_header(header_path, patterns, options)
677 cleanup_empty_blocks(header_path, options)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900678
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900679def cleanup_one_extra_option(defconfig_path, configs, options):
680 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
681
682 Arguments:
683 defconfig_path: path to the cleaned defconfig file.
684 configs: A list of CONFIGs to remove.
685 options: option flags.
686 """
687
688 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
689 end = '"\n'
690
691 with open(defconfig_path) as f:
692 lines = f.readlines()
693
694 for i, line in enumerate(lines):
695 if line.startswith(start) and line.endswith(end):
696 break
697 else:
698 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
699 return
700
701 old_tokens = line[len(start):-len(end)].split(',')
702 new_tokens = []
703
704 for token in old_tokens:
705 pos = token.find('=')
706 if not (token[:pos] if pos >= 0 else token) in configs:
707 new_tokens.append(token)
708
709 if new_tokens == old_tokens:
710 return
711
712 tolines = copy.copy(lines)
713
714 if new_tokens:
715 tolines[i] = start + ','.join(new_tokens) + end
716 else:
717 tolines.pop(i)
718
719 show_diff(lines, tolines, defconfig_path, options.color)
720
721 if options.dry_run:
722 return
723
724 with open(defconfig_path, 'w') as f:
725 for line in tolines:
726 f.write(line)
727
728def cleanup_extra_options(configs, options):
729 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
730
731 Arguments:
732 configs: A list of CONFIGs to remove.
733 options: option flags.
734 """
Chris Packham85e15c52017-05-02 21:30:46 +1200735 if not confirm(options, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
736 return
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900737
738 configs = [ config[len('CONFIG_'):] for config in configs ]
739
740 defconfigs = get_all_defconfigs()
741
742 for defconfig in defconfigs:
743 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
744 options)
745
Chris Packham9d5274f2017-05-02 21:30:47 +1200746def cleanup_whitelist(configs, options):
747 """Delete config whitelist entries
748
749 Arguments:
750 configs: A list of CONFIGs to remove.
751 options: option flags.
752 """
753 if not confirm(options, 'Clean up whitelist entries?'):
754 return
755
756 with open(os.path.join('scripts', 'config_whitelist.txt')) as f:
757 lines = f.readlines()
758
759 lines = [x for x in lines if x.strip() not in configs]
760
761 with open(os.path.join('scripts', 'config_whitelist.txt'), 'w') as f:
762 f.write(''.join(lines))
763
Chris Packham0e6deff2017-05-02 21:30:48 +1200764def find_matching(patterns, line):
765 for pat in patterns:
766 if pat.search(line):
767 return True
768 return False
769
770def cleanup_readme(configs, options):
771 """Delete config description in README
772
773 Arguments:
774 configs: A list of CONFIGs to remove.
775 options: option flags.
776 """
777 if not confirm(options, 'Clean up README?'):
778 return
779
780 patterns = []
781 for config in configs:
782 patterns.append(re.compile(r'^\s+%s' % config))
783
784 with open('README') as f:
785 lines = f.readlines()
786
787 found = False
788 newlines = []
789 for line in lines:
790 if not found:
791 found = find_matching(patterns, line)
792 if found:
793 continue
794
795 if found and re.search(r'^\s+CONFIG', line):
796 found = False
797
798 if not found:
799 newlines.append(line)
800
801 with open('README', 'w') as f:
802 f.write(''.join(newlines))
803
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200804def try_expand(line):
805 """If value looks like an expression, try expanding it
806 Otherwise just return the existing value
807 """
808 if line.find('=') == -1:
809 return line
810
811 try:
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +0100812 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200813 cfg, val = re.split("=", line)
814 val= val.strip('\"')
815 if re.search("[*+-/]|<<|SZ_+|\(([^\)]+)\)", val):
Markus Klotzbuecherbac0bb52020-02-12 20:46:44 +0100816 newval = hex(aeval(val))
Simon Glass1f701862019-10-31 07:42:57 -0600817 print("\tExpanded expression %s to %s" % (val, newval))
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200818 return cfg+'='+newval
819 except:
Simon Glass1f701862019-10-31 07:42:57 -0600820 print("\tFailed to expand expression in %s" % line)
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200821
822 return line
823
Chris Packham9d5274f2017-05-02 21:30:47 +1200824
Masahiro Yamadab6160812015-05-20 11:36:07 +0900825### classes ###
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900826class Progress:
827
828 """Progress Indicator"""
829
830 def __init__(self, total):
831 """Create a new progress indicator.
832
833 Arguments:
834 total: A number of defconfig files to process.
835 """
836 self.current = 0
837 self.total = total
838
839 def inc(self):
840 """Increment the number of processed defconfig files."""
841
842 self.current += 1
843
844 def show(self):
845 """Display the progress."""
Simon Glass1f701862019-10-31 07:42:57 -0600846 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900847 sys.stdout.flush()
848
Simon Glass44116332017-06-15 21:39:33 -0600849
850class KconfigScanner:
851 """Kconfig scanner."""
852
853 def __init__(self):
854 """Scan all the Kconfig files and create a Config object."""
855 # Define environment variables referenced from Kconfig
856 os.environ['srctree'] = os.getcwd()
857 os.environ['UBOOTVERSION'] = 'dummy'
858 os.environ['KCONFIG_OBJDIR'] = ''
Tom Rini3c5f4152019-09-20 17:42:09 -0400859 self.conf = kconfiglib.Kconfig()
Simon Glass44116332017-06-15 21:39:33 -0600860
861
Masahiro Yamadab6160812015-05-20 11:36:07 +0900862class KconfigParser:
863
864 """A parser of .config and include/autoconf.mk."""
865
866 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
867 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
868
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900869 def __init__(self, configs, options, build_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900870 """Create a new parser.
871
872 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900873 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900874 options: option flags.
875 build_dir: Build directory.
876 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900877 self.configs = configs
Masahiro Yamadab6160812015-05-20 11:36:07 +0900878 self.options = options
Masahiro Yamada5393b612016-05-19 15:52:00 +0900879 self.dotconfig = os.path.join(build_dir, '.config')
880 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada6d139172016-08-22 22:18:22 +0900881 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
882 'autoconf.mk')
Simon Glass8fb5bd02017-06-01 19:39:01 -0600883 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada07f98522016-05-19 15:52:06 +0900884 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900885
Simon Glass257f5232017-07-10 14:47:47 -0600886 def get_arch(self):
887 """Parse .config file and return the architecture.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900888
889 Returns:
Simon Glass257f5232017-07-10 14:47:47 -0600890 Architecture name (e.g. 'arm').
Masahiro Yamadab6160812015-05-20 11:36:07 +0900891 """
892 arch = ''
893 cpu = ''
Masahiro Yamada5393b612016-05-19 15:52:00 +0900894 for line in open(self.dotconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900895 m = self.re_arch.match(line)
896 if m:
897 arch = m.group(1)
898 continue
899 m = self.re_cpu.match(line)
900 if m:
901 cpu = m.group(1)
902
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900903 if not arch:
904 return None
Masahiro Yamadab6160812015-05-20 11:36:07 +0900905
906 # fix-up for aarch64
907 if arch == 'arm' and cpu == 'armv8':
908 arch = 'aarch64'
909
Simon Glass257f5232017-07-10 14:47:47 -0600910 return arch
Masahiro Yamadab6160812015-05-20 11:36:07 +0900911
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900912 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900913 """Parse .config, defconfig, include/autoconf.mk for one config.
914
915 This function looks for the config options in the lines from
916 defconfig, .config, and include/autoconf.mk in order to decide
917 which action should be taken for this defconfig.
918
919 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900920 config: CONFIG name to parse.
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900921 dotconfig_lines: lines from the .config file.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900922 autoconf_lines: lines from the include/autoconf.mk file.
923
924 Returns:
925 A tupple of the action for this defconfig and the line
926 matched for the config.
927 """
Masahiro Yamadab6160812015-05-20 11:36:07 +0900928 not_set = '# %s is not set' % config
929
Masahiro Yamadab6160812015-05-20 11:36:07 +0900930 for line in autoconf_lines:
931 line = line.rstrip()
932 if line.startswith(config + '='):
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900933 new_val = line
Masahiro Yamadab6160812015-05-20 11:36:07 +0900934 break
Masahiro Yamadab6160812015-05-20 11:36:07 +0900935 else:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900936 new_val = not_set
937
Markus Klotzbuecher3d5d4182019-05-15 15:15:52 +0200938 new_val = try_expand(new_val)
939
Masahiro Yamada35204d92016-08-22 22:18:21 +0900940 for line in dotconfig_lines:
941 line = line.rstrip()
942 if line.startswith(config + '=') or line == not_set:
943 old_val = line
944 break
945 else:
946 if new_val == not_set:
947 return (ACTION_NO_ENTRY, config)
948 else:
949 return (ACTION_NO_ENTRY_WARN, config)
950
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900951 # If this CONFIG is neither bool nor trisate
952 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
953 # tools/scripts/define2mk.sed changes '1' to 'y'.
954 # This is a problem if the CONFIG is int type.
955 # Check the type in Kconfig and handle it correctly.
956 if new_val[-2:] == '=y':
957 new_val = new_val[:-1] + '1'
958
Masahiro Yamadab48387f2016-06-15 14:33:50 +0900959 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
960 new_val)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900961
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900962 def update_dotconfig(self):
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900963 """Parse files for the config options and update the .config.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900964
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900965 This function parses the generated .config and include/autoconf.mk
966 searching the target options.
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900967 Move the config option(s) to the .config as needed.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900968
969 Arguments:
970 defconfig: defconfig name.
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900971
972 Returns:
Masahiro Yamada263d1372016-05-19 15:52:04 +0900973 Return a tuple of (updated flag, log string).
974 The "updated flag" is True if the .config was updated, False
975 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900976 """
977
Masahiro Yamadab6160812015-05-20 11:36:07 +0900978 results = []
Masahiro Yamada263d1372016-05-19 15:52:04 +0900979 updated = False
Masahiro Yamada35204d92016-08-22 22:18:21 +0900980 suspicious = False
Masahiro Yamada6d139172016-08-22 22:18:22 +0900981 rm_files = [self.config_autoconf, self.autoconf]
982
983 if self.options.spl:
984 if os.path.exists(self.spl_autoconf):
985 autoconf_path = self.spl_autoconf
986 rm_files.append(self.spl_autoconf)
987 else:
988 for f in rm_files:
989 os.remove(f)
990 return (updated, suspicious,
991 color_text(self.options.color, COLOR_BROWN,
992 "SPL is not enabled. Skipped.") + '\n')
993 else:
994 autoconf_path = self.autoconf
Masahiro Yamadab6160812015-05-20 11:36:07 +0900995
Masahiro Yamada5393b612016-05-19 15:52:00 +0900996 with open(self.dotconfig) as f:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900997 dotconfig_lines = f.readlines()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900998
Masahiro Yamada6d139172016-08-22 22:18:22 +0900999 with open(autoconf_path) as f:
Masahiro Yamadab6160812015-05-20 11:36:07 +09001000 autoconf_lines = f.readlines()
1001
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001002 for config in self.configs:
1003 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger166edec2015-05-19 13:21:17 -05001004 autoconf_lines)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001005 results.append(result)
1006
1007 log = ''
1008
1009 for (action, value) in results:
1010 if action == ACTION_MOVE:
1011 actlog = "Move '%s'" % value
1012 log_color = COLOR_LIGHT_GREEN
Masahiro Yamada5643d6e2016-05-19 15:51:56 +09001013 elif action == ACTION_NO_ENTRY:
1014 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamadab6160812015-05-20 11:36:07 +09001015 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada35204d92016-08-22 22:18:21 +09001016 elif action == ACTION_NO_ENTRY_WARN:
1017 actlog = "%s is not defined in Kconfig (suspicious). Do nothing." % value
1018 log_color = COLOR_YELLOW
1019 suspicious = True
Masahiro Yamada5643d6e2016-05-19 15:51:56 +09001020 elif action == ACTION_NO_CHANGE:
1021 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
1022 % value
Masahiro Yamadab6160812015-05-20 11:36:07 +09001023 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada6d139172016-08-22 22:18:22 +09001024 elif action == ACTION_SPL_NOT_EXIST:
1025 actlog = "SPL is not enabled for this defconfig. Skip."
1026 log_color = COLOR_PURPLE
Masahiro Yamadab6160812015-05-20 11:36:07 +09001027 else:
1028 sys.exit("Internal Error. This should not happen.")
1029
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001030 log += color_text(self.options.color, log_color, actlog) + '\n'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001031
Masahiro Yamada5393b612016-05-19 15:52:00 +09001032 with open(self.dotconfig, 'a') as f:
Masahiro Yamada953d93b2016-05-19 15:51:49 +09001033 for (action, value) in results:
1034 if action == ACTION_MOVE:
1035 f.write(value + '\n')
Masahiro Yamada263d1372016-05-19 15:52:04 +09001036 updated = True
Masahiro Yamadab6160812015-05-20 11:36:07 +09001037
Masahiro Yamada07f98522016-05-19 15:52:06 +09001038 self.results = results
Masahiro Yamada6d139172016-08-22 22:18:22 +09001039 for f in rm_files:
1040 os.remove(f)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001041
Masahiro Yamada35204d92016-08-22 22:18:21 +09001042 return (updated, suspicious, log)
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +09001043
Masahiro Yamada07f98522016-05-19 15:52:06 +09001044 def check_defconfig(self):
1045 """Check the defconfig after savedefconfig
1046
1047 Returns:
1048 Return additional log if moved CONFIGs were removed again by
1049 'make savedefconfig'.
1050 """
1051
1052 log = ''
1053
1054 with open(self.defconfig) as f:
1055 defconfig_lines = f.readlines()
1056
1057 for (action, value) in self.results:
1058 if action != ACTION_MOVE:
1059 continue
1060 if not value + '\n' in defconfig_lines:
1061 log += color_text(self.options.color, COLOR_YELLOW,
1062 "'%s' was removed by savedefconfig.\n" %
1063 value)
1064
1065 return log
1066
Simon Glass43cf08f2017-06-01 19:39:02 -06001067
1068class DatabaseThread(threading.Thread):
1069 """This thread processes results from Slot threads.
1070
1071 It collects the data in the master config directary. There is only one
1072 result thread, and this helps to serialise the build output.
1073 """
1074 def __init__(self, config_db, db_queue):
1075 """Set up a new result thread
1076
1077 Args:
1078 builder: Builder which will be sent each result
1079 """
1080 threading.Thread.__init__(self)
1081 self.config_db = config_db
1082 self.db_queue= db_queue
1083
1084 def run(self):
1085 """Called to start up the result thread.
1086
1087 We collect the next result job and pass it on to the build.
1088 """
1089 while True:
1090 defconfig, configs = self.db_queue.get()
1091 self.config_db[defconfig] = configs
1092 self.db_queue.task_done()
1093
1094
Masahiro Yamadab6160812015-05-20 11:36:07 +09001095class Slot:
1096
1097 """A slot to store a subprocess.
1098
1099 Each instance of this class handles one subprocess.
1100 This class is useful to control multiple threads
1101 for faster processing.
1102 """
1103
Simon Glass257f5232017-07-10 14:47:47 -06001104 def __init__(self, toolchains, configs, options, progress, devnull,
1105 make_cmd, reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001106 """Create a new process slot.
1107
1108 Arguments:
Simon Glass257f5232017-07-10 14:47:47 -06001109 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001110 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001111 options: option flags.
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001112 progress: A progress indicator.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001113 devnull: A file object of '/dev/null'.
1114 make_cmd: command name of GNU Make.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001115 reference_src_dir: Determine the true starting config state from this
1116 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -06001117 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +09001118 """
Simon Glass257f5232017-07-10 14:47:47 -06001119 self.toolchains = toolchains
Masahiro Yamadab6160812015-05-20 11:36:07 +09001120 self.options = options
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001121 self.progress = progress
Masahiro Yamadab6160812015-05-20 11:36:07 +09001122 self.build_dir = tempfile.mkdtemp()
1123 self.devnull = devnull
1124 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001125 self.reference_src_dir = reference_src_dir
Simon Glass43cf08f2017-06-01 19:39:02 -06001126 self.db_queue = db_queue
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +09001127 self.parser = KconfigParser(configs, options, self.build_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001128 self.state = STATE_IDLE
Masahiro Yamada1271b672016-08-22 22:18:20 +09001129 self.failed_boards = set()
1130 self.suspicious_boards = set()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001131
1132 def __del__(self):
1133 """Delete the working directory
1134
1135 This function makes sure the temporary directory is cleaned away
1136 even if Python suddenly dies due to error. It should be done in here
Joe Hershberger640de872016-06-10 14:53:29 -05001137 because it is guaranteed the destructor is always invoked when the
Masahiro Yamadab6160812015-05-20 11:36:07 +09001138 instance of the class gets unreferenced.
1139
1140 If the subprocess is still running, wait until it finishes.
1141 """
1142 if self.state != STATE_IDLE:
1143 while self.ps.poll() == None:
1144 pass
1145 shutil.rmtree(self.build_dir)
1146
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001147 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001148 """Assign a new subprocess for defconfig and add it to the slot.
1149
1150 If the slot is vacant, create a new subprocess for processing the
1151 given defconfig and add it to the slot. Just returns False if
1152 the slot is occupied (i.e. the current subprocess is still running).
1153
1154 Arguments:
1155 defconfig: defconfig name.
1156
1157 Returns:
1158 Return True on success or False on failure
1159 """
1160 if self.state != STATE_IDLE:
1161 return False
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001162
Masahiro Yamadab6160812015-05-20 11:36:07 +09001163 self.defconfig = defconfig
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001164 self.log = ''
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001165 self.current_src_dir = self.reference_src_dir
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001166 self.do_defconfig()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001167 return True
1168
1169 def poll(self):
1170 """Check the status of the subprocess and handle it as needed.
1171
1172 Returns True if the slot is vacant (i.e. in idle state).
1173 If the configuration is successfully finished, assign a new
1174 subprocess to build include/autoconf.mk.
1175 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada263d1372016-05-19 15:52:04 +09001176 parse the .config and the include/autoconf.mk, moving
1177 config options to the .config as needed.
1178 If the .config was updated, run "make savedefconfig" to sync
1179 it, update the original defconfig, and then set the slot back
1180 to the idle state.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001181
1182 Returns:
1183 Return True if the subprocess is terminated, False otherwise
1184 """
1185 if self.state == STATE_IDLE:
1186 return True
1187
1188 if self.ps.poll() == None:
1189 return False
1190
1191 if self.ps.poll() != 0:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001192 self.handle_error()
1193 elif self.state == STATE_DEFCONFIG:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001194 if self.reference_src_dir and not self.current_src_dir:
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001195 self.do_savedefconfig()
1196 else:
1197 self.do_autoconf()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001198 elif self.state == STATE_AUTOCONF:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001199 if self.current_src_dir:
1200 self.current_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001201 self.do_defconfig()
Simon Glass43cf08f2017-06-01 19:39:02 -06001202 elif self.options.build_db:
1203 self.do_build_db()
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001204 else:
1205 self.do_savedefconfig()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001206 elif self.state == STATE_SAVEDEFCONFIG:
1207 self.update_defconfig()
1208 else:
1209 sys.exit("Internal Error. This should not happen.")
Masahiro Yamadab6160812015-05-20 11:36:07 +09001210
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001211 return True if self.state == STATE_IDLE else False
Joe Hershberger166edec2015-05-19 13:21:17 -05001212
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001213 def handle_error(self):
1214 """Handle error cases."""
Masahiro Yamada83c17672016-05-19 15:52:08 +09001215
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001216 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
1217 "Failed to process.\n")
1218 if self.options.verbose:
1219 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher8773f352020-02-12 20:46:45 +01001220 self.ps.stderr.read().decode())
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001221 self.finish(False)
Joe Hershberger166edec2015-05-19 13:21:17 -05001222
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001223 def do_defconfig(self):
1224 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamada0f6beda2016-05-19 15:52:07 +09001225
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001226 cmd = list(self.make_cmd)
1227 cmd.append(self.defconfig)
1228 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001229 stderr=subprocess.PIPE,
1230 cwd=self.current_src_dir)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001231 self.state = STATE_DEFCONFIG
Masahiro Yamada0f6beda2016-05-19 15:52:07 +09001232
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001233 def do_autoconf(self):
Simon Glass8fb5bd02017-06-01 19:39:01 -06001234 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamadab6160812015-05-20 11:36:07 +09001235
Simon Glass257f5232017-07-10 14:47:47 -06001236 arch = self.parser.get_arch()
1237 try:
1238 toolchain = self.toolchains.Select(arch)
1239 except ValueError:
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001240 self.log += color_text(self.options.color, COLOR_YELLOW,
Chris Packham1ebcbd12017-08-27 20:00:51 +12001241 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001242 self.finish(False)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001243 return
Simon Glass1f701862019-10-31 07:42:57 -06001244 env = toolchain.MakeEnvironment(False)
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +09001245
Masahiro Yamadab6160812015-05-20 11:36:07 +09001246 cmd = list(self.make_cmd)
Joe Hershberger765442b2015-05-19 13:21:18 -05001247 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glass8fb5bd02017-06-01 19:39:01 -06001248 cmd.append(AUTO_CONF_PATH)
Simon Glass257f5232017-07-10 14:47:47 -06001249 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001250 stderr=subprocess.PIPE,
1251 cwd=self.current_src_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001252 self.state = STATE_AUTOCONF
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001253
Simon Glass43cf08f2017-06-01 19:39:02 -06001254 def do_build_db(self):
1255 """Add the board to the database"""
1256 configs = {}
1257 with open(os.path.join(self.build_dir, AUTO_CONF_PATH)) as fd:
1258 for line in fd.readlines():
1259 if line.startswith('CONFIG'):
1260 config, value = line.split('=', 1)
1261 configs[config] = value.rstrip()
1262 self.db_queue.put([self.defconfig, configs])
1263 self.finish(True)
1264
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001265 def do_savedefconfig(self):
1266 """Update the .config and run 'make savedefconfig'."""
1267
Masahiro Yamada35204d92016-08-22 22:18:21 +09001268 (updated, suspicious, log) = self.parser.update_dotconfig()
1269 if suspicious:
1270 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001271 self.log += log
1272
1273 if not self.options.force_sync and not updated:
1274 self.finish(True)
1275 return
1276 if updated:
1277 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
1278 "Syncing by savedefconfig...\n")
1279 else:
1280 self.log += "Syncing by savedefconfig (forced by option)...\n"
1281
1282 cmd = list(self.make_cmd)
1283 cmd.append('savedefconfig')
1284 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1285 stderr=subprocess.PIPE)
1286 self.state = STATE_SAVEDEFCONFIG
1287
1288 def update_defconfig(self):
1289 """Update the input defconfig and go back to the idle state."""
1290
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001291 log = self.parser.check_defconfig()
1292 if log:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001293 self.suspicious_boards.add(self.defconfig)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001294 self.log += log
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001295 orig_defconfig = os.path.join('configs', self.defconfig)
1296 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1297 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1298
1299 if updated:
Joe Hershberger93f1c2d2016-06-10 14:53:30 -05001300 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001301 "defconfig was updated.\n")
1302
1303 if not self.options.dry_run and updated:
1304 shutil.move(new_defconfig, orig_defconfig)
1305 self.finish(True)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001306
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001307 def finish(self, success):
1308 """Display log along with progress and go to the idle state.
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001309
1310 Arguments:
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001311 success: Should be True when the defconfig was processed
1312 successfully, or False when it fails.
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001313 """
1314 # output at least 30 characters to hide the "* defconfigs out of *".
1315 log = self.defconfig.ljust(30) + '\n'
1316
1317 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1318 # Some threads are running in parallel.
1319 # Print log atomically to not mix up logs from different threads.
Simon Glass1f701862019-10-31 07:42:57 -06001320 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001321
1322 if not success:
1323 if self.options.exit_on_error:
1324 sys.exit("Exit on error.")
1325 # If --exit-on-error flag is not set, skip this board and continue.
1326 # Record the failed board.
Masahiro Yamada1271b672016-08-22 22:18:20 +09001327 self.failed_boards.add(self.defconfig)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001328
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001329 self.progress.inc()
1330 self.progress.show()
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001331 self.state = STATE_IDLE
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001332
Masahiro Yamadab6160812015-05-20 11:36:07 +09001333 def get_failed_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001334 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001335 """
1336 return self.failed_boards
1337
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001338 def get_suspicious_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001339 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001340 """
Masahiro Yamada35204d92016-08-22 22:18:21 +09001341 return self.suspicious_boards - self.failed_boards
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001342
Masahiro Yamadab6160812015-05-20 11:36:07 +09001343class Slots:
1344
1345 """Controller of the array of subprocess slots."""
1346
Simon Glass257f5232017-07-10 14:47:47 -06001347 def __init__(self, toolchains, configs, options, progress,
1348 reference_src_dir, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001349 """Create a new slots controller.
1350
1351 Arguments:
Simon Glass257f5232017-07-10 14:47:47 -06001352 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001353 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001354 options: option flags.
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001355 progress: A progress indicator.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001356 reference_src_dir: Determine the true starting config state from this
1357 source tree.
Simon Glass43cf08f2017-06-01 19:39:02 -06001358 db_queue: output queue to write config info for the database
Masahiro Yamadab6160812015-05-20 11:36:07 +09001359 """
1360 self.options = options
1361 self.slots = []
1362 devnull = get_devnull()
1363 make_cmd = get_make_cmd()
1364 for i in range(options.jobs):
Simon Glass257f5232017-07-10 14:47:47 -06001365 self.slots.append(Slot(toolchains, configs, options, progress,
1366 devnull, make_cmd, reference_src_dir,
1367 db_queue))
Masahiro Yamadab6160812015-05-20 11:36:07 +09001368
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001369 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001370 """Add a new subprocess if a vacant slot is found.
1371
1372 Arguments:
1373 defconfig: defconfig name to be put into.
1374
1375 Returns:
1376 Return True on success or False on failure
1377 """
1378 for slot in self.slots:
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001379 if slot.add(defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001380 return True
1381 return False
1382
1383 def available(self):
1384 """Check if there is a vacant slot.
1385
1386 Returns:
1387 Return True if at lease one vacant slot is found, False otherwise.
1388 """
1389 for slot in self.slots:
1390 if slot.poll():
1391 return True
1392 return False
1393
1394 def empty(self):
1395 """Check if all slots are vacant.
1396
1397 Returns:
1398 Return True if all the slots are vacant, False otherwise.
1399 """
1400 ret = True
1401 for slot in self.slots:
1402 if not slot.poll():
1403 ret = False
1404 return ret
1405
1406 def show_failed_boards(self):
1407 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada1271b672016-08-22 22:18:20 +09001408 boards = set()
Masahiro Yamada0153f032016-06-15 14:33:53 +09001409 output_file = 'moveconfig.failed'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001410
1411 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001412 boards |= slot.get_failed_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001413
Masahiro Yamada0153f032016-06-15 14:33:53 +09001414 if boards:
1415 boards = '\n'.join(boards) + '\n'
1416 msg = "The following boards were not processed due to error:\n"
1417 msg += boards
1418 msg += "(the list has been saved in %s)\n" % output_file
Simon Glass1f701862019-10-31 07:42:57 -06001419 print(color_text(self.options.color, COLOR_LIGHT_RED,
1420 msg), file=sys.stderr)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001421
Masahiro Yamada0153f032016-06-15 14:33:53 +09001422 with open(output_file, 'w') as f:
1423 f.write(boards)
Joe Hershbergerdade12e2015-05-19 13:21:22 -05001424
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001425 def show_suspicious_boards(self):
1426 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada1271b672016-08-22 22:18:20 +09001427 boards = set()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001428 output_file = 'moveconfig.suspicious'
1429
1430 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001431 boards |= slot.get_suspicious_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001432
1433 if boards:
1434 boards = '\n'.join(boards) + '\n'
1435 msg = "The following boards might have been converted incorrectly.\n"
1436 msg += "It is highly recommended to check them manually:\n"
1437 msg += boards
1438 msg += "(the list has been saved in %s)\n" % output_file
Simon Glass1f701862019-10-31 07:42:57 -06001439 print(color_text(self.options.color, COLOR_YELLOW,
1440 msg), file=sys.stderr)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001441
1442 with open(output_file, 'w') as f:
1443 f.write(boards)
1444
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001445class ReferenceSource:
1446
1447 """Reference source against which original configs should be parsed."""
1448
1449 def __init__(self, commit):
1450 """Create a reference source directory based on a specified commit.
1451
1452 Arguments:
1453 commit: commit to git-clone
1454 """
1455 self.src_dir = tempfile.mkdtemp()
Simon Glass1f701862019-10-31 07:42:57 -06001456 print("Cloning git repo to a separate work directory...")
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001457 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1458 cwd=self.src_dir)
Simon Glass1f701862019-10-31 07:42:57 -06001459 print("Checkout '%s' to build the original autoconf.mk." % \
1460 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001461 subprocess.check_output(['git', 'checkout', commit],
1462 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001463
1464 def __del__(self):
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001465 """Delete the reference source directory
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001466
1467 This function makes sure the temporary directory is cleaned away
1468 even if Python suddenly dies due to error. It should be done in here
1469 because it is guaranteed the destructor is always invoked when the
1470 instance of the class gets unreferenced.
1471 """
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001472 shutil.rmtree(self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001473
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001474 def get_dir(self):
1475 """Return the absolute path to the reference source directory."""
1476
1477 return self.src_dir
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001478
Simon Glass257f5232017-07-10 14:47:47 -06001479def move_config(toolchains, configs, options, db_queue):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001480 """Move config options to defconfig files.
1481
1482 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001483 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001484 options: option flags
1485 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001486 if len(configs) == 0:
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001487 if options.force_sync:
Simon Glass1f701862019-10-31 07:42:57 -06001488 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glass43cf08f2017-06-01 19:39:02 -06001489 elif options.build_db:
Simon Glass1f701862019-10-31 07:42:57 -06001490 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001491 else:
Simon Glass1f701862019-10-31 07:42:57 -06001492 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001493 else:
Simon Glass1f701862019-10-31 07:42:57 -06001494 print('Move ' + ', '.join(configs), end=' ')
1495 print('(jobs: %d)\n' % options.jobs)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001496
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001497 if options.git_ref:
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001498 reference_src = ReferenceSource(options.git_ref)
1499 reference_src_dir = reference_src.get_dir()
1500 else:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001501 reference_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001502
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001503 if options.defconfigs:
Masahiro Yamada3984d6e2016-10-19 14:39:54 +09001504 defconfigs = get_matched_defconfigs(options.defconfigs)
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001505 else:
Masahiro Yamada58175e32016-07-25 19:15:28 +09001506 defconfigs = get_all_defconfigs()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001507
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001508 progress = Progress(len(defconfigs))
Simon Glass257f5232017-07-10 14:47:47 -06001509 slots = Slots(toolchains, configs, options, progress, reference_src_dir,
1510 db_queue)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001511
1512 # Main loop to process defconfig files:
1513 # Add a new subprocess into a vacant slot.
1514 # Sleep if there is no available slot.
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001515 for defconfig in defconfigs:
1516 while not slots.add(defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001517 while not slots.available():
1518 # No available slot: sleep for a while
1519 time.sleep(SLEEP_TIME)
1520
1521 # wait until all the subprocesses finish
1522 while not slots.empty():
1523 time.sleep(SLEEP_TIME)
1524
Simon Glass1f701862019-10-31 07:42:57 -06001525 print('')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001526 slots.show_failed_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001527 slots.show_suspicious_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001528
Simon Glass44116332017-06-15 21:39:33 -06001529def find_kconfig_rules(kconf, config, imply_config):
1530 """Check whether a config has a 'select' or 'imply' keyword
1531
1532 Args:
Tom Rini3c5f4152019-09-20 17:42:09 -04001533 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-06-15 21:39:33 -06001534 config: Name of config to check (without CONFIG_ prefix)
1535 imply_config: Implying config (without CONFIG_ prefix) which may or
1536 may not have an 'imply' for 'config')
1537
1538 Returns:
1539 Symbol object for 'config' if found, else None
1540 """
Tom Rini3c5f4152019-09-20 17:42:09 -04001541 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -06001542 if sym:
Ulf Magnusson1682e052017-09-19 12:52:55 +02001543 for sel in sym.get_selected_symbols() | sym.get_implied_symbols():
Simon Glass44116332017-06-15 21:39:33 -06001544 if sel.get_name() == config:
1545 return sym
1546 return None
1547
1548def check_imply_rule(kconf, config, imply_config):
1549 """Check if we can add an 'imply' option
1550
1551 This finds imply_config in the Kconfig and looks to see if it is possible
1552 to add an 'imply' for 'config' to that part of the Kconfig.
1553
1554 Args:
Tom Rini3c5f4152019-09-20 17:42:09 -04001555 kconf: Kconfiglib.Kconfig object
Simon Glass44116332017-06-15 21:39:33 -06001556 config: Name of config to check (without CONFIG_ prefix)
1557 imply_config: Implying config (without CONFIG_ prefix) which may or
1558 may not have an 'imply' for 'config')
1559
1560 Returns:
1561 tuple:
1562 filename of Kconfig file containing imply_config, or None if none
1563 line number within the Kconfig file, or 0 if none
1564 message indicating the result
1565 """
Tom Rini3c5f4152019-09-20 17:42:09 -04001566 sym = kconf.syms.get(imply_config)
Simon Glass44116332017-06-15 21:39:33 -06001567 if not sym:
1568 return 'cannot find sym'
1569 locs = sym.get_def_locations()
1570 if len(locs) != 1:
1571 return '%d locations' % len(locs)
1572 fname, linenum = locs[0]
1573 cwd = os.getcwd()
1574 if cwd and fname.startswith(cwd):
1575 fname = fname[len(cwd) + 1:]
1576 file_line = ' at %s:%d' % (fname, linenum)
1577 with open(fname) as fd:
1578 data = fd.read().splitlines()
1579 if data[linenum - 1] != 'config %s' % imply_config:
1580 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1581 return fname, linenum, 'adding%s' % file_line
1582
1583def add_imply_rule(config, fname, linenum):
1584 """Add a new 'imply' option to a Kconfig
1585
1586 Args:
1587 config: config option to add an imply for (without CONFIG_ prefix)
1588 fname: Kconfig filename to update
1589 linenum: Line number to place the 'imply' before
1590
1591 Returns:
1592 Message indicating the result
1593 """
1594 file_line = ' at %s:%d' % (fname, linenum)
1595 data = open(fname).read().splitlines()
1596 linenum -= 1
1597
1598 for offset, line in enumerate(data[linenum:]):
1599 if line.strip().startswith('help') or not line:
1600 data.insert(linenum + offset, '\timply %s' % config)
1601 with open(fname, 'w') as fd:
1602 fd.write('\n'.join(data) + '\n')
1603 return 'added%s' % file_line
1604
1605 return 'could not insert%s'
1606
1607(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1608 1, 2, 4, 8)
Simon Glass92e55582017-06-15 21:39:32 -06001609
1610IMPLY_FLAGS = {
1611 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1612 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1613 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glass44116332017-06-15 21:39:33 -06001614 'non-arch-board': [
1615 IMPLY_NON_ARCH_BOARD,
1616 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass92e55582017-06-15 21:39:32 -06001617};
1618
Simon Glass44116332017-06-15 21:39:33 -06001619def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1620 check_kconfig=True, find_superset=False):
Simon Glassc6e73cf2017-06-01 19:39:03 -06001621 """Find CONFIG options which imply those in the list
1622
1623 Some CONFIG options can be implied by others and this can help to reduce
1624 the size of the defconfig files. For example, CONFIG_X86 implies
1625 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1626 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1627 each of the x86 defconfig files.
1628
1629 This function uses the moveconfig database to find such options. It
1630 displays a list of things that could possibly imply those in the list.
1631 The algorithm ignores any that start with CONFIG_TARGET since these
1632 typically refer to only a few defconfigs (often one). It also does not
1633 display a config with less than 5 defconfigs.
1634
1635 The algorithm works using sets. For each target config in config_list:
1636 - Get the set 'defconfigs' which use that target config
1637 - For each config (from a list of all configs):
1638 - Get the set 'imply_defconfig' of defconfigs which use that config
1639 -
1640 - If imply_defconfigs contains anything not in defconfigs then
1641 this config does not imply the target config
1642
1643 Params:
1644 config_list: List of CONFIG options to check (each a string)
Simon Glass44116332017-06-15 21:39:33 -06001645 add_imply: Automatically add an 'imply' for each config.
Simon Glass92e55582017-06-15 21:39:32 -06001646 imply_flags: Flags which control which implying configs are allowed
1647 (IMPLY_...)
Simon Glass44116332017-06-15 21:39:33 -06001648 skip_added: Don't show options which already have an imply added.
1649 check_kconfig: Check if implied symbols already have an 'imply' or
1650 'select' for the target config, and show this information if so.
Simon Glassc6e73cf2017-06-01 19:39:03 -06001651 find_superset: True to look for configs which are a superset of those
1652 already found. So for example if CONFIG_EXYNOS5 implies an option,
1653 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1654 implies that option, this will drop the former in favour of the
1655 latter. In practice this option has not proved very used.
1656
1657 Note the terminoloy:
1658 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1659 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1660 """
Simon Glass44116332017-06-15 21:39:33 -06001661 kconf = KconfigScanner().conf if check_kconfig else None
1662 if add_imply and add_imply != 'all':
1663 add_imply = add_imply.split()
1664
Simon Glassc6e73cf2017-06-01 19:39:03 -06001665 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1666 config_db = {}
1667
1668 # Holds a dict containing the set of defconfigs that contain each config
1669 # key is config, value is set of defconfigs using that config
1670 defconfig_db = collections.defaultdict(set)
1671
1672 # Set of all config options we have seen
1673 all_configs = set()
1674
1675 # Set of all defconfigs we have seen
1676 all_defconfigs = set()
1677
1678 # Read in the database
1679 configs = {}
1680 with open(CONFIG_DATABASE) as fd:
1681 for line in fd.readlines():
1682 line = line.rstrip()
1683 if not line: # Separator between defconfigs
1684 config_db[defconfig] = configs
1685 all_defconfigs.add(defconfig)
1686 configs = {}
1687 elif line[0] == ' ': # CONFIG line
1688 config, value = line.strip().split('=', 1)
1689 configs[config] = value
1690 defconfig_db[config].add(defconfig)
1691 all_configs.add(config)
1692 else: # New defconfig
1693 defconfig = line
1694
1695 # Work through each target config option in tern, independently
1696 for config in config_list:
1697 defconfigs = defconfig_db.get(config)
1698 if not defconfigs:
Simon Glass1f701862019-10-31 07:42:57 -06001699 print('%s not found in any defconfig' % config)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001700 continue
1701
1702 # Get the set of defconfigs without this one (since a config cannot
1703 # imply itself)
1704 non_defconfigs = all_defconfigs - defconfigs
1705 num_defconfigs = len(defconfigs)
Simon Glass1f701862019-10-31 07:42:57 -06001706 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1707 len(all_configs)))
Simon Glassc6e73cf2017-06-01 19:39:03 -06001708
1709 # This will hold the results: key=config, value=defconfigs containing it
1710 imply_configs = {}
1711 rest_configs = all_configs - set([config])
1712
1713 # Look at every possible config, except the target one
1714 for imply_config in rest_configs:
Simon Glass92e55582017-06-15 21:39:32 -06001715 if 'ERRATUM' in imply_config:
Simon Glassc6e73cf2017-06-01 19:39:03 -06001716 continue
Simon Glass92e55582017-06-15 21:39:32 -06001717 if not (imply_flags & IMPLY_CMD):
1718 if 'CONFIG_CMD' in imply_config:
1719 continue
1720 if not (imply_flags & IMPLY_TARGET):
1721 if 'CONFIG_TARGET' in imply_config:
1722 continue
Simon Glassc6e73cf2017-06-01 19:39:03 -06001723
1724 # Find set of defconfigs that have this config
1725 imply_defconfig = defconfig_db[imply_config]
1726
1727 # Get the intersection of this with defconfigs containing the
1728 # target config
1729 common_defconfigs = imply_defconfig & defconfigs
1730
1731 # Get the set of defconfigs containing this config which DO NOT
1732 # also contain the taret config. If this set is non-empty it means
1733 # that this config affects other defconfigs as well as (possibly)
1734 # the ones affected by the target config. This means it implies
1735 # things we don't want to imply.
1736 not_common_defconfigs = imply_defconfig & non_defconfigs
1737 if not_common_defconfigs:
1738 continue
1739
1740 # If there are common defconfigs, imply_config may be useful
1741 if common_defconfigs:
1742 skip = False
1743 if find_superset:
Simon Glass1f701862019-10-31 07:42:57 -06001744 for prev in list(imply_configs.keys()):
Simon Glassc6e73cf2017-06-01 19:39:03 -06001745 prev_count = len(imply_configs[prev])
1746 count = len(common_defconfigs)
1747 if (prev_count > count and
1748 (imply_configs[prev] & common_defconfigs ==
1749 common_defconfigs)):
1750 # skip imply_config because prev is a superset
1751 skip = True
1752 break
1753 elif count > prev_count:
1754 # delete prev because imply_config is a superset
1755 del imply_configs[prev]
1756 if not skip:
1757 imply_configs[imply_config] = common_defconfigs
1758
1759 # Now we have a dict imply_configs of configs which imply each config
1760 # The value of each dict item is the set of defconfigs containing that
1761 # config. Rank them so that we print the configs that imply the largest
1762 # number of defconfigs first.
Simon Glass44116332017-06-15 21:39:33 -06001763 ranked_iconfigs = sorted(imply_configs,
Simon Glassc6e73cf2017-06-01 19:39:03 -06001764 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glass44116332017-06-15 21:39:33 -06001765 kconfig_info = ''
1766 cwd = os.getcwd()
1767 add_list = collections.defaultdict(list)
1768 for iconfig in ranked_iconfigs:
1769 num_common = len(imply_configs[iconfig])
Simon Glassc6e73cf2017-06-01 19:39:03 -06001770
1771 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass92e55582017-06-15 21:39:32 -06001772 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glassc6e73cf2017-06-01 19:39:03 -06001773 continue
Simon Glass44116332017-06-15 21:39:33 -06001774 missing = defconfigs - imply_configs[iconfig]
Simon Glassc6e73cf2017-06-01 19:39:03 -06001775 missing_str = ', '.join(missing) if missing else 'all'
1776 missing_str = ''
Simon Glass44116332017-06-15 21:39:33 -06001777 show = True
1778 if kconf:
1779 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1780 iconfig[CONFIG_LEN:])
1781 kconfig_info = ''
1782 if sym:
1783 locs = sym.get_def_locations()
1784 if len(locs) == 1:
1785 fname, linenum = locs[0]
1786 if cwd and fname.startswith(cwd):
1787 fname = fname[len(cwd) + 1:]
1788 kconfig_info = '%s:%d' % (fname, linenum)
1789 if skip_added:
1790 show = False
1791 else:
Tom Rini3c5f4152019-09-20 17:42:09 -04001792 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glass44116332017-06-15 21:39:33 -06001793 fname = ''
1794 if sym:
1795 locs = sym.get_def_locations()
1796 if len(locs) == 1:
1797 fname, linenum = locs[0]
1798 if cwd and fname.startswith(cwd):
1799 fname = fname[len(cwd) + 1:]
1800 in_arch_board = not sym or (fname.startswith('arch') or
1801 fname.startswith('board'))
1802 if (not in_arch_board and
1803 not (imply_flags & IMPLY_NON_ARCH_BOARD)):
1804 continue
1805
1806 if add_imply and (add_imply == 'all' or
1807 iconfig in add_imply):
1808 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1809 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1810 if fname:
1811 add_list[fname].append(linenum)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001812
Simon Glass44116332017-06-15 21:39:33 -06001813 if show and kconfig_info != 'skip':
Simon Glass1f701862019-10-31 07:42:57 -06001814 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1815 kconfig_info, missing_str))
Simon Glass44116332017-06-15 21:39:33 -06001816
1817 # Having collected a list of things to add, now we add them. We process
1818 # each file from the largest line number to the smallest so that
1819 # earlier additions do not affect our line numbers. E.g. if we added an
1820 # imply at line 20 it would change the position of each line after
1821 # that.
Simon Glass1f701862019-10-31 07:42:57 -06001822 for fname, linenums in add_list.items():
Simon Glass44116332017-06-15 21:39:33 -06001823 for linenum in sorted(linenums, reverse=True):
1824 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
1825
Simon Glassc6e73cf2017-06-01 19:39:03 -06001826
Masahiro Yamadab6160812015-05-20 11:36:07 +09001827def main():
1828 try:
1829 cpu_count = multiprocessing.cpu_count()
1830 except NotImplementedError:
1831 cpu_count = 1
1832
1833 parser = optparse.OptionParser()
1834 # Add options here
Simon Glass44116332017-06-15 21:39:33 -06001835 parser.add_option('-a', '--add-imply', type='string', default='',
1836 help='comma-separated list of CONFIG options to add '
1837 "an 'imply' statement to for the CONFIG in -i")
1838 parser.add_option('-A', '--skip-added', action='store_true', default=False,
1839 help="don't show options which are already marked as "
1840 'implying others')
Simon Glass43cf08f2017-06-01 19:39:02 -06001841 parser.add_option('-b', '--build-db', action='store_true', default=False,
1842 help='build a CONFIG database')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001843 parser.add_option('-c', '--color', action='store_true', default=False,
1844 help='display the log in color')
Simon Glass8bf41c22016-09-12 23:18:21 -06001845 parser.add_option('-C', '--commit', action='store_true', default=False,
1846 help='Create a git commit for the operation')
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001847 parser.add_option('-d', '--defconfigs', type='string',
Simon Glass8f3cf312017-06-01 19:38:59 -06001848 help='a file containing a list of defconfigs to move, '
1849 "one per line (for example 'snow_defconfig') "
1850 "or '-' to read from stdin")
Simon Glassc6e73cf2017-06-01 19:39:03 -06001851 parser.add_option('-i', '--imply', action='store_true', default=False,
1852 help='find options which imply others')
Simon Glass92e55582017-06-15 21:39:32 -06001853 parser.add_option('-I', '--imply-flags', type='string', default='',
1854 help="control the -i option ('help' for help")
Masahiro Yamadab6160812015-05-20 11:36:07 +09001855 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1856 help='perform a trial run (show log with no changes)')
1857 parser.add_option('-e', '--exit-on-error', action='store_true',
1858 default=False,
1859 help='exit immediately on any error')
Masahiro Yamada83c17672016-05-19 15:52:08 +09001860 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1861 help='force sync by savedefconfig')
Masahiro Yamada6d139172016-08-22 22:18:22 +09001862 parser.add_option('-S', '--spl', action='store_true', default=False,
1863 help='parse config options defined for SPL build')
Joe Hershberger23475932015-05-19 13:21:20 -05001864 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1865 action='store_true', default=False,
1866 help='only cleanup the headers')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001867 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1868 help='the number of jobs to run simultaneously')
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001869 parser.add_option('-r', '--git-ref', type='string',
1870 help='the git ref to clone for building the autoconf.mk')
Simon Glass13e05a02016-09-12 23:18:20 -06001871 parser.add_option('-y', '--yes', action='store_true', default=False,
1872 help="respond 'yes' to any prompts")
Joe Hershberger808b63f2015-05-19 13:21:24 -05001873 parser.add_option('-v', '--verbose', action='store_true', default=False,
1874 help='show any build errors as boards are built')
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001875 parser.usage += ' CONFIG ...'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001876
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001877 (options, configs) = parser.parse_args()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001878
Simon Glassc6e73cf2017-06-01 19:39:03 -06001879 if len(configs) == 0 and not any((options.force_sync, options.build_db,
1880 options.imply)):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001881 parser.print_usage()
1882 sys.exit(1)
1883
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001884 # prefix the option name with CONFIG_ if missing
1885 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1886 for config in configs ]
Masahiro Yamadab6160812015-05-20 11:36:07 +09001887
Joe Hershberger23475932015-05-19 13:21:20 -05001888 check_top_directory()
1889
Simon Glassc6e73cf2017-06-01 19:39:03 -06001890 if options.imply:
Simon Glass92e55582017-06-15 21:39:32 -06001891 imply_flags = 0
Simon Glass5f096922017-07-10 14:47:46 -06001892 if options.imply_flags == 'all':
1893 imply_flags = -1
1894
1895 elif options.imply_flags:
1896 for flag in options.imply_flags.split(','):
1897 bad = flag not in IMPLY_FLAGS
1898 if bad:
Simon Glass1f701862019-10-31 07:42:57 -06001899 print("Invalid flag '%s'" % flag)
Simon Glass5f096922017-07-10 14:47:46 -06001900 if flag == 'help' or bad:
Simon Glass1f701862019-10-31 07:42:57 -06001901 print("Imply flags: (separate with ',')")
1902 for name, info in IMPLY_FLAGS.items():
1903 print(' %-15s: %s' % (name, info[1]))
Simon Glass5f096922017-07-10 14:47:46 -06001904 parser.print_usage()
1905 sys.exit(1)
1906 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass92e55582017-06-15 21:39:32 -06001907
Simon Glass44116332017-06-15 21:39:33 -06001908 do_imply_config(configs, options.add_imply, imply_flags,
1909 options.skip_added)
Simon Glassc6e73cf2017-06-01 19:39:03 -06001910 return
1911
Simon Glass43cf08f2017-06-01 19:39:02 -06001912 config_db = {}
Simon Glass1f701862019-10-31 07:42:57 -06001913 db_queue = queue.Queue()
Simon Glass43cf08f2017-06-01 19:39:02 -06001914 t = DatabaseThread(config_db, db_queue)
1915 t.setDaemon(True)
1916 t.start()
1917
Joe Hershberger23475932015-05-19 13:21:20 -05001918 if not options.cleanup_headers_only:
Masahiro Yamadad0a9d2a2016-07-25 19:15:23 +09001919 check_clean_directory()
Simon Glass1f701862019-10-31 07:42:57 -06001920 bsettings.Setup('')
Simon Glass257f5232017-07-10 14:47:47 -06001921 toolchains = toolchain.Toolchains()
1922 toolchains.GetSettings()
1923 toolchains.Scan(verbose=False)
1924 move_config(toolchains, configs, options, db_queue)
Simon Glass43cf08f2017-06-01 19:39:02 -06001925 db_queue.join()
Joe Hershberger23475932015-05-19 13:21:20 -05001926
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001927 if configs:
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +09001928 cleanup_headers(configs, options)
Masahiro Yamadadce28de2016-07-25 19:15:29 +09001929 cleanup_extra_options(configs, options)
Chris Packham9d5274f2017-05-02 21:30:47 +12001930 cleanup_whitelist(configs, options)
Chris Packham0e6deff2017-05-02 21:30:48 +12001931 cleanup_readme(configs, options)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001932
Simon Glass8bf41c22016-09-12 23:18:21 -06001933 if options.commit:
1934 subprocess.call(['git', 'add', '-u'])
1935 if configs:
1936 msg = 'Convert %s %sto Kconfig' % (configs[0],
1937 'et al ' if len(configs) > 1 else '')
1938 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1939 '\n '.join(configs))
1940 else:
1941 msg = 'configs: Resync with savedefconfig'
1942 msg += '\n\nRsync all defconfig files using moveconfig.py'
1943 subprocess.call(['git', 'commit', '-s', '-m', msg])
1944
Simon Glass43cf08f2017-06-01 19:39:02 -06001945 if options.build_db:
1946 with open(CONFIG_DATABASE, 'w') as fd:
Simon Glass1f701862019-10-31 07:42:57 -06001947 for defconfig, configs in config_db.items():
Simon Glass1c879312017-08-13 16:02:54 -06001948 fd.write('%s\n' % defconfig)
Simon Glass43cf08f2017-06-01 19:39:02 -06001949 for config in sorted(configs.keys()):
Simon Glass1c879312017-08-13 16:02:54 -06001950 fd.write(' %s=%s\n' % (config, configs[config]))
1951 fd.write('\n')
Simon Glass43cf08f2017-06-01 19:39:02 -06001952
Masahiro Yamadab6160812015-05-20 11:36:07 +09001953if __name__ == '__main__':
1954 main()