blob: 41dd803c4ef8e368e385881f651d9e801d156cb6 [file] [log] [blame]
Simon Glass793dca32019-10-31 07:42:57 -06001#!/usr/bin/env python3
Tom Rini83d290c2018-05-06 17:58:06 -04002# SPDX-License-Identifier: GPL-2.0+
Masahiro Yamada5a27c732015-05-20 11:36:07 +09003#
4# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5#
Masahiro Yamada5a27c732015-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
Simon Glass7570d9b2021-03-26 16:17:29 +130015Installing
16----------
17
18You may need to install 'python3-asteval' for the 'asteval' module.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090019
20Usage
21-----
22
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090023First, you must edit the Kconfig to add the menu entries for the configs
Joe Hershberger96464ba2015-05-19 13:21:17 -050024you are moving.
25
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090026And then run this tool giving CONFIG names you want to move.
27For example, if you want to move CONFIG_CMD_USB and CONFIG_SYS_TEXT_BASE,
28simply type as follows:
Masahiro Yamada5a27c732015-05-20 11:36:07 +090029
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090030 $ tools/moveconfig.py CONFIG_CMD_USB CONFIG_SYS_TEXT_BASE
Masahiro Yamada5a27c732015-05-20 11:36:07 +090031
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090032The tool walks through all the defconfig files and move the given CONFIGs.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090033
34The log is also displayed on the terminal.
35
Masahiro Yamada1d085562016-05-19 15:52:02 +090036The log is printed for each defconfig as follows:
Masahiro Yamada5a27c732015-05-20 11:36:07 +090037
Masahiro Yamada1d085562016-05-19 15:52:02 +090038<defconfig_name>
39 <action1>
40 <action2>
41 <action3>
42 ...
Masahiro Yamada5a27c732015-05-20 11:36:07 +090043
Masahiro Yamada1d085562016-05-19 15:52:02 +090044<defconfig_name> is the name of the defconfig.
45
46<action*> shows what the tool did for that defconfig.
Masahiro Yamadac21fc7e2016-08-21 16:12:36 +090047It looks like one of the following:
Masahiro Yamada5a27c732015-05-20 11:36:07 +090048
49 - Move 'CONFIG_... '
50 This config option was moved to the defconfig
51
Masahiro Yamadacc008292016-05-19 15:51:56 +090052 - CONFIG_... is not defined in Kconfig. Do nothing.
Masahiro Yamada916224c2016-08-22 22:18:21 +090053 The entry for this CONFIG was not found in Kconfig. The option is not
54 defined in the config header, either. So, this case can be just skipped.
55
56 - CONFIG_... is not defined in Kconfig (suspicious). Do nothing.
57 This option is defined in the config header, but its entry was not found
58 in Kconfig.
Masahiro Yamadacc008292016-05-19 15:51:56 +090059 There are two common cases:
60 - You forgot to create an entry for the CONFIG before running
61 this tool, or made a typo in a CONFIG passed to this tool.
62 - The entry was hidden due to unmet 'depends on'.
Masahiro Yamada916224c2016-08-22 22:18:21 +090063 The tool does not know if the result is reasonable, so please check it
64 manually.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090065
Masahiro Yamadacc008292016-05-19 15:51:56 +090066 - 'CONFIG_...' is the same as the define in Kconfig. Do nothing.
67 The define in the config header matched the one in Kconfig.
68 We do not need to touch it.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090069
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +090070 - Compiler is missing. Do nothing.
71 The compiler specified for this architecture was not found
72 in your PATH environment.
73 (If -e option is passed, the tool exits immediately.)
74
75 - Failed to process.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090076 An error occurred during processing this defconfig. Skipped.
77 (If -e option is passed, the tool exits immediately on error.)
78
79Finally, you will be asked, Clean up headers? [y/n]:
80
81If you say 'y' here, the unnecessary config defines are removed
82from the config headers (include/configs/*.h).
83It just uses the regex method, so you should not rely on it.
84Just in case, please do 'git diff' to see what happened.
85
86
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090087How does it work?
88-----------------
Masahiro Yamada5a27c732015-05-20 11:36:07 +090089
90This tool runs configuration and builds include/autoconf.mk for every
91defconfig. The config options defined in Kconfig appear in the .config
92file (unless they are hidden because of unmet dependency.)
93On the other hand, the config options defined by board headers are seen
94in include/autoconf.mk. The tool looks for the specified options in both
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090095of them to decide the appropriate action for the options. If the given
96config option is found in the .config, but its value does not match the
97one from the board header, the config option in the .config is replaced
98with the define in the board header. Then, the .config is synced by
99"make savedefconfig" and the defconfig is updated with it.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900100
101For faster processing, this tool handles multi-threading. It creates
102separate build directories where the out-of-tree build is run. The
103temporary build directories are automatically created and deleted as
104needed. The number of threads are chosen based on the number of the CPU
105cores of your system although you can change it via -j (--jobs) option.
106
107
108Toolchains
109----------
110
111Appropriate toolchain are necessary to generate include/autoconf.mk
112for all the architectures supported by U-Boot. Most of them are available
Simon Glass6821a742017-07-10 14:47:47 -0600113at the kernel.org site, some are not provided by kernel.org. This tool uses
114the same tools as buildman, so see that tool for setup (e.g. --fetch-arch).
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900115
116
Simon Glassddf91c62017-06-01 19:39:00 -0600117Tips and trips
118--------------
119
120To sync only X86 defconfigs:
121
122 ./tools/moveconfig.py -s -d <(grep -l X86 configs/*)
123
124or:
125
126 grep -l X86 configs/* | ./tools/moveconfig.py -s -d -
127
128To process CONFIG_CMD_FPGAD only for a subset of configs based on path match:
129
130 ls configs/{hrcon*,iocon*,strider*} | \
131 ./tools/moveconfig.py -Cy CONFIG_CMD_FPGAD -d -
132
133
Simon Glass99b66602017-06-01 19:39:03 -0600134Finding implied CONFIGs
135-----------------------
136
137Some CONFIG options can be implied by others and this can help to reduce
138the size of the defconfig files. For example, CONFIG_X86 implies
139CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
140all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
141each of the x86 defconfig files.
142
143This tool can help find such configs. To use it, first build a database:
144
145 ./tools/moveconfig.py -b
146
147Then try to query it:
148
149 ./tools/moveconfig.py -i CONFIG_CMD_IRQ
150 CONFIG_CMD_IRQ found in 311/2384 defconfigs
151 44 : CONFIG_SYS_FSL_ERRATUM_IFC_A002769
152 41 : CONFIG_SYS_FSL_ERRATUM_A007075
153 31 : CONFIG_SYS_FSL_DDR_VER_44
154 28 : CONFIG_ARCH_P1010
155 28 : CONFIG_SYS_FSL_ERRATUM_P1010_A003549
156 28 : CONFIG_SYS_FSL_ERRATUM_SEC_A003571
157 28 : CONFIG_SYS_FSL_ERRATUM_IFC_A003399
158 25 : CONFIG_SYS_FSL_ERRATUM_A008044
159 22 : CONFIG_ARCH_P1020
160 21 : CONFIG_SYS_FSL_DDR_VER_46
161 20 : CONFIG_MAX_PIRQ_LINKS
162 20 : CONFIG_HPET_ADDRESS
163 20 : CONFIG_X86
164 20 : CONFIG_PCIE_ECAM_SIZE
165 20 : CONFIG_IRQ_SLOT_COUNT
166 20 : CONFIG_I8259_PIC
167 20 : CONFIG_CPU_ADDR_BITS
168 20 : CONFIG_RAMBASE
169 20 : CONFIG_SYS_FSL_ERRATUM_A005871
170 20 : CONFIG_PCIE_ECAM_BASE
171 20 : CONFIG_X86_TSC_TIMER
172 20 : CONFIG_I8254_TIMER
173 20 : CONFIG_CMD_GETTIME
174 19 : CONFIG_SYS_FSL_ERRATUM_A005812
175 18 : CONFIG_X86_RUN_32BIT
176 17 : CONFIG_CMD_CHIP_CONFIG
177 ...
178
179This shows a list of config options which might imply CONFIG_CMD_EEPROM along
180with how many defconfigs they cover. From this you can see that CONFIG_X86
181implies CONFIG_CMD_EEPROM. Therefore, instead of adding CONFIG_CMD_EEPROM to
182the defconfig of every x86 board, you could add a single imply line to the
183Kconfig file:
184
185 config X86
186 bool "x86 architecture"
187 ...
188 imply CMD_EEPROM
189
190That will cover 20 defconfigs. Many of the options listed are not suitable as
191they are not related. E.g. it would be odd for CONFIG_CMD_GETTIME to imply
192CMD_EEPROM.
193
194Using this search you can reduce the size of moveconfig patches.
195
Simon Glasscb008832017-06-15 21:39:33 -0600196You can automatically add 'imply' statements in the Kconfig with the -a
197option:
198
199 ./tools/moveconfig.py -s -i CONFIG_SCSI \
200 -a CONFIG_ARCH_LS1021A,CONFIG_ARCH_LS1043A
201
202This will add 'imply SCSI' to the two CONFIG options mentioned, assuming that
203the database indicates that they do actually imply CONFIG_SCSI and do not
204already have an 'imply SCSI'.
205
206The output shows where the imply is added:
207
208 18 : CONFIG_ARCH_LS1021A arch/arm/cpu/armv7/ls102xa/Kconfig:1
209 13 : CONFIG_ARCH_LS1043A arch/arm/cpu/armv8/fsl-layerscape/Kconfig:11
210 12 : CONFIG_ARCH_LS1046A arch/arm/cpu/armv8/fsl-layerscape/Kconfig:31
211
212The first number is the number of boards which can avoid having a special
213CONFIG_SCSI option in their defconfig file if this 'imply' is added.
214The location at the right is the Kconfig file and line number where the config
215appears. For example, adding 'imply CONFIG_SCSI' to the 'config ARCH_LS1021A'
216in arch/arm/cpu/armv7/ls102xa/Kconfig at line 1 will help 18 boards to reduce
217the size of their defconfig files.
218
219If you want to add an 'imply' to every imply config in the list, you can use
220
221 ./tools/moveconfig.py -s -i CONFIG_SCSI -a all
222
223To control which ones are displayed, use -I <list> where list is a list of
224options (use '-I help' to see possible options and their meaning).
225
226To skip showing you options that already have an 'imply' attached, use -A.
227
228When you have finished adding 'imply' options you can regenerate the
229defconfig files for affected boards with something like:
230
231 git show --stat | ./tools/moveconfig.py -s -d -
232
233This will regenerate only those defconfigs changed in the current commit.
234If you start with (say) 100 defconfigs being changed in the commit, and add
235a few 'imply' options as above, then regenerate, hopefully you can reduce the
236number of defconfigs changed in the commit.
237
Simon Glass99b66602017-06-01 19:39:03 -0600238
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900239Available options
240-----------------
241
242 -c, --color
243 Surround each portion of the log with escape sequences to display it
244 in color on the terminal.
245
Simon Glass9ede2122016-09-12 23:18:21 -0600246 -C, --commit
247 Create a git commit with the changes when the operation is complete. A
248 standard commit message is used which may need to be edited.
249
Joe Hershberger91040e82015-05-19 13:21:19 -0500250 -d, --defconfigs
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900251 Specify a file containing a list of defconfigs to move. The defconfig
Simon Glassddf91c62017-06-01 19:39:00 -0600252 files can be given with shell-style wildcards. Use '-' to read from stdin.
Joe Hershberger91040e82015-05-19 13:21:19 -0500253
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900254 -n, --dry-run
Masahiro Yamadab6ef3932016-05-19 15:51:58 +0900255 Perform a trial run that does not make any changes. It is useful to
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900256 see what is going to happen before one actually runs it.
257
258 -e, --exit-on-error
259 Exit immediately if Make exits with a non-zero status while processing
260 a defconfig file.
261
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900262 -s, --force-sync
263 Do "make savedefconfig" forcibly for all the defconfig files.
264 If not specified, "make savedefconfig" only occurs for cases
265 where at least one CONFIG was moved.
266
Masahiro Yamada07913d12016-08-22 22:18:22 +0900267 -S, --spl
268 Look for moved config options in spl/include/autoconf.mk instead of
269 include/autoconf.mk. This is useful for moving options for SPL build
270 because SPL related options (mostly prefixed with CONFIG_SPL_) are
271 sometimes blocked by CONFIG_SPL_BUILD ifdef conditionals.
272
Joe Hershberger2144f882015-05-19 13:21:20 -0500273 -H, --headers-only
274 Only cleanup the headers; skip the defconfig processing
275
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900276 -j, --jobs
277 Specify the number of threads to run simultaneously. If not specified,
278 the number of threads is the same as the number of CPU cores.
279
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500280 -r, --git-ref
281 Specify the git ref to clone for building the autoconf.mk. If unspecified
282 use the CWD. This is useful for when changes to the Kconfig affect the
283 default values and you want to capture the state of the defconfig from
284 before that change was in effect. If in doubt, specify a ref pre-Kconfig
285 changes (use HEAD if Kconfig changes are not committed). Worst case it will
286 take a bit longer to run, but will always do the right thing.
287
Joe Hershberger95bf9c72015-05-19 13:21:24 -0500288 -v, --verbose
289 Show any build errors as boards are built
290
Simon Glass6b403df2016-09-12 23:18:20 -0600291 -y, --yes
292 Instead of prompting, automatically go ahead with all operations. This
Simon Glassddf91c62017-06-01 19:39:00 -0600293 includes cleaning up headers, CONFIG_SYS_EXTRA_OPTIONS, the config whitelist
294 and the README.
Simon Glass6b403df2016-09-12 23:18:20 -0600295
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900296To see the complete list of supported options, run
297
298 $ tools/moveconfig.py -h
299
300"""
301
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100302import asteval
Simon Glass99b66602017-06-01 19:39:03 -0600303import collections
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900304import copy
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900305import difflib
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900306import filecmp
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900307import fnmatch
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900308import glob
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900309import multiprocessing
310import optparse
311import os
Simon Glass793dca32019-10-31 07:42:57 -0600312import queue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900313import re
314import shutil
315import subprocess
316import sys
317import tempfile
Simon Glassd73fcb12017-06-01 19:39:02 -0600318import threading
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900319import time
320
Simon Glass0ede00f2020-04-17 18:09:02 -0600321from buildman import bsettings
322from buildman import kconfiglib
323from buildman import toolchain
Simon Glasscb008832017-06-15 21:39:33 -0600324
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900325SHOW_GNU_MAKE = 'scripts/show-gnu-make'
326SLEEP_TIME=0.03
327
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900328STATE_IDLE = 0
329STATE_DEFCONFIG = 1
330STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -0500331STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900332
333ACTION_MOVE = 0
Masahiro Yamadacc008292016-05-19 15:51:56 +0900334ACTION_NO_ENTRY = 1
Masahiro Yamada916224c2016-08-22 22:18:21 +0900335ACTION_NO_ENTRY_WARN = 2
336ACTION_NO_CHANGE = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900337
338COLOR_BLACK = '0;30'
339COLOR_RED = '0;31'
340COLOR_GREEN = '0;32'
341COLOR_BROWN = '0;33'
342COLOR_BLUE = '0;34'
343COLOR_PURPLE = '0;35'
344COLOR_CYAN = '0;36'
345COLOR_LIGHT_GRAY = '0;37'
346COLOR_DARK_GRAY = '1;30'
347COLOR_LIGHT_RED = '1;31'
348COLOR_LIGHT_GREEN = '1;32'
349COLOR_YELLOW = '1;33'
350COLOR_LIGHT_BLUE = '1;34'
351COLOR_LIGHT_PURPLE = '1;35'
352COLOR_LIGHT_CYAN = '1;36'
353COLOR_WHITE = '1;37'
354
Simon Glassf3b8e642017-06-01 19:39:01 -0600355AUTO_CONF_PATH = 'include/config/auto.conf'
Simon Glassd73fcb12017-06-01 19:39:02 -0600356CONFIG_DATABASE = 'moveconfig.db'
Simon Glassf3b8e642017-06-01 19:39:01 -0600357
Simon Glasscb008832017-06-15 21:39:33 -0600358CONFIG_LEN = len('CONFIG_')
Simon Glassf3b8e642017-06-01 19:39:01 -0600359
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200360SIZES = {
361 "SZ_1": 0x00000001, "SZ_2": 0x00000002,
362 "SZ_4": 0x00000004, "SZ_8": 0x00000008,
363 "SZ_16": 0x00000010, "SZ_32": 0x00000020,
364 "SZ_64": 0x00000040, "SZ_128": 0x00000080,
365 "SZ_256": 0x00000100, "SZ_512": 0x00000200,
366 "SZ_1K": 0x00000400, "SZ_2K": 0x00000800,
367 "SZ_4K": 0x00001000, "SZ_8K": 0x00002000,
368 "SZ_16K": 0x00004000, "SZ_32K": 0x00008000,
369 "SZ_64K": 0x00010000, "SZ_128K": 0x00020000,
370 "SZ_256K": 0x00040000, "SZ_512K": 0x00080000,
371 "SZ_1M": 0x00100000, "SZ_2M": 0x00200000,
372 "SZ_4M": 0x00400000, "SZ_8M": 0x00800000,
373 "SZ_16M": 0x01000000, "SZ_32M": 0x02000000,
374 "SZ_64M": 0x04000000, "SZ_128M": 0x08000000,
375 "SZ_256M": 0x10000000, "SZ_512M": 0x20000000,
376 "SZ_1G": 0x40000000, "SZ_2G": 0x80000000,
377 "SZ_4G": 0x100000000
378}
379
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900380### helper functions ###
381def get_devnull():
382 """Get the file object of '/dev/null' device."""
383 try:
384 devnull = subprocess.DEVNULL # py3k
385 except AttributeError:
386 devnull = open(os.devnull, 'wb')
387 return devnull
388
389def check_top_directory():
390 """Exit if we are not at the top of source directory."""
391 for f in ('README', 'Licenses'):
392 if not os.path.exists(f):
393 sys.exit('Please run at the top of source directory.')
394
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900395def check_clean_directory():
396 """Exit if the source tree is not clean."""
397 for f in ('.config', 'include/config'):
398 if os.path.exists(f):
399 sys.exit("source tree is not clean, please run 'make mrproper'")
400
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900401def get_make_cmd():
402 """Get the command name of GNU Make.
403
404 U-Boot needs GNU Make for building, but the command name is not
405 necessarily "make". (for example, "gmake" on FreeBSD).
406 Returns the most appropriate command name on your system.
407 """
408 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
409 ret = process.communicate()
410 if process.returncode:
411 sys.exit('GNU Make not found')
412 return ret[0].rstrip()
413
Simon Glass25f978c2017-06-01 19:38:58 -0600414def get_matched_defconfig(line):
415 """Get the defconfig files that match a pattern
416
417 Args:
418 line: Path or filename to match, e.g. 'configs/snow_defconfig' or
419 'k2*_defconfig'. If no directory is provided, 'configs/' is
420 prepended
421
422 Returns:
423 a list of matching defconfig files
424 """
425 dirname = os.path.dirname(line)
426 if dirname:
427 pattern = line
428 else:
429 pattern = os.path.join('configs', line)
430 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
431
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900432def get_matched_defconfigs(defconfigs_file):
Simon Glassee4e61b2017-06-01 19:38:59 -0600433 """Get all the defconfig files that match the patterns in a file.
434
435 Args:
436 defconfigs_file: File containing a list of defconfigs to process, or
437 '-' to read the list from stdin
438
439 Returns:
440 A list of paths to defconfig files, with no duplicates
441 """
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900442 defconfigs = []
Simon Glassee4e61b2017-06-01 19:38:59 -0600443 if defconfigs_file == '-':
444 fd = sys.stdin
445 defconfigs_file = 'stdin'
446 else:
447 fd = open(defconfigs_file)
448 for i, line in enumerate(fd):
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900449 line = line.strip()
450 if not line:
451 continue # skip blank lines silently
Simon Glass2ddd85d2017-06-15 21:39:31 -0600452 if ' ' in line:
453 line = line.split(' ')[0] # handle 'git log' input
Simon Glass25f978c2017-06-01 19:38:58 -0600454 matched = get_matched_defconfig(line)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900455 if not matched:
Simon Glass793dca32019-10-31 07:42:57 -0600456 print("warning: %s:%d: no defconfig matched '%s'" % \
457 (defconfigs_file, i + 1, line), file=sys.stderr)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900458
459 defconfigs += matched
460
461 # use set() to drop multiple matching
462 return [ defconfig[len('configs') + 1:] for defconfig in set(defconfigs) ]
463
Masahiro Yamada684c3062016-07-25 19:15:28 +0900464def get_all_defconfigs():
465 """Get all the defconfig files under the configs/ directory."""
466 defconfigs = []
467 for (dirpath, dirnames, filenames) in os.walk('configs'):
468 dirpath = dirpath[len('configs') + 1:]
469 for filename in fnmatch.filter(filenames, '*_defconfig'):
470 defconfigs.append(os.path.join(dirpath, filename))
471
472 return defconfigs
473
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900474def color_text(color_enabled, color, string):
475 """Return colored string."""
476 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900477 # LF should not be surrounded by the escape sequence.
478 # Otherwise, additional whitespace or line-feed might be printed.
479 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
480 for s in string.split('\n') ])
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900481 else:
482 return string
483
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900484def show_diff(a, b, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900485 """Show unidified diff.
486
487 Arguments:
488 a: A list of lines (before)
489 b: A list of lines (after)
490 file_path: Path to the file
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900491 color_enabled: Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900492 """
493
494 diff = difflib.unified_diff(a, b,
495 fromfile=os.path.join('a', file_path),
496 tofile=os.path.join('b', file_path))
497
498 for line in diff:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900499 if line[0] == '-' and line[1] != '-':
Simon Glass793dca32019-10-31 07:42:57 -0600500 print(color_text(color_enabled, COLOR_RED, line), end=' ')
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900501 elif line[0] == '+' and line[1] != '+':
Simon Glass793dca32019-10-31 07:42:57 -0600502 print(color_text(color_enabled, COLOR_GREEN, line), end=' ')
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900503 else:
Simon Glass793dca32019-10-31 07:42:57 -0600504 print(line, end=' ')
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900505
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900506def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
507 extend_post):
508 """Extend matched lines if desired patterns are found before/after already
509 matched lines.
510
511 Arguments:
512 lines: A list of lines handled.
513 matched: A list of line numbers that have been already matched.
514 (will be updated by this function)
515 pre_patterns: A list of regular expression that should be matched as
516 preamble.
517 post_patterns: A list of regular expression that should be matched as
518 postamble.
519 extend_pre: Add the line number of matched preamble to the matched list.
520 extend_post: Add the line number of matched postamble to the matched list.
521 """
522 extended_matched = []
523
524 j = matched[0]
525
526 for i in matched:
527 if i == 0 or i < j:
528 continue
529 j = i
530 while j in matched:
531 j += 1
532 if j >= len(lines):
533 break
534
535 for p in pre_patterns:
536 if p.search(lines[i - 1]):
537 break
538 else:
539 # not matched
540 continue
541
542 for p in post_patterns:
543 if p.search(lines[j]):
544 break
545 else:
546 # not matched
547 continue
548
549 if extend_pre:
550 extended_matched.append(i - 1)
551 if extend_post:
552 extended_matched.append(j)
553
554 matched += extended_matched
555 matched.sort()
556
Chris Packham85edfc12017-05-02 21:30:46 +1200557def confirm(options, prompt):
558 if not options.yes:
559 while True:
Simon Glass793dca32019-10-31 07:42:57 -0600560 choice = input('{} [y/n]: '.format(prompt))
Chris Packham85edfc12017-05-02 21:30:46 +1200561 choice = choice.lower()
Simon Glass793dca32019-10-31 07:42:57 -0600562 print(choice)
Chris Packham85edfc12017-05-02 21:30:46 +1200563 if choice == 'y' or choice == 'n':
564 break
565
566 if choice == 'n':
567 return False
568
569 return True
570
Chris Packham4d9dbb12019-01-30 20:23:16 +1300571def cleanup_empty_blocks(header_path, options):
572 """Clean up empty conditional blocks
573
574 Arguments:
575 header_path: path to the cleaned file.
576 options: option flags.
577 """
578 pattern = re.compile(r'^\s*#\s*if.*$\n^\s*#\s*endif.*$\n*', flags=re.M)
579 with open(header_path) as f:
Simon Glass7570d9b2021-03-26 16:17:29 +1300580 try:
581 data = f.read()
582 except UnicodeDecodeError as e:
583 print("Failed on file %s': %s" % (header_path, e))
584 return
Chris Packham4d9dbb12019-01-30 20:23:16 +1300585
586 new_data = pattern.sub('\n', data)
587
588 show_diff(data.splitlines(True), new_data.splitlines(True), header_path,
589 options.color)
590
591 if options.dry_run:
592 return
593
594 with open(header_path, 'w') as f:
595 f.write(new_data)
596
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900597def cleanup_one_header(header_path, patterns, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900598 """Clean regex-matched lines away from a file.
599
600 Arguments:
601 header_path: path to the cleaned file.
602 patterns: list of regex patterns. Any lines matching to these
603 patterns are deleted.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900604 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900605 """
606 with open(header_path) as f:
Simon Glass7570d9b2021-03-26 16:17:29 +1300607 try:
608 lines = f.readlines()
609 except UnicodeDecodeError as e:
610 print("Failed on file %s': %s" % (header_path, e))
611 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900612
613 matched = []
614 for i, line in enumerate(lines):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900615 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
616 matched.append(i)
617 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900618 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900619 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900620 matched.append(i)
621 break
622
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900623 if not matched:
624 return
625
626 # remove empty #ifdef ... #endif, successive blank lines
627 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
628 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
629 pattern_endif = re.compile(r'#\s*endif\W') # #endif
630 pattern_blank = re.compile(r'^\s*$') # empty line
631
632 while True:
633 old_matched = copy.copy(matched)
634 extend_matched_lines(lines, matched, [pattern_if],
635 [pattern_endif], True, True)
636 extend_matched_lines(lines, matched, [pattern_elif],
637 [pattern_elif, pattern_endif], True, False)
638 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
639 [pattern_blank], False, True)
640 extend_matched_lines(lines, matched, [pattern_blank],
641 [pattern_elif, pattern_endif], True, False)
642 extend_matched_lines(lines, matched, [pattern_blank],
643 [pattern_blank], True, False)
644 if matched == old_matched:
645 break
646
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900647 tolines = copy.copy(lines)
648
649 for i in reversed(matched):
650 tolines.pop(i)
651
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900652 show_diff(lines, tolines, header_path, options.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900653
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900654 if options.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900655 return
656
657 with open(header_path, 'w') as f:
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900658 for line in tolines:
659 f.write(line)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900660
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900661def cleanup_headers(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900662 """Delete config defines from board headers.
663
664 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900665 configs: A list of CONFIGs to remove.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900666 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900667 """
Chris Packham85edfc12017-05-02 21:30:46 +1200668 if not confirm(options, 'Clean up headers?'):
669 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900670
671 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900672 for config in configs:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900673 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
674 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
675
Joe Hershberger60727f52015-05-19 13:21:21 -0500676 for dir in 'include', 'arch', 'board':
677 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900678 if dirpath == os.path.join('include', 'generated'):
679 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500680 for filename in filenames:
Simon Glassa38cc172020-08-11 11:23:34 -0600681 if not filename.endswith(('~', '.dts', '.dtsi', '.bin',
Trevor Woernerdc514d72021-03-15 12:01:33 -0400682 '.elf','.aml','.dat')):
Chris Packham4d9dbb12019-01-30 20:23:16 +1300683 header_path = os.path.join(dirpath, filename)
Tom Rini02b56702019-11-10 21:19:37 -0500684 # This file contains UTF-16 data and no CONFIG symbols
685 if header_path == 'include/video_font_data.h':
686 continue
Chris Packham4d9dbb12019-01-30 20:23:16 +1300687 cleanup_one_header(header_path, patterns, options)
688 cleanup_empty_blocks(header_path, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900689
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900690def cleanup_one_extra_option(defconfig_path, configs, options):
691 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
692
693 Arguments:
694 defconfig_path: path to the cleaned defconfig file.
695 configs: A list of CONFIGs to remove.
696 options: option flags.
697 """
698
699 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
700 end = '"\n'
701
702 with open(defconfig_path) as f:
703 lines = f.readlines()
704
705 for i, line in enumerate(lines):
706 if line.startswith(start) and line.endswith(end):
707 break
708 else:
709 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
710 return
711
712 old_tokens = line[len(start):-len(end)].split(',')
713 new_tokens = []
714
715 for token in old_tokens:
716 pos = token.find('=')
717 if not (token[:pos] if pos >= 0 else token) in configs:
718 new_tokens.append(token)
719
720 if new_tokens == old_tokens:
721 return
722
723 tolines = copy.copy(lines)
724
725 if new_tokens:
726 tolines[i] = start + ','.join(new_tokens) + end
727 else:
728 tolines.pop(i)
729
730 show_diff(lines, tolines, defconfig_path, options.color)
731
732 if options.dry_run:
733 return
734
735 with open(defconfig_path, 'w') as f:
736 for line in tolines:
737 f.write(line)
738
739def cleanup_extra_options(configs, options):
740 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
741
742 Arguments:
743 configs: A list of CONFIGs to remove.
744 options: option flags.
745 """
Chris Packham85edfc12017-05-02 21:30:46 +1200746 if not confirm(options, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
747 return
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900748
749 configs = [ config[len('CONFIG_'):] for config in configs ]
750
751 defconfigs = get_all_defconfigs()
752
753 for defconfig in defconfigs:
754 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
755 options)
756
Chris Packhamca438342017-05-02 21:30:47 +1200757def cleanup_whitelist(configs, options):
758 """Delete config whitelist entries
759
760 Arguments:
761 configs: A list of CONFIGs to remove.
762 options: option flags.
763 """
764 if not confirm(options, 'Clean up whitelist entries?'):
765 return
766
767 with open(os.path.join('scripts', 'config_whitelist.txt')) as f:
768 lines = f.readlines()
769
770 lines = [x for x in lines if x.strip() not in configs]
771
772 with open(os.path.join('scripts', 'config_whitelist.txt'), 'w') as f:
773 f.write(''.join(lines))
774
Chris Packhamf90df592017-05-02 21:30:48 +1200775def find_matching(patterns, line):
776 for pat in patterns:
777 if pat.search(line):
778 return True
779 return False
780
781def cleanup_readme(configs, options):
782 """Delete config description in README
783
784 Arguments:
785 configs: A list of CONFIGs to remove.
786 options: option flags.
787 """
788 if not confirm(options, 'Clean up README?'):
789 return
790
791 patterns = []
792 for config in configs:
793 patterns.append(re.compile(r'^\s+%s' % config))
794
795 with open('README') as f:
796 lines = f.readlines()
797
798 found = False
799 newlines = []
800 for line in lines:
801 if not found:
802 found = find_matching(patterns, line)
803 if found:
804 continue
805
806 if found and re.search(r'^\s+CONFIG', line):
807 found = False
808
809 if not found:
810 newlines.append(line)
811
812 with open('README', 'w') as f:
813 f.write(''.join(newlines))
814
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200815def try_expand(line):
816 """If value looks like an expression, try expanding it
817 Otherwise just return the existing value
818 """
819 if line.find('=') == -1:
820 return line
821
822 try:
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100823 aeval = asteval.Interpreter( usersyms=SIZES, minimal=True )
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200824 cfg, val = re.split("=", line)
825 val= val.strip('\"')
826 if re.search("[*+-/]|<<|SZ_+|\(([^\)]+)\)", val):
Markus Klotzbuecherb3192f42020-02-12 20:46:44 +0100827 newval = hex(aeval(val))
Simon Glass793dca32019-10-31 07:42:57 -0600828 print("\tExpanded expression %s to %s" % (val, newval))
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200829 return cfg+'='+newval
830 except:
Simon Glass793dca32019-10-31 07:42:57 -0600831 print("\tFailed to expand expression in %s" % line)
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200832
833 return line
834
Chris Packhamca438342017-05-02 21:30:47 +1200835
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900836### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900837class Progress:
838
839 """Progress Indicator"""
840
841 def __init__(self, total):
842 """Create a new progress indicator.
843
844 Arguments:
845 total: A number of defconfig files to process.
846 """
847 self.current = 0
848 self.total = total
849
850 def inc(self):
851 """Increment the number of processed defconfig files."""
852
853 self.current += 1
854
855 def show(self):
856 """Display the progress."""
Simon Glass793dca32019-10-31 07:42:57 -0600857 print(' %d defconfigs out of %d\r' % (self.current, self.total), end=' ')
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900858 sys.stdout.flush()
859
Simon Glasscb008832017-06-15 21:39:33 -0600860
861class KconfigScanner:
862 """Kconfig scanner."""
863
864 def __init__(self):
865 """Scan all the Kconfig files and create a Config object."""
866 # Define environment variables referenced from Kconfig
867 os.environ['srctree'] = os.getcwd()
868 os.environ['UBOOTVERSION'] = 'dummy'
869 os.environ['KCONFIG_OBJDIR'] = ''
Tom Rini65e05dd2019-09-20 17:42:09 -0400870 self.conf = kconfiglib.Kconfig()
Simon Glasscb008832017-06-15 21:39:33 -0600871
872
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900873class KconfigParser:
874
875 """A parser of .config and include/autoconf.mk."""
876
877 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
878 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
879
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900880 def __init__(self, configs, options, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900881 """Create a new parser.
882
883 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900884 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900885 options: option flags.
886 build_dir: Build directory.
887 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900888 self.configs = configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900889 self.options = options
Masahiro Yamada1f169922016-05-19 15:52:00 +0900890 self.dotconfig = os.path.join(build_dir, '.config')
891 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900892 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
893 'autoconf.mk')
Simon Glassf3b8e642017-06-01 19:39:01 -0600894 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900895 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900896
Simon Glass6821a742017-07-10 14:47:47 -0600897 def get_arch(self):
898 """Parse .config file and return the architecture.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900899
900 Returns:
Simon Glass6821a742017-07-10 14:47:47 -0600901 Architecture name (e.g. 'arm').
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900902 """
903 arch = ''
904 cpu = ''
Masahiro Yamada1f169922016-05-19 15:52:00 +0900905 for line in open(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900906 m = self.re_arch.match(line)
907 if m:
908 arch = m.group(1)
909 continue
910 m = self.re_cpu.match(line)
911 if m:
912 cpu = m.group(1)
913
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900914 if not arch:
915 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900916
917 # fix-up for aarch64
918 if arch == 'arm' and cpu == 'armv8':
919 arch = 'aarch64'
920
Simon Glass6821a742017-07-10 14:47:47 -0600921 return arch
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900922
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900923 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900924 """Parse .config, defconfig, include/autoconf.mk for one config.
925
926 This function looks for the config options in the lines from
927 defconfig, .config, and include/autoconf.mk in order to decide
928 which action should be taken for this defconfig.
929
930 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900931 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900932 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900933 autoconf_lines: lines from the include/autoconf.mk file.
934
935 Returns:
936 A tupple of the action for this defconfig and the line
937 matched for the config.
938 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900939 not_set = '# %s is not set' % config
940
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900941 for line in autoconf_lines:
942 line = line.rstrip()
943 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900944 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900945 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900946 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900947 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900948
Markus Klotzbuecherb237d352019-05-15 15:15:52 +0200949 new_val = try_expand(new_val)
950
Masahiro Yamada916224c2016-08-22 22:18:21 +0900951 for line in dotconfig_lines:
952 line = line.rstrip()
953 if line.startswith(config + '=') or line == not_set:
954 old_val = line
955 break
956 else:
957 if new_val == not_set:
958 return (ACTION_NO_ENTRY, config)
959 else:
960 return (ACTION_NO_ENTRY_WARN, config)
961
Masahiro Yamadacc008292016-05-19 15:51:56 +0900962 # If this CONFIG is neither bool nor trisate
963 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
964 # tools/scripts/define2mk.sed changes '1' to 'y'.
965 # This is a problem if the CONFIG is int type.
966 # Check the type in Kconfig and handle it correctly.
967 if new_val[-2:] == '=y':
968 new_val = new_val[:-1] + '1'
969
Masahiro Yamada50301592016-06-15 14:33:50 +0900970 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
971 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900972
Masahiro Yamada1d085562016-05-19 15:52:02 +0900973 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900974 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900975
Masahiro Yamadacc008292016-05-19 15:51:56 +0900976 This function parses the generated .config and include/autoconf.mk
977 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900978 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900979
980 Arguments:
981 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900982
983 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900984 Return a tuple of (updated flag, log string).
985 The "updated flag" is True if the .config was updated, False
986 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900987 """
988
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900989 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900990 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900991 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900992 rm_files = [self.config_autoconf, self.autoconf]
993
994 if self.options.spl:
995 if os.path.exists(self.spl_autoconf):
996 autoconf_path = self.spl_autoconf
997 rm_files.append(self.spl_autoconf)
998 else:
999 for f in rm_files:
1000 os.remove(f)
1001 return (updated, suspicious,
1002 color_text(self.options.color, COLOR_BROWN,
1003 "SPL is not enabled. Skipped.") + '\n')
1004 else:
1005 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001006
Masahiro Yamada1f169922016-05-19 15:52:00 +09001007 with open(self.dotconfig) as f:
Masahiro Yamadacc008292016-05-19 15:51:56 +09001008 dotconfig_lines = f.readlines()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001009
Masahiro Yamada07913d12016-08-22 22:18:22 +09001010 with open(autoconf_path) as f:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001011 autoconf_lines = f.readlines()
1012
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001013 for config in self.configs:
1014 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -05001015 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001016 results.append(result)
1017
1018 log = ''
1019
1020 for (action, value) in results:
1021 if action == ACTION_MOVE:
1022 actlog = "Move '%s'" % value
1023 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +09001024 elif action == ACTION_NO_ENTRY:
1025 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001026 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +09001027 elif action == ACTION_NO_ENTRY_WARN:
1028 actlog = "%s is not defined in Kconfig (suspicious). Do nothing." % value
1029 log_color = COLOR_YELLOW
1030 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +09001031 elif action == ACTION_NO_CHANGE:
1032 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
1033 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001034 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada07913d12016-08-22 22:18:22 +09001035 elif action == ACTION_SPL_NOT_EXIST:
1036 actlog = "SPL is not enabled for this defconfig. Skip."
1037 log_color = COLOR_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001038 else:
1039 sys.exit("Internal Error. This should not happen.")
1040
Masahiro Yamada1d085562016-05-19 15:52:02 +09001041 log += color_text(self.options.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001042
Masahiro Yamada1f169922016-05-19 15:52:00 +09001043 with open(self.dotconfig, 'a') as f:
Masahiro Yamadae423d172016-05-19 15:51:49 +09001044 for (action, value) in results:
1045 if action == ACTION_MOVE:
1046 f.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +09001047 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001048
Masahiro Yamada5da4f852016-05-19 15:52:06 +09001049 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +09001050 for f in rm_files:
1051 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001052
Masahiro Yamada916224c2016-08-22 22:18:21 +09001053 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +09001054
Masahiro Yamada5da4f852016-05-19 15:52:06 +09001055 def check_defconfig(self):
1056 """Check the defconfig after savedefconfig
1057
1058 Returns:
1059 Return additional log if moved CONFIGs were removed again by
1060 'make savedefconfig'.
1061 """
1062
1063 log = ''
1064
1065 with open(self.defconfig) as f:
1066 defconfig_lines = f.readlines()
1067
1068 for (action, value) in self.results:
1069 if action != ACTION_MOVE:
1070 continue
1071 if not value + '\n' in defconfig_lines:
1072 log += color_text(self.options.color, COLOR_YELLOW,
1073 "'%s' was removed by savedefconfig.\n" %
1074 value)
1075
1076 return log
1077
Simon Glassd73fcb12017-06-01 19:39:02 -06001078
1079class DatabaseThread(threading.Thread):
1080 """This thread processes results from Slot threads.
1081
1082 It collects the data in the master config directary. There is only one
1083 result thread, and this helps to serialise the build output.
1084 """
1085 def __init__(self, config_db, db_queue):
1086 """Set up a new result thread
1087
1088 Args:
1089 builder: Builder which will be sent each result
1090 """
1091 threading.Thread.__init__(self)
1092 self.config_db = config_db
1093 self.db_queue= db_queue
1094
1095 def run(self):
1096 """Called to start up the result thread.
1097
1098 We collect the next result job and pass it on to the build.
1099 """
1100 while True:
1101 defconfig, configs = self.db_queue.get()
1102 self.config_db[defconfig] = configs
1103 self.db_queue.task_done()
1104
1105
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001106class Slot:
1107
1108 """A slot to store a subprocess.
1109
1110 Each instance of this class handles one subprocess.
1111 This class is useful to control multiple threads
1112 for faster processing.
1113 """
1114
Simon Glass6821a742017-07-10 14:47:47 -06001115 def __init__(self, toolchains, configs, options, progress, devnull,
1116 make_cmd, reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001117 """Create a new process slot.
1118
1119 Arguments:
Simon Glass6821a742017-07-10 14:47:47 -06001120 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001121 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001122 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001123 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001124 devnull: A file object of '/dev/null'.
1125 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001126 reference_src_dir: Determine the true starting config state from this
1127 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -06001128 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001129 """
Simon Glass6821a742017-07-10 14:47:47 -06001130 self.toolchains = toolchains
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001131 self.options = options
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001132 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001133 self.build_dir = tempfile.mkdtemp()
1134 self.devnull = devnull
1135 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001136 self.reference_src_dir = reference_src_dir
Simon Glassd73fcb12017-06-01 19:39:02 -06001137 self.db_queue = db_queue
Masahiro Yamada522e8dc2016-05-19 15:52:01 +09001138 self.parser = KconfigParser(configs, options, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001139 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001140 self.failed_boards = set()
1141 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001142
1143 def __del__(self):
1144 """Delete the working directory
1145
1146 This function makes sure the temporary directory is cleaned away
1147 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -05001148 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001149 instance of the class gets unreferenced.
1150
1151 If the subprocess is still running, wait until it finishes.
1152 """
1153 if self.state != STATE_IDLE:
1154 while self.ps.poll() == None:
1155 pass
1156 shutil.rmtree(self.build_dir)
1157
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001158 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001159 """Assign a new subprocess for defconfig and add it to the slot.
1160
1161 If the slot is vacant, create a new subprocess for processing the
1162 given defconfig and add it to the slot. Just returns False if
1163 the slot is occupied (i.e. the current subprocess is still running).
1164
1165 Arguments:
1166 defconfig: defconfig name.
1167
1168 Returns:
1169 Return True on success or False on failure
1170 """
1171 if self.state != STATE_IDLE:
1172 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001173
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001174 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +09001175 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001176 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001177 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001178 return True
1179
1180 def poll(self):
1181 """Check the status of the subprocess and handle it as needed.
1182
1183 Returns True if the slot is vacant (i.e. in idle state).
1184 If the configuration is successfully finished, assign a new
1185 subprocess to build include/autoconf.mk.
1186 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +09001187 parse the .config and the include/autoconf.mk, moving
1188 config options to the .config as needed.
1189 If the .config was updated, run "make savedefconfig" to sync
1190 it, update the original defconfig, and then set the slot back
1191 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001192
1193 Returns:
1194 Return True if the subprocess is terminated, False otherwise
1195 """
1196 if self.state == STATE_IDLE:
1197 return True
1198
1199 if self.ps.poll() == None:
1200 return False
1201
1202 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001203 self.handle_error()
1204 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001205 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001206 self.do_savedefconfig()
1207 else:
1208 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001209 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001210 if self.current_src_dir:
1211 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001212 self.do_defconfig()
Simon Glassd73fcb12017-06-01 19:39:02 -06001213 elif self.options.build_db:
1214 self.do_build_db()
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001215 else:
1216 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001217 elif self.state == STATE_SAVEDEFCONFIG:
1218 self.update_defconfig()
1219 else:
1220 sys.exit("Internal Error. This should not happen.")
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001221
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001222 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -05001223
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001224 def handle_error(self):
1225 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001226
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001227 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
1228 "Failed to process.\n")
1229 if self.options.verbose:
1230 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
Markus Klotzbuecher4f5c5e92020-02-12 20:46:45 +01001231 self.ps.stderr.read().decode())
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001232 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -05001233
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001234 def do_defconfig(self):
1235 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +09001236
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001237 cmd = list(self.make_cmd)
1238 cmd.append(self.defconfig)
1239 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001240 stderr=subprocess.PIPE,
1241 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001242 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +09001243
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001244 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -06001245 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001246
Simon Glass6821a742017-07-10 14:47:47 -06001247 arch = self.parser.get_arch()
1248 try:
1249 toolchain = self.toolchains.Select(arch)
1250 except ValueError:
Masahiro Yamada1d085562016-05-19 15:52:02 +09001251 self.log += color_text(self.options.color, COLOR_YELLOW,
Chris Packhamce3ba452017-08-27 20:00:51 +12001252 "Tool chain for '%s' is missing. Do nothing.\n" % arch)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001253 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001254 return
Simon Glass793dca32019-10-31 07:42:57 -06001255 env = toolchain.MakeEnvironment(False)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +09001256
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001257 cmd = list(self.make_cmd)
Joe Hershberger7740f652015-05-19 13:21:18 -05001258 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -06001259 cmd.append(AUTO_CONF_PATH)
Simon Glass6821a742017-07-10 14:47:47 -06001260 self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001261 stderr=subprocess.PIPE,
1262 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001263 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001264
Simon Glassd73fcb12017-06-01 19:39:02 -06001265 def do_build_db(self):
1266 """Add the board to the database"""
1267 configs = {}
1268 with open(os.path.join(self.build_dir, AUTO_CONF_PATH)) as fd:
1269 for line in fd.readlines():
1270 if line.startswith('CONFIG'):
1271 config, value = line.split('=', 1)
1272 configs[config] = value.rstrip()
1273 self.db_queue.put([self.defconfig, configs])
1274 self.finish(True)
1275
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001276 def do_savedefconfig(self):
1277 """Update the .config and run 'make savedefconfig'."""
1278
Masahiro Yamada916224c2016-08-22 22:18:21 +09001279 (updated, suspicious, log) = self.parser.update_dotconfig()
1280 if suspicious:
1281 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001282 self.log += log
1283
1284 if not self.options.force_sync and not updated:
1285 self.finish(True)
1286 return
1287 if updated:
1288 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
1289 "Syncing by savedefconfig...\n")
1290 else:
1291 self.log += "Syncing by savedefconfig (forced by option)...\n"
1292
1293 cmd = list(self.make_cmd)
1294 cmd.append('savedefconfig')
1295 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1296 stderr=subprocess.PIPE)
1297 self.state = STATE_SAVEDEFCONFIG
1298
1299 def update_defconfig(self):
1300 """Update the input defconfig and go back to the idle state."""
1301
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001302 log = self.parser.check_defconfig()
1303 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001304 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001305 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001306 orig_defconfig = os.path.join('configs', self.defconfig)
1307 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1308 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1309
1310 if updated:
Joe Hershberger06cc1d32016-06-10 14:53:30 -05001311 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001312 "defconfig was updated.\n")
1313
1314 if not self.options.dry_run and updated:
1315 shutil.move(new_defconfig, orig_defconfig)
1316 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001317
Masahiro Yamada4efef992016-05-19 15:52:03 +09001318 def finish(self, success):
1319 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001320
1321 Arguments:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001322 success: Should be True when the defconfig was processed
1323 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001324 """
1325 # output at least 30 characters to hide the "* defconfigs out of *".
1326 log = self.defconfig.ljust(30) + '\n'
1327
1328 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1329 # Some threads are running in parallel.
1330 # Print log atomically to not mix up logs from different threads.
Simon Glass793dca32019-10-31 07:42:57 -06001331 print(log, file=(sys.stdout if success else sys.stderr))
Masahiro Yamada4efef992016-05-19 15:52:03 +09001332
1333 if not success:
1334 if self.options.exit_on_error:
1335 sys.exit("Exit on error.")
1336 # If --exit-on-error flag is not set, skip this board and continue.
1337 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001338 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001339
Masahiro Yamada1d085562016-05-19 15:52:02 +09001340 self.progress.inc()
1341 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001342 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001343
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001344 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001345 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001346 """
1347 return self.failed_boards
1348
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001349 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001350 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001351 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001352 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001353
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001354class Slots:
1355
1356 """Controller of the array of subprocess slots."""
1357
Simon Glass6821a742017-07-10 14:47:47 -06001358 def __init__(self, toolchains, configs, options, progress,
1359 reference_src_dir, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001360 """Create a new slots controller.
1361
1362 Arguments:
Simon Glass6821a742017-07-10 14:47:47 -06001363 toolchains: Toolchains object containing toolchains.
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001364 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001365 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001366 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001367 reference_src_dir: Determine the true starting config state from this
1368 source tree.
Simon Glassd73fcb12017-06-01 19:39:02 -06001369 db_queue: output queue to write config info for the database
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001370 """
1371 self.options = options
1372 self.slots = []
1373 devnull = get_devnull()
1374 make_cmd = get_make_cmd()
1375 for i in range(options.jobs):
Simon Glass6821a742017-07-10 14:47:47 -06001376 self.slots.append(Slot(toolchains, configs, options, progress,
1377 devnull, make_cmd, reference_src_dir,
1378 db_queue))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001379
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001380 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001381 """Add a new subprocess if a vacant slot is found.
1382
1383 Arguments:
1384 defconfig: defconfig name to be put into.
1385
1386 Returns:
1387 Return True on success or False on failure
1388 """
1389 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001390 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001391 return True
1392 return False
1393
1394 def available(self):
1395 """Check if there is a vacant slot.
1396
1397 Returns:
1398 Return True if at lease one vacant slot is found, False otherwise.
1399 """
1400 for slot in self.slots:
1401 if slot.poll():
1402 return True
1403 return False
1404
1405 def empty(self):
1406 """Check if all slots are vacant.
1407
1408 Returns:
1409 Return True if all the slots are vacant, False otherwise.
1410 """
1411 ret = True
1412 for slot in self.slots:
1413 if not slot.poll():
1414 ret = False
1415 return ret
1416
1417 def show_failed_boards(self):
1418 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001419 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001420 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001421
1422 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001423 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001424
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001425 if boards:
1426 boards = '\n'.join(boards) + '\n'
1427 msg = "The following boards were not processed due to error:\n"
1428 msg += boards
1429 msg += "(the list has been saved in %s)\n" % output_file
Simon Glass793dca32019-10-31 07:42:57 -06001430 print(color_text(self.options.color, COLOR_LIGHT_RED,
1431 msg), file=sys.stderr)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001432
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001433 with open(output_file, 'w') as f:
1434 f.write(boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001435
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001436 def show_suspicious_boards(self):
1437 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001438 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001439 output_file = 'moveconfig.suspicious'
1440
1441 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001442 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001443
1444 if boards:
1445 boards = '\n'.join(boards) + '\n'
1446 msg = "The following boards might have been converted incorrectly.\n"
1447 msg += "It is highly recommended to check them manually:\n"
1448 msg += boards
1449 msg += "(the list has been saved in %s)\n" % output_file
Simon Glass793dca32019-10-31 07:42:57 -06001450 print(color_text(self.options.color, COLOR_YELLOW,
1451 msg), file=sys.stderr)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001452
1453 with open(output_file, 'w') as f:
1454 f.write(boards)
1455
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001456class ReferenceSource:
1457
1458 """Reference source against which original configs should be parsed."""
1459
1460 def __init__(self, commit):
1461 """Create a reference source directory based on a specified commit.
1462
1463 Arguments:
1464 commit: commit to git-clone
1465 """
1466 self.src_dir = tempfile.mkdtemp()
Simon Glass793dca32019-10-31 07:42:57 -06001467 print("Cloning git repo to a separate work directory...")
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001468 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1469 cwd=self.src_dir)
Simon Glass793dca32019-10-31 07:42:57 -06001470 print("Checkout '%s' to build the original autoconf.mk." % \
1471 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip())
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001472 subprocess.check_output(['git', 'checkout', commit],
1473 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001474
1475 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001476 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001477
1478 This function makes sure the temporary directory is cleaned away
1479 even if Python suddenly dies due to error. It should be done in here
1480 because it is guaranteed the destructor is always invoked when the
1481 instance of the class gets unreferenced.
1482 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001483 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001484
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001485 def get_dir(self):
1486 """Return the absolute path to the reference source directory."""
1487
1488 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001489
Simon Glass6821a742017-07-10 14:47:47 -06001490def move_config(toolchains, configs, options, db_queue):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001491 """Move config options to defconfig files.
1492
1493 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001494 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001495 options: option flags
1496 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001497 if len(configs) == 0:
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001498 if options.force_sync:
Simon Glass793dca32019-10-31 07:42:57 -06001499 print('No CONFIG is specified. You are probably syncing defconfigs.', end=' ')
Simon Glassd73fcb12017-06-01 19:39:02 -06001500 elif options.build_db:
Simon Glass793dca32019-10-31 07:42:57 -06001501 print('Building %s database' % CONFIG_DATABASE)
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001502 else:
Simon Glass793dca32019-10-31 07:42:57 -06001503 print('Neither CONFIG nor --force-sync is specified. Nothing will happen.', end=' ')
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001504 else:
Simon Glass793dca32019-10-31 07:42:57 -06001505 print('Move ' + ', '.join(configs), end=' ')
1506 print('(jobs: %d)\n' % options.jobs)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001507
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001508 if options.git_ref:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001509 reference_src = ReferenceSource(options.git_ref)
1510 reference_src_dir = reference_src.get_dir()
1511 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001512 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001513
Joe Hershberger91040e82015-05-19 13:21:19 -05001514 if options.defconfigs:
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +09001515 defconfigs = get_matched_defconfigs(options.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001516 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001517 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001518
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001519 progress = Progress(len(defconfigs))
Simon Glass6821a742017-07-10 14:47:47 -06001520 slots = Slots(toolchains, configs, options, progress, reference_src_dir,
1521 db_queue)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001522
1523 # Main loop to process defconfig files:
1524 # Add a new subprocess into a vacant slot.
1525 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001526 for defconfig in defconfigs:
1527 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001528 while not slots.available():
1529 # No available slot: sleep for a while
1530 time.sleep(SLEEP_TIME)
1531
1532 # wait until all the subprocesses finish
1533 while not slots.empty():
1534 time.sleep(SLEEP_TIME)
1535
Simon Glass793dca32019-10-31 07:42:57 -06001536 print('')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001537 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001538 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001539
Simon Glasscb008832017-06-15 21:39:33 -06001540def find_kconfig_rules(kconf, config, imply_config):
1541 """Check whether a config has a 'select' or 'imply' keyword
1542
1543 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001544 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001545 config: Name of config to check (without CONFIG_ prefix)
1546 imply_config: Implying config (without CONFIG_ prefix) which may or
1547 may not have an 'imply' for 'config')
1548
1549 Returns:
1550 Symbol object for 'config' if found, else None
1551 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001552 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001553 if sym:
Ulf Magnusson4e1102f2017-09-19 12:52:55 +02001554 for sel in sym.get_selected_symbols() | sym.get_implied_symbols():
Simon Glasscb008832017-06-15 21:39:33 -06001555 if sel.get_name() == config:
1556 return sym
1557 return None
1558
1559def check_imply_rule(kconf, config, imply_config):
1560 """Check if we can add an 'imply' option
1561
1562 This finds imply_config in the Kconfig and looks to see if it is possible
1563 to add an 'imply' for 'config' to that part of the Kconfig.
1564
1565 Args:
Tom Rini65e05dd2019-09-20 17:42:09 -04001566 kconf: Kconfiglib.Kconfig object
Simon Glasscb008832017-06-15 21:39:33 -06001567 config: Name of config to check (without CONFIG_ prefix)
1568 imply_config: Implying config (without CONFIG_ prefix) which may or
1569 may not have an 'imply' for 'config')
1570
1571 Returns:
1572 tuple:
1573 filename of Kconfig file containing imply_config, or None if none
1574 line number within the Kconfig file, or 0 if none
1575 message indicating the result
1576 """
Tom Rini65e05dd2019-09-20 17:42:09 -04001577 sym = kconf.syms.get(imply_config)
Simon Glasscb008832017-06-15 21:39:33 -06001578 if not sym:
1579 return 'cannot find sym'
1580 locs = sym.get_def_locations()
1581 if len(locs) != 1:
1582 return '%d locations' % len(locs)
1583 fname, linenum = locs[0]
1584 cwd = os.getcwd()
1585 if cwd and fname.startswith(cwd):
1586 fname = fname[len(cwd) + 1:]
1587 file_line = ' at %s:%d' % (fname, linenum)
1588 with open(fname) as fd:
1589 data = fd.read().splitlines()
1590 if data[linenum - 1] != 'config %s' % imply_config:
1591 return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1592 return fname, linenum, 'adding%s' % file_line
1593
1594def add_imply_rule(config, fname, linenum):
1595 """Add a new 'imply' option to a Kconfig
1596
1597 Args:
1598 config: config option to add an imply for (without CONFIG_ prefix)
1599 fname: Kconfig filename to update
1600 linenum: Line number to place the 'imply' before
1601
1602 Returns:
1603 Message indicating the result
1604 """
1605 file_line = ' at %s:%d' % (fname, linenum)
1606 data = open(fname).read().splitlines()
1607 linenum -= 1
1608
1609 for offset, line in enumerate(data[linenum:]):
1610 if line.strip().startswith('help') or not line:
1611 data.insert(linenum + offset, '\timply %s' % config)
1612 with open(fname, 'w') as fd:
1613 fd.write('\n'.join(data) + '\n')
1614 return 'added%s' % file_line
1615
1616 return 'could not insert%s'
1617
1618(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1619 1, 2, 4, 8)
Simon Glass9b2a2e82017-06-15 21:39:32 -06001620
1621IMPLY_FLAGS = {
1622 'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1623 'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1624 'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
Simon Glasscb008832017-06-15 21:39:33 -06001625 'non-arch-board': [
1626 IMPLY_NON_ARCH_BOARD,
1627 'Allow Kconfig options outside arch/ and /board/ to imply'],
Simon Glass9b2a2e82017-06-15 21:39:32 -06001628};
1629
Simon Glasscb008832017-06-15 21:39:33 -06001630def do_imply_config(config_list, add_imply, imply_flags, skip_added,
1631 check_kconfig=True, find_superset=False):
Simon Glass99b66602017-06-01 19:39:03 -06001632 """Find CONFIG options which imply those in the list
1633
1634 Some CONFIG options can be implied by others and this can help to reduce
1635 the size of the defconfig files. For example, CONFIG_X86 implies
1636 CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1637 all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1638 each of the x86 defconfig files.
1639
1640 This function uses the moveconfig database to find such options. It
1641 displays a list of things that could possibly imply those in the list.
1642 The algorithm ignores any that start with CONFIG_TARGET since these
1643 typically refer to only a few defconfigs (often one). It also does not
1644 display a config with less than 5 defconfigs.
1645
1646 The algorithm works using sets. For each target config in config_list:
1647 - Get the set 'defconfigs' which use that target config
1648 - For each config (from a list of all configs):
1649 - Get the set 'imply_defconfig' of defconfigs which use that config
1650 -
1651 - If imply_defconfigs contains anything not in defconfigs then
1652 this config does not imply the target config
1653
1654 Params:
1655 config_list: List of CONFIG options to check (each a string)
Simon Glasscb008832017-06-15 21:39:33 -06001656 add_imply: Automatically add an 'imply' for each config.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001657 imply_flags: Flags which control which implying configs are allowed
1658 (IMPLY_...)
Simon Glasscb008832017-06-15 21:39:33 -06001659 skip_added: Don't show options which already have an imply added.
1660 check_kconfig: Check if implied symbols already have an 'imply' or
1661 'select' for the target config, and show this information if so.
Simon Glass99b66602017-06-01 19:39:03 -06001662 find_superset: True to look for configs which are a superset of those
1663 already found. So for example if CONFIG_EXYNOS5 implies an option,
1664 but CONFIG_EXYNOS covers a larger set of defconfigs and also
1665 implies that option, this will drop the former in favour of the
1666 latter. In practice this option has not proved very used.
1667
1668 Note the terminoloy:
1669 config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1670 defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1671 """
Simon Glasscb008832017-06-15 21:39:33 -06001672 kconf = KconfigScanner().conf if check_kconfig else None
1673 if add_imply and add_imply != 'all':
1674 add_imply = add_imply.split()
1675
Simon Glass99b66602017-06-01 19:39:03 -06001676 # key is defconfig name, value is dict of (CONFIG_xxx, value)
1677 config_db = {}
1678
1679 # Holds a dict containing the set of defconfigs that contain each config
1680 # key is config, value is set of defconfigs using that config
1681 defconfig_db = collections.defaultdict(set)
1682
1683 # Set of all config options we have seen
1684 all_configs = set()
1685
1686 # Set of all defconfigs we have seen
1687 all_defconfigs = set()
1688
1689 # Read in the database
1690 configs = {}
1691 with open(CONFIG_DATABASE) as fd:
1692 for line in fd.readlines():
1693 line = line.rstrip()
1694 if not line: # Separator between defconfigs
1695 config_db[defconfig] = configs
1696 all_defconfigs.add(defconfig)
1697 configs = {}
1698 elif line[0] == ' ': # CONFIG line
1699 config, value = line.strip().split('=', 1)
1700 configs[config] = value
1701 defconfig_db[config].add(defconfig)
1702 all_configs.add(config)
1703 else: # New defconfig
1704 defconfig = line
1705
1706 # Work through each target config option in tern, independently
1707 for config in config_list:
1708 defconfigs = defconfig_db.get(config)
1709 if not defconfigs:
Simon Glass793dca32019-10-31 07:42:57 -06001710 print('%s not found in any defconfig' % config)
Simon Glass99b66602017-06-01 19:39:03 -06001711 continue
1712
1713 # Get the set of defconfigs without this one (since a config cannot
1714 # imply itself)
1715 non_defconfigs = all_defconfigs - defconfigs
1716 num_defconfigs = len(defconfigs)
Simon Glass793dca32019-10-31 07:42:57 -06001717 print('%s found in %d/%d defconfigs' % (config, num_defconfigs,
1718 len(all_configs)))
Simon Glass99b66602017-06-01 19:39:03 -06001719
1720 # This will hold the results: key=config, value=defconfigs containing it
1721 imply_configs = {}
1722 rest_configs = all_configs - set([config])
1723
1724 # Look at every possible config, except the target one
1725 for imply_config in rest_configs:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001726 if 'ERRATUM' in imply_config:
Simon Glass99b66602017-06-01 19:39:03 -06001727 continue
Simon Glass9b2a2e82017-06-15 21:39:32 -06001728 if not (imply_flags & IMPLY_CMD):
1729 if 'CONFIG_CMD' in imply_config:
1730 continue
1731 if not (imply_flags & IMPLY_TARGET):
1732 if 'CONFIG_TARGET' in imply_config:
1733 continue
Simon Glass99b66602017-06-01 19:39:03 -06001734
1735 # Find set of defconfigs that have this config
1736 imply_defconfig = defconfig_db[imply_config]
1737
1738 # Get the intersection of this with defconfigs containing the
1739 # target config
1740 common_defconfigs = imply_defconfig & defconfigs
1741
1742 # Get the set of defconfigs containing this config which DO NOT
1743 # also contain the taret config. If this set is non-empty it means
1744 # that this config affects other defconfigs as well as (possibly)
1745 # the ones affected by the target config. This means it implies
1746 # things we don't want to imply.
1747 not_common_defconfigs = imply_defconfig & non_defconfigs
1748 if not_common_defconfigs:
1749 continue
1750
1751 # If there are common defconfigs, imply_config may be useful
1752 if common_defconfigs:
1753 skip = False
1754 if find_superset:
Simon Glass793dca32019-10-31 07:42:57 -06001755 for prev in list(imply_configs.keys()):
Simon Glass99b66602017-06-01 19:39:03 -06001756 prev_count = len(imply_configs[prev])
1757 count = len(common_defconfigs)
1758 if (prev_count > count and
1759 (imply_configs[prev] & common_defconfigs ==
1760 common_defconfigs)):
1761 # skip imply_config because prev is a superset
1762 skip = True
1763 break
1764 elif count > prev_count:
1765 # delete prev because imply_config is a superset
1766 del imply_configs[prev]
1767 if not skip:
1768 imply_configs[imply_config] = common_defconfigs
1769
1770 # Now we have a dict imply_configs of configs which imply each config
1771 # The value of each dict item is the set of defconfigs containing that
1772 # config. Rank them so that we print the configs that imply the largest
1773 # number of defconfigs first.
Simon Glasscb008832017-06-15 21:39:33 -06001774 ranked_iconfigs = sorted(imply_configs,
Simon Glass99b66602017-06-01 19:39:03 -06001775 key=lambda k: len(imply_configs[k]), reverse=True)
Simon Glasscb008832017-06-15 21:39:33 -06001776 kconfig_info = ''
1777 cwd = os.getcwd()
1778 add_list = collections.defaultdict(list)
1779 for iconfig in ranked_iconfigs:
1780 num_common = len(imply_configs[iconfig])
Simon Glass99b66602017-06-01 19:39:03 -06001781
1782 # Don't bother if there are less than 5 defconfigs affected.
Simon Glass9b2a2e82017-06-15 21:39:32 -06001783 if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
Simon Glass99b66602017-06-01 19:39:03 -06001784 continue
Simon Glasscb008832017-06-15 21:39:33 -06001785 missing = defconfigs - imply_configs[iconfig]
Simon Glass99b66602017-06-01 19:39:03 -06001786 missing_str = ', '.join(missing) if missing else 'all'
1787 missing_str = ''
Simon Glasscb008832017-06-15 21:39:33 -06001788 show = True
1789 if kconf:
1790 sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1791 iconfig[CONFIG_LEN:])
1792 kconfig_info = ''
1793 if sym:
1794 locs = sym.get_def_locations()
1795 if len(locs) == 1:
1796 fname, linenum = locs[0]
1797 if cwd and fname.startswith(cwd):
1798 fname = fname[len(cwd) + 1:]
1799 kconfig_info = '%s:%d' % (fname, linenum)
1800 if skip_added:
1801 show = False
1802 else:
Tom Rini65e05dd2019-09-20 17:42:09 -04001803 sym = kconf.syms.get(iconfig[CONFIG_LEN:])
Simon Glasscb008832017-06-15 21:39:33 -06001804 fname = ''
1805 if sym:
1806 locs = sym.get_def_locations()
1807 if len(locs) == 1:
1808 fname, linenum = locs[0]
1809 if cwd and fname.startswith(cwd):
1810 fname = fname[len(cwd) + 1:]
1811 in_arch_board = not sym or (fname.startswith('arch') or
1812 fname.startswith('board'))
1813 if (not in_arch_board and
1814 not (imply_flags & IMPLY_NON_ARCH_BOARD)):
1815 continue
1816
1817 if add_imply and (add_imply == 'all' or
1818 iconfig in add_imply):
1819 fname, linenum, kconfig_info = (check_imply_rule(kconf,
1820 config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1821 if fname:
1822 add_list[fname].append(linenum)
1823
1824 if show and kconfig_info != 'skip':
Simon Glass793dca32019-10-31 07:42:57 -06001825 print('%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1826 kconfig_info, missing_str))
Simon Glasscb008832017-06-15 21:39:33 -06001827
1828 # Having collected a list of things to add, now we add them. We process
1829 # each file from the largest line number to the smallest so that
1830 # earlier additions do not affect our line numbers. E.g. if we added an
1831 # imply at line 20 it would change the position of each line after
1832 # that.
Simon Glass793dca32019-10-31 07:42:57 -06001833 for fname, linenums in add_list.items():
Simon Glasscb008832017-06-15 21:39:33 -06001834 for linenum in sorted(linenums, reverse=True):
1835 add_imply_rule(config[CONFIG_LEN:], fname, linenum)
Simon Glass99b66602017-06-01 19:39:03 -06001836
1837
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001838def main():
1839 try:
1840 cpu_count = multiprocessing.cpu_count()
1841 except NotImplementedError:
1842 cpu_count = 1
1843
1844 parser = optparse.OptionParser()
1845 # Add options here
Simon Glasscb008832017-06-15 21:39:33 -06001846 parser.add_option('-a', '--add-imply', type='string', default='',
1847 help='comma-separated list of CONFIG options to add '
1848 "an 'imply' statement to for the CONFIG in -i")
1849 parser.add_option('-A', '--skip-added', action='store_true', default=False,
1850 help="don't show options which are already marked as "
1851 'implying others')
Simon Glassd73fcb12017-06-01 19:39:02 -06001852 parser.add_option('-b', '--build-db', action='store_true', default=False,
1853 help='build a CONFIG database')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001854 parser.add_option('-c', '--color', action='store_true', default=False,
1855 help='display the log in color')
Simon Glass9ede2122016-09-12 23:18:21 -06001856 parser.add_option('-C', '--commit', action='store_true', default=False,
1857 help='Create a git commit for the operation')
Joe Hershberger91040e82015-05-19 13:21:19 -05001858 parser.add_option('-d', '--defconfigs', type='string',
Simon Glassee4e61b2017-06-01 19:38:59 -06001859 help='a file containing a list of defconfigs to move, '
1860 "one per line (for example 'snow_defconfig') "
1861 "or '-' to read from stdin")
Simon Glass99b66602017-06-01 19:39:03 -06001862 parser.add_option('-i', '--imply', action='store_true', default=False,
1863 help='find options which imply others')
Simon Glass9b2a2e82017-06-15 21:39:32 -06001864 parser.add_option('-I', '--imply-flags', type='string', default='',
1865 help="control the -i option ('help' for help")
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001866 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1867 help='perform a trial run (show log with no changes)')
1868 parser.add_option('-e', '--exit-on-error', action='store_true',
1869 default=False,
1870 help='exit immediately on any error')
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001871 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1872 help='force sync by savedefconfig')
Masahiro Yamada07913d12016-08-22 22:18:22 +09001873 parser.add_option('-S', '--spl', action='store_true', default=False,
1874 help='parse config options defined for SPL build')
Joe Hershberger2144f882015-05-19 13:21:20 -05001875 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1876 action='store_true', default=False,
1877 help='only cleanup the headers')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001878 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1879 help='the number of jobs to run simultaneously')
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001880 parser.add_option('-r', '--git-ref', type='string',
1881 help='the git ref to clone for building the autoconf.mk')
Simon Glass6b403df2016-09-12 23:18:20 -06001882 parser.add_option('-y', '--yes', action='store_true', default=False,
1883 help="respond 'yes' to any prompts")
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001884 parser.add_option('-v', '--verbose', action='store_true', default=False,
1885 help='show any build errors as boards are built')
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001886 parser.usage += ' CONFIG ...'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001887
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001888 (options, configs) = parser.parse_args()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001889
Simon Glass99b66602017-06-01 19:39:03 -06001890 if len(configs) == 0 and not any((options.force_sync, options.build_db,
1891 options.imply)):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001892 parser.print_usage()
1893 sys.exit(1)
1894
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001895 # prefix the option name with CONFIG_ if missing
1896 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1897 for config in configs ]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001898
Joe Hershberger2144f882015-05-19 13:21:20 -05001899 check_top_directory()
1900
Simon Glass99b66602017-06-01 19:39:03 -06001901 if options.imply:
Simon Glass9b2a2e82017-06-15 21:39:32 -06001902 imply_flags = 0
Simon Glassdee36c72017-07-10 14:47:46 -06001903 if options.imply_flags == 'all':
1904 imply_flags = -1
1905
1906 elif options.imply_flags:
1907 for flag in options.imply_flags.split(','):
1908 bad = flag not in IMPLY_FLAGS
1909 if bad:
Simon Glass793dca32019-10-31 07:42:57 -06001910 print("Invalid flag '%s'" % flag)
Simon Glassdee36c72017-07-10 14:47:46 -06001911 if flag == 'help' or bad:
Simon Glass793dca32019-10-31 07:42:57 -06001912 print("Imply flags: (separate with ',')")
1913 for name, info in IMPLY_FLAGS.items():
1914 print(' %-15s: %s' % (name, info[1]))
Simon Glassdee36c72017-07-10 14:47:46 -06001915 parser.print_usage()
1916 sys.exit(1)
1917 imply_flags |= IMPLY_FLAGS[flag][0]
Simon Glass9b2a2e82017-06-15 21:39:32 -06001918
Simon Glasscb008832017-06-15 21:39:33 -06001919 do_imply_config(configs, options.add_imply, imply_flags,
1920 options.skip_added)
Simon Glass99b66602017-06-01 19:39:03 -06001921 return
1922
Simon Glassd73fcb12017-06-01 19:39:02 -06001923 config_db = {}
Simon Glass793dca32019-10-31 07:42:57 -06001924 db_queue = queue.Queue()
Simon Glassd73fcb12017-06-01 19:39:02 -06001925 t = DatabaseThread(config_db, db_queue)
1926 t.setDaemon(True)
1927 t.start()
1928
Joe Hershberger2144f882015-05-19 13:21:20 -05001929 if not options.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001930 check_clean_directory()
Simon Glass793dca32019-10-31 07:42:57 -06001931 bsettings.Setup('')
Simon Glass6821a742017-07-10 14:47:47 -06001932 toolchains = toolchain.Toolchains()
1933 toolchains.GetSettings()
1934 toolchains.Scan(verbose=False)
1935 move_config(toolchains, configs, options, db_queue)
Simon Glassd73fcb12017-06-01 19:39:02 -06001936 db_queue.join()
Joe Hershberger2144f882015-05-19 13:21:20 -05001937
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001938 if configs:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +09001939 cleanup_headers(configs, options)
Masahiro Yamada9ab02962016-07-25 19:15:29 +09001940 cleanup_extra_options(configs, options)
Chris Packhamca438342017-05-02 21:30:47 +12001941 cleanup_whitelist(configs, options)
Chris Packhamf90df592017-05-02 21:30:48 +12001942 cleanup_readme(configs, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001943
Simon Glass9ede2122016-09-12 23:18:21 -06001944 if options.commit:
1945 subprocess.call(['git', 'add', '-u'])
1946 if configs:
1947 msg = 'Convert %s %sto Kconfig' % (configs[0],
1948 'et al ' if len(configs) > 1 else '')
1949 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1950 '\n '.join(configs))
1951 else:
1952 msg = 'configs: Resync with savedefconfig'
1953 msg += '\n\nRsync all defconfig files using moveconfig.py'
1954 subprocess.call(['git', 'commit', '-s', '-m', msg])
1955
Simon Glassd73fcb12017-06-01 19:39:02 -06001956 if options.build_db:
1957 with open(CONFIG_DATABASE, 'w') as fd:
Simon Glass793dca32019-10-31 07:42:57 -06001958 for defconfig, configs in config_db.items():
Simon Glassc79d18c2017-08-13 16:02:54 -06001959 fd.write('%s\n' % defconfig)
Simon Glassd73fcb12017-06-01 19:39:02 -06001960 for config in sorted(configs.keys()):
Simon Glassc79d18c2017-08-13 16:02:54 -06001961 fd.write(' %s=%s\n' % (config, configs[config]))
1962 fd.write('\n')
Simon Glassd73fcb12017-06-01 19:39:02 -06001963
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001964if __name__ == '__main__':
1965 main()