blob: c581fb6a0dfdd8fca66b6633125c0fe75a5dc431 [file] [log] [blame]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001#!/usr/bin/env python2
2#
3# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
4#
5# SPDX-License-Identifier: GPL-2.0+
6#
7
8"""
9Move config options from headers to defconfig files.
10
11Since Kconfig was introduced to U-Boot, we have worked on moving
12config options from headers to Kconfig (defconfig).
13
14This tool intends to help this tremendous work.
15
16
17Usage
18-----
19
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090020First, you must edit the Kconfig to add the menu entries for the configs
Joe Hershberger96464ba2015-05-19 13:21:17 -050021you are moving.
22
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090023And then run this tool giving CONFIG names you want to move.
24For example, if you want to move CONFIG_CMD_USB and CONFIG_SYS_TEXT_BASE,
25simply type as follows:
Masahiro Yamada5a27c732015-05-20 11:36:07 +090026
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090027 $ tools/moveconfig.py CONFIG_CMD_USB CONFIG_SYS_TEXT_BASE
Masahiro Yamada5a27c732015-05-20 11:36:07 +090028
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090029The tool walks through all the defconfig files and move the given CONFIGs.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090030
31The log is also displayed on the terminal.
32
Masahiro Yamada1d085562016-05-19 15:52:02 +090033The log is printed for each defconfig as follows:
Masahiro Yamada5a27c732015-05-20 11:36:07 +090034
Masahiro Yamada1d085562016-05-19 15:52:02 +090035<defconfig_name>
36 <action1>
37 <action2>
38 <action3>
39 ...
Masahiro Yamada5a27c732015-05-20 11:36:07 +090040
Masahiro Yamada1d085562016-05-19 15:52:02 +090041<defconfig_name> is the name of the defconfig.
42
43<action*> shows what the tool did for that defconfig.
Masahiro Yamadac21fc7e2016-08-21 16:12:36 +090044It looks like one of the following:
Masahiro Yamada5a27c732015-05-20 11:36:07 +090045
46 - Move 'CONFIG_... '
47 This config option was moved to the defconfig
48
Masahiro Yamadacc008292016-05-19 15:51:56 +090049 - CONFIG_... is not defined in Kconfig. Do nothing.
Masahiro Yamada916224c2016-08-22 22:18:21 +090050 The entry for this CONFIG was not found in Kconfig. The option is not
51 defined in the config header, either. So, this case can be just skipped.
52
53 - CONFIG_... is not defined in Kconfig (suspicious). Do nothing.
54 This option is defined in the config header, but its entry was not found
55 in Kconfig.
Masahiro Yamadacc008292016-05-19 15:51:56 +090056 There are two common cases:
57 - You forgot to create an entry for the CONFIG before running
58 this tool, or made a typo in a CONFIG passed to this tool.
59 - The entry was hidden due to unmet 'depends on'.
Masahiro Yamada916224c2016-08-22 22:18:21 +090060 The tool does not know if the result is reasonable, so please check it
61 manually.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090062
Masahiro Yamadacc008292016-05-19 15:51:56 +090063 - 'CONFIG_...' is the same as the define in Kconfig. Do nothing.
64 The define in the config header matched the one in Kconfig.
65 We do not need to touch it.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090066
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +090067 - Compiler is missing. Do nothing.
68 The compiler specified for this architecture was not found
69 in your PATH environment.
70 (If -e option is passed, the tool exits immediately.)
71
72 - Failed to process.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090073 An error occurred during processing this defconfig. Skipped.
74 (If -e option is passed, the tool exits immediately on error.)
75
76Finally, you will be asked, Clean up headers? [y/n]:
77
78If you say 'y' here, the unnecessary config defines are removed
79from the config headers (include/configs/*.h).
80It just uses the regex method, so you should not rely on it.
81Just in case, please do 'git diff' to see what happened.
82
83
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090084How does it work?
85-----------------
Masahiro Yamada5a27c732015-05-20 11:36:07 +090086
87This tool runs configuration and builds include/autoconf.mk for every
88defconfig. The config options defined in Kconfig appear in the .config
89file (unless they are hidden because of unmet dependency.)
90On the other hand, the config options defined by board headers are seen
91in include/autoconf.mk. The tool looks for the specified options in both
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090092of them to decide the appropriate action for the options. If the given
93config option is found in the .config, but its value does not match the
94one from the board header, the config option in the .config is replaced
95with the define in the board header. Then, the .config is synced by
96"make savedefconfig" and the defconfig is updated with it.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090097
98For faster processing, this tool handles multi-threading. It creates
99separate build directories where the out-of-tree build is run. The
100temporary build directories are automatically created and deleted as
101needed. The number of threads are chosen based on the number of the CPU
102cores of your system although you can change it via -j (--jobs) option.
103
104
105Toolchains
106----------
107
108Appropriate toolchain are necessary to generate include/autoconf.mk
109for all the architectures supported by U-Boot. Most of them are available
110at the kernel.org site, some are not provided by kernel.org.
111
112The default per-arch CROSS_COMPILE used by this tool is specified by
113the list below, CROSS_COMPILE. You may wish to update the list to
114use your own. Instead of modifying the list directly, you can give
115them via environments.
116
117
118Available options
119-----------------
120
121 -c, --color
122 Surround each portion of the log with escape sequences to display it
123 in color on the terminal.
124
Simon Glass9ede2122016-09-12 23:18:21 -0600125 -C, --commit
126 Create a git commit with the changes when the operation is complete. A
127 standard commit message is used which may need to be edited.
128
Joe Hershberger91040e82015-05-19 13:21:19 -0500129 -d, --defconfigs
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900130 Specify a file containing a list of defconfigs to move. The defconfig
131 files can be given with shell-style wildcards.
Joe Hershberger91040e82015-05-19 13:21:19 -0500132
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900133 -n, --dry-run
Masahiro Yamadab6ef3932016-05-19 15:51:58 +0900134 Perform a trial run that does not make any changes. It is useful to
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900135 see what is going to happen before one actually runs it.
136
137 -e, --exit-on-error
138 Exit immediately if Make exits with a non-zero status while processing
139 a defconfig file.
140
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900141 -s, --force-sync
142 Do "make savedefconfig" forcibly for all the defconfig files.
143 If not specified, "make savedefconfig" only occurs for cases
144 where at least one CONFIG was moved.
145
Masahiro Yamada07913d12016-08-22 22:18:22 +0900146 -S, --spl
147 Look for moved config options in spl/include/autoconf.mk instead of
148 include/autoconf.mk. This is useful for moving options for SPL build
149 because SPL related options (mostly prefixed with CONFIG_SPL_) are
150 sometimes blocked by CONFIG_SPL_BUILD ifdef conditionals.
151
Joe Hershberger2144f882015-05-19 13:21:20 -0500152 -H, --headers-only
153 Only cleanup the headers; skip the defconfig processing
154
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900155 -j, --jobs
156 Specify the number of threads to run simultaneously. If not specified,
157 the number of threads is the same as the number of CPU cores.
158
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500159 -r, --git-ref
160 Specify the git ref to clone for building the autoconf.mk. If unspecified
161 use the CWD. This is useful for when changes to the Kconfig affect the
162 default values and you want to capture the state of the defconfig from
163 before that change was in effect. If in doubt, specify a ref pre-Kconfig
164 changes (use HEAD if Kconfig changes are not committed). Worst case it will
165 take a bit longer to run, but will always do the right thing.
166
Joe Hershberger95bf9c72015-05-19 13:21:24 -0500167 -v, --verbose
168 Show any build errors as boards are built
169
Simon Glass6b403df2016-09-12 23:18:20 -0600170 -y, --yes
171 Instead of prompting, automatically go ahead with all operations. This
172 includes cleaning up headers and CONFIG_SYS_EXTRA_OPTIONS.
173
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900174To see the complete list of supported options, run
175
176 $ tools/moveconfig.py -h
177
178"""
179
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900180import copy
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900181import difflib
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900182import filecmp
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900183import fnmatch
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900184import glob
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900185import multiprocessing
186import optparse
187import os
188import re
189import shutil
190import subprocess
191import sys
192import tempfile
193import time
194
195SHOW_GNU_MAKE = 'scripts/show-gnu-make'
196SLEEP_TIME=0.03
197
198# Here is the list of cross-tools I use.
199# Most of them are available at kernel.org
Masahiro Yamadac21fc7e2016-08-21 16:12:36 +0900200# (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the following:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900201# arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
Bin Meng4440ece2015-09-25 01:22:39 -0700202# nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900203# nios2: https://sourcery.mentor.com/GNUToolchain/subscription42545
204# sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu
205CROSS_COMPILE = {
206 'arc': 'arc-linux-',
207 'aarch64': 'aarch64-linux-',
208 'arm': 'arm-unknown-linux-gnueabi-',
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900209 'm68k': 'm68k-linux-',
210 'microblaze': 'microblaze-linux-',
211 'mips': 'mips-linux-',
212 'nds32': 'nds32le-linux-',
213 'nios2': 'nios2-linux-gnu-',
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900214 'powerpc': 'powerpc-linux-',
215 'sh': 'sh-linux-gnu-',
Masahiro Yamada88e13462016-08-21 16:03:08 +0900216 'x86': 'i386-linux-',
217 'xtensa': 'xtensa-linux-'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900218}
219
220STATE_IDLE = 0
221STATE_DEFCONFIG = 1
222STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -0500223STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900224
225ACTION_MOVE = 0
Masahiro Yamadacc008292016-05-19 15:51:56 +0900226ACTION_NO_ENTRY = 1
Masahiro Yamada916224c2016-08-22 22:18:21 +0900227ACTION_NO_ENTRY_WARN = 2
228ACTION_NO_CHANGE = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900229
230COLOR_BLACK = '0;30'
231COLOR_RED = '0;31'
232COLOR_GREEN = '0;32'
233COLOR_BROWN = '0;33'
234COLOR_BLUE = '0;34'
235COLOR_PURPLE = '0;35'
236COLOR_CYAN = '0;36'
237COLOR_LIGHT_GRAY = '0;37'
238COLOR_DARK_GRAY = '1;30'
239COLOR_LIGHT_RED = '1;31'
240COLOR_LIGHT_GREEN = '1;32'
241COLOR_YELLOW = '1;33'
242COLOR_LIGHT_BLUE = '1;34'
243COLOR_LIGHT_PURPLE = '1;35'
244COLOR_LIGHT_CYAN = '1;36'
245COLOR_WHITE = '1;37'
246
247### helper functions ###
248def get_devnull():
249 """Get the file object of '/dev/null' device."""
250 try:
251 devnull = subprocess.DEVNULL # py3k
252 except AttributeError:
253 devnull = open(os.devnull, 'wb')
254 return devnull
255
256def check_top_directory():
257 """Exit if we are not at the top of source directory."""
258 for f in ('README', 'Licenses'):
259 if not os.path.exists(f):
260 sys.exit('Please run at the top of source directory.')
261
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900262def check_clean_directory():
263 """Exit if the source tree is not clean."""
264 for f in ('.config', 'include/config'):
265 if os.path.exists(f):
266 sys.exit("source tree is not clean, please run 'make mrproper'")
267
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900268def get_make_cmd():
269 """Get the command name of GNU Make.
270
271 U-Boot needs GNU Make for building, but the command name is not
272 necessarily "make". (for example, "gmake" on FreeBSD).
273 Returns the most appropriate command name on your system.
274 """
275 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
276 ret = process.communicate()
277 if process.returncode:
278 sys.exit('GNU Make not found')
279 return ret[0].rstrip()
280
Simon Glass25f978c2017-06-01 19:38:58 -0600281def get_matched_defconfig(line):
282 """Get the defconfig files that match a pattern
283
284 Args:
285 line: Path or filename to match, e.g. 'configs/snow_defconfig' or
286 'k2*_defconfig'. If no directory is provided, 'configs/' is
287 prepended
288
289 Returns:
290 a list of matching defconfig files
291 """
292 dirname = os.path.dirname(line)
293 if dirname:
294 pattern = line
295 else:
296 pattern = os.path.join('configs', line)
297 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
298
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900299def get_matched_defconfigs(defconfigs_file):
Simon Glassee4e61b2017-06-01 19:38:59 -0600300 """Get all the defconfig files that match the patterns in a file.
301
302 Args:
303 defconfigs_file: File containing a list of defconfigs to process, or
304 '-' to read the list from stdin
305
306 Returns:
307 A list of paths to defconfig files, with no duplicates
308 """
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900309 defconfigs = []
Simon Glassee4e61b2017-06-01 19:38:59 -0600310 if defconfigs_file == '-':
311 fd = sys.stdin
312 defconfigs_file = 'stdin'
313 else:
314 fd = open(defconfigs_file)
315 for i, line in enumerate(fd):
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900316 line = line.strip()
317 if not line:
318 continue # skip blank lines silently
Simon Glass25f978c2017-06-01 19:38:58 -0600319 matched = get_matched_defconfig(line)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900320 if not matched:
321 print >> sys.stderr, "warning: %s:%d: no defconfig matched '%s'" % \
322 (defconfigs_file, i + 1, line)
323
324 defconfigs += matched
325
326 # use set() to drop multiple matching
327 return [ defconfig[len('configs') + 1:] for defconfig in set(defconfigs) ]
328
Masahiro Yamada684c3062016-07-25 19:15:28 +0900329def get_all_defconfigs():
330 """Get all the defconfig files under the configs/ directory."""
331 defconfigs = []
332 for (dirpath, dirnames, filenames) in os.walk('configs'):
333 dirpath = dirpath[len('configs') + 1:]
334 for filename in fnmatch.filter(filenames, '*_defconfig'):
335 defconfigs.append(os.path.join(dirpath, filename))
336
337 return defconfigs
338
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900339def color_text(color_enabled, color, string):
340 """Return colored string."""
341 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900342 # LF should not be surrounded by the escape sequence.
343 # Otherwise, additional whitespace or line-feed might be printed.
344 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
345 for s in string.split('\n') ])
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900346 else:
347 return string
348
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900349def show_diff(a, b, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900350 """Show unidified diff.
351
352 Arguments:
353 a: A list of lines (before)
354 b: A list of lines (after)
355 file_path: Path to the file
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900356 color_enabled: Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900357 """
358
359 diff = difflib.unified_diff(a, b,
360 fromfile=os.path.join('a', file_path),
361 tofile=os.path.join('b', file_path))
362
363 for line in diff:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900364 if line[0] == '-' and line[1] != '-':
365 print color_text(color_enabled, COLOR_RED, line),
366 elif line[0] == '+' and line[1] != '+':
367 print color_text(color_enabled, COLOR_GREEN, line),
368 else:
369 print line,
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900370
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900371def update_cross_compile(color_enabled):
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400372 """Update per-arch CROSS_COMPILE via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900373
374 The default CROSS_COMPILE values are available
375 in the CROSS_COMPILE list above.
376
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400377 You can override them via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900378 CROSS_COMPILE_{ARCH}.
379
380 For example, if you want to override toolchain prefixes
381 for ARM and PowerPC, you can do as follows in your shell:
382
383 export CROSS_COMPILE_ARM=...
384 export CROSS_COMPILE_POWERPC=...
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900385
386 Then, this function checks if specified compilers really exist in your
387 PATH environment.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900388 """
389 archs = []
390
391 for arch in os.listdir('arch'):
392 if os.path.exists(os.path.join('arch', arch, 'Makefile')):
393 archs.append(arch)
394
395 # arm64 is a special case
396 archs.append('aarch64')
397
398 for arch in archs:
399 env = 'CROSS_COMPILE_' + arch.upper()
400 cross_compile = os.environ.get(env)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900401 if not cross_compile:
402 cross_compile = CROSS_COMPILE.get(arch, '')
403
404 for path in os.environ["PATH"].split(os.pathsep):
405 gcc_path = os.path.join(path, cross_compile + 'gcc')
406 if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
407 break
408 else:
409 print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
410 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
411 % (cross_compile, arch))
412 cross_compile = None
413
414 CROSS_COMPILE[arch] = cross_compile
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900415
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900416def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
417 extend_post):
418 """Extend matched lines if desired patterns are found before/after already
419 matched lines.
420
421 Arguments:
422 lines: A list of lines handled.
423 matched: A list of line numbers that have been already matched.
424 (will be updated by this function)
425 pre_patterns: A list of regular expression that should be matched as
426 preamble.
427 post_patterns: A list of regular expression that should be matched as
428 postamble.
429 extend_pre: Add the line number of matched preamble to the matched list.
430 extend_post: Add the line number of matched postamble to the matched list.
431 """
432 extended_matched = []
433
434 j = matched[0]
435
436 for i in matched:
437 if i == 0 or i < j:
438 continue
439 j = i
440 while j in matched:
441 j += 1
442 if j >= len(lines):
443 break
444
445 for p in pre_patterns:
446 if p.search(lines[i - 1]):
447 break
448 else:
449 # not matched
450 continue
451
452 for p in post_patterns:
453 if p.search(lines[j]):
454 break
455 else:
456 # not matched
457 continue
458
459 if extend_pre:
460 extended_matched.append(i - 1)
461 if extend_post:
462 extended_matched.append(j)
463
464 matched += extended_matched
465 matched.sort()
466
Chris Packham85edfc12017-05-02 21:30:46 +1200467def confirm(options, prompt):
468 if not options.yes:
469 while True:
470 choice = raw_input('{} [y/n]: '.format(prompt))
471 choice = choice.lower()
472 print choice
473 if choice == 'y' or choice == 'n':
474 break
475
476 if choice == 'n':
477 return False
478
479 return True
480
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900481def cleanup_one_header(header_path, patterns, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900482 """Clean regex-matched lines away from a file.
483
484 Arguments:
485 header_path: path to the cleaned file.
486 patterns: list of regex patterns. Any lines matching to these
487 patterns are deleted.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900488 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900489 """
490 with open(header_path) as f:
491 lines = f.readlines()
492
493 matched = []
494 for i, line in enumerate(lines):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900495 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
496 matched.append(i)
497 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900498 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900499 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900500 matched.append(i)
501 break
502
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900503 if not matched:
504 return
505
506 # remove empty #ifdef ... #endif, successive blank lines
507 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
508 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
509 pattern_endif = re.compile(r'#\s*endif\W') # #endif
510 pattern_blank = re.compile(r'^\s*$') # empty line
511
512 while True:
513 old_matched = copy.copy(matched)
514 extend_matched_lines(lines, matched, [pattern_if],
515 [pattern_endif], True, True)
516 extend_matched_lines(lines, matched, [pattern_elif],
517 [pattern_elif, pattern_endif], True, False)
518 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
519 [pattern_blank], False, True)
520 extend_matched_lines(lines, matched, [pattern_blank],
521 [pattern_elif, pattern_endif], True, False)
522 extend_matched_lines(lines, matched, [pattern_blank],
523 [pattern_blank], True, False)
524 if matched == old_matched:
525 break
526
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900527 tolines = copy.copy(lines)
528
529 for i in reversed(matched):
530 tolines.pop(i)
531
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900532 show_diff(lines, tolines, header_path, options.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900533
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900534 if options.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900535 return
536
537 with open(header_path, 'w') as f:
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900538 for line in tolines:
539 f.write(line)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900540
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900541def cleanup_headers(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900542 """Delete config defines from board headers.
543
544 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900545 configs: A list of CONFIGs to remove.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900546 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900547 """
Chris Packham85edfc12017-05-02 21:30:46 +1200548 if not confirm(options, 'Clean up headers?'):
549 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900550
551 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900552 for config in configs:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900553 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
554 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
555
Joe Hershberger60727f52015-05-19 13:21:21 -0500556 for dir in 'include', 'arch', 'board':
557 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900558 if dirpath == os.path.join('include', 'generated'):
559 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500560 for filename in filenames:
561 if not fnmatch.fnmatch(filename, '*~'):
562 cleanup_one_header(os.path.join(dirpath, filename),
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900563 patterns, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900564
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900565def cleanup_one_extra_option(defconfig_path, configs, options):
566 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
567
568 Arguments:
569 defconfig_path: path to the cleaned defconfig file.
570 configs: A list of CONFIGs to remove.
571 options: option flags.
572 """
573
574 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
575 end = '"\n'
576
577 with open(defconfig_path) as f:
578 lines = f.readlines()
579
580 for i, line in enumerate(lines):
581 if line.startswith(start) and line.endswith(end):
582 break
583 else:
584 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
585 return
586
587 old_tokens = line[len(start):-len(end)].split(',')
588 new_tokens = []
589
590 for token in old_tokens:
591 pos = token.find('=')
592 if not (token[:pos] if pos >= 0 else token) in configs:
593 new_tokens.append(token)
594
595 if new_tokens == old_tokens:
596 return
597
598 tolines = copy.copy(lines)
599
600 if new_tokens:
601 tolines[i] = start + ','.join(new_tokens) + end
602 else:
603 tolines.pop(i)
604
605 show_diff(lines, tolines, defconfig_path, options.color)
606
607 if options.dry_run:
608 return
609
610 with open(defconfig_path, 'w') as f:
611 for line in tolines:
612 f.write(line)
613
614def cleanup_extra_options(configs, options):
615 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
616
617 Arguments:
618 configs: A list of CONFIGs to remove.
619 options: option flags.
620 """
Chris Packham85edfc12017-05-02 21:30:46 +1200621 if not confirm(options, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
622 return
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900623
624 configs = [ config[len('CONFIG_'):] for config in configs ]
625
626 defconfigs = get_all_defconfigs()
627
628 for defconfig in defconfigs:
629 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
630 options)
631
Chris Packhamca438342017-05-02 21:30:47 +1200632def cleanup_whitelist(configs, options):
633 """Delete config whitelist entries
634
635 Arguments:
636 configs: A list of CONFIGs to remove.
637 options: option flags.
638 """
639 if not confirm(options, 'Clean up whitelist entries?'):
640 return
641
642 with open(os.path.join('scripts', 'config_whitelist.txt')) as f:
643 lines = f.readlines()
644
645 lines = [x for x in lines if x.strip() not in configs]
646
647 with open(os.path.join('scripts', 'config_whitelist.txt'), 'w') as f:
648 f.write(''.join(lines))
649
Chris Packhamf90df592017-05-02 21:30:48 +1200650def find_matching(patterns, line):
651 for pat in patterns:
652 if pat.search(line):
653 return True
654 return False
655
656def cleanup_readme(configs, options):
657 """Delete config description in README
658
659 Arguments:
660 configs: A list of CONFIGs to remove.
661 options: option flags.
662 """
663 if not confirm(options, 'Clean up README?'):
664 return
665
666 patterns = []
667 for config in configs:
668 patterns.append(re.compile(r'^\s+%s' % config))
669
670 with open('README') as f:
671 lines = f.readlines()
672
673 found = False
674 newlines = []
675 for line in lines:
676 if not found:
677 found = find_matching(patterns, line)
678 if found:
679 continue
680
681 if found and re.search(r'^\s+CONFIG', line):
682 found = False
683
684 if not found:
685 newlines.append(line)
686
687 with open('README', 'w') as f:
688 f.write(''.join(newlines))
689
Chris Packhamca438342017-05-02 21:30:47 +1200690
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900691### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900692class Progress:
693
694 """Progress Indicator"""
695
696 def __init__(self, total):
697 """Create a new progress indicator.
698
699 Arguments:
700 total: A number of defconfig files to process.
701 """
702 self.current = 0
703 self.total = total
704
705 def inc(self):
706 """Increment the number of processed defconfig files."""
707
708 self.current += 1
709
710 def show(self):
711 """Display the progress."""
712 print ' %d defconfigs out of %d\r' % (self.current, self.total),
713 sys.stdout.flush()
714
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900715class KconfigParser:
716
717 """A parser of .config and include/autoconf.mk."""
718
719 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
720 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
721
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900722 def __init__(self, configs, options, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900723 """Create a new parser.
724
725 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900726 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900727 options: option flags.
728 build_dir: Build directory.
729 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900730 self.configs = configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900731 self.options = options
Masahiro Yamada1f169922016-05-19 15:52:00 +0900732 self.dotconfig = os.path.join(build_dir, '.config')
733 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900734 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
735 'autoconf.mk')
Masahiro Yamada1f169922016-05-19 15:52:00 +0900736 self.config_autoconf = os.path.join(build_dir, 'include', 'config',
737 'auto.conf')
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900738 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900739
740 def get_cross_compile(self):
741 """Parse .config file and return CROSS_COMPILE.
742
743 Returns:
744 A string storing the compiler prefix for the architecture.
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900745 Return a NULL string for architectures that do not require
746 compiler prefix (Sandbox and native build is the case).
747 Return None if the specified compiler is missing in your PATH.
748 Caller should distinguish '' and None.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900749 """
750 arch = ''
751 cpu = ''
Masahiro Yamada1f169922016-05-19 15:52:00 +0900752 for line in open(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900753 m = self.re_arch.match(line)
754 if m:
755 arch = m.group(1)
756 continue
757 m = self.re_cpu.match(line)
758 if m:
759 cpu = m.group(1)
760
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900761 if not arch:
762 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900763
764 # fix-up for aarch64
765 if arch == 'arm' and cpu == 'armv8':
766 arch = 'aarch64'
767
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900768 return CROSS_COMPILE.get(arch, None)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900769
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900770 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900771 """Parse .config, defconfig, include/autoconf.mk for one config.
772
773 This function looks for the config options in the lines from
774 defconfig, .config, and include/autoconf.mk in order to decide
775 which action should be taken for this defconfig.
776
777 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900778 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900779 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900780 autoconf_lines: lines from the include/autoconf.mk file.
781
782 Returns:
783 A tupple of the action for this defconfig and the line
784 matched for the config.
785 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900786 not_set = '# %s is not set' % config
787
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900788 for line in autoconf_lines:
789 line = line.rstrip()
790 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900791 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900792 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900793 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900794 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900795
Masahiro Yamada916224c2016-08-22 22:18:21 +0900796 for line in dotconfig_lines:
797 line = line.rstrip()
798 if line.startswith(config + '=') or line == not_set:
799 old_val = line
800 break
801 else:
802 if new_val == not_set:
803 return (ACTION_NO_ENTRY, config)
804 else:
805 return (ACTION_NO_ENTRY_WARN, config)
806
Masahiro Yamadacc008292016-05-19 15:51:56 +0900807 # If this CONFIG is neither bool nor trisate
808 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
809 # tools/scripts/define2mk.sed changes '1' to 'y'.
810 # This is a problem if the CONFIG is int type.
811 # Check the type in Kconfig and handle it correctly.
812 if new_val[-2:] == '=y':
813 new_val = new_val[:-1] + '1'
814
Masahiro Yamada50301592016-06-15 14:33:50 +0900815 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
816 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900817
Masahiro Yamada1d085562016-05-19 15:52:02 +0900818 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900819 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900820
Masahiro Yamadacc008292016-05-19 15:51:56 +0900821 This function parses the generated .config and include/autoconf.mk
822 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900823 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900824
825 Arguments:
826 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900827
828 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900829 Return a tuple of (updated flag, log string).
830 The "updated flag" is True if the .config was updated, False
831 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900832 """
833
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900834 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900835 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900836 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900837 rm_files = [self.config_autoconf, self.autoconf]
838
839 if self.options.spl:
840 if os.path.exists(self.spl_autoconf):
841 autoconf_path = self.spl_autoconf
842 rm_files.append(self.spl_autoconf)
843 else:
844 for f in rm_files:
845 os.remove(f)
846 return (updated, suspicious,
847 color_text(self.options.color, COLOR_BROWN,
848 "SPL is not enabled. Skipped.") + '\n')
849 else:
850 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900851
Masahiro Yamada1f169922016-05-19 15:52:00 +0900852 with open(self.dotconfig) as f:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900853 dotconfig_lines = f.readlines()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900854
Masahiro Yamada07913d12016-08-22 22:18:22 +0900855 with open(autoconf_path) as f:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900856 autoconf_lines = f.readlines()
857
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900858 for config in self.configs:
859 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500860 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900861 results.append(result)
862
863 log = ''
864
865 for (action, value) in results:
866 if action == ACTION_MOVE:
867 actlog = "Move '%s'" % value
868 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900869 elif action == ACTION_NO_ENTRY:
870 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900871 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900872 elif action == ACTION_NO_ENTRY_WARN:
873 actlog = "%s is not defined in Kconfig (suspicious). Do nothing." % value
874 log_color = COLOR_YELLOW
875 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900876 elif action == ACTION_NO_CHANGE:
877 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
878 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900879 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada07913d12016-08-22 22:18:22 +0900880 elif action == ACTION_SPL_NOT_EXIST:
881 actlog = "SPL is not enabled for this defconfig. Skip."
882 log_color = COLOR_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900883 else:
884 sys.exit("Internal Error. This should not happen.")
885
Masahiro Yamada1d085562016-05-19 15:52:02 +0900886 log += color_text(self.options.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900887
Masahiro Yamada1f169922016-05-19 15:52:00 +0900888 with open(self.dotconfig, 'a') as f:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900889 for (action, value) in results:
890 if action == ACTION_MOVE:
891 f.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900892 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900893
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900894 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900895 for f in rm_files:
896 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900897
Masahiro Yamada916224c2016-08-22 22:18:21 +0900898 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900899
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900900 def check_defconfig(self):
901 """Check the defconfig after savedefconfig
902
903 Returns:
904 Return additional log if moved CONFIGs were removed again by
905 'make savedefconfig'.
906 """
907
908 log = ''
909
910 with open(self.defconfig) as f:
911 defconfig_lines = f.readlines()
912
913 for (action, value) in self.results:
914 if action != ACTION_MOVE:
915 continue
916 if not value + '\n' in defconfig_lines:
917 log += color_text(self.options.color, COLOR_YELLOW,
918 "'%s' was removed by savedefconfig.\n" %
919 value)
920
921 return log
922
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900923class Slot:
924
925 """A slot to store a subprocess.
926
927 Each instance of this class handles one subprocess.
928 This class is useful to control multiple threads
929 for faster processing.
930 """
931
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500932 def __init__(self, configs, options, progress, devnull, make_cmd, reference_src_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900933 """Create a new process slot.
934
935 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900936 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900937 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900938 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900939 devnull: A file object of '/dev/null'.
940 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500941 reference_src_dir: Determine the true starting config state from this
942 source tree.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900943 """
944 self.options = options
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900945 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900946 self.build_dir = tempfile.mkdtemp()
947 self.devnull = devnull
948 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500949 self.reference_src_dir = reference_src_dir
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900950 self.parser = KconfigParser(configs, options, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900951 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900952 self.failed_boards = set()
953 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900954
955 def __del__(self):
956 """Delete the working directory
957
958 This function makes sure the temporary directory is cleaned away
959 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500960 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900961 instance of the class gets unreferenced.
962
963 If the subprocess is still running, wait until it finishes.
964 """
965 if self.state != STATE_IDLE:
966 while self.ps.poll() == None:
967 pass
968 shutil.rmtree(self.build_dir)
969
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900970 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900971 """Assign a new subprocess for defconfig and add it to the slot.
972
973 If the slot is vacant, create a new subprocess for processing the
974 given defconfig and add it to the slot. Just returns False if
975 the slot is occupied (i.e. the current subprocess is still running).
976
977 Arguments:
978 defconfig: defconfig name.
979
980 Returns:
981 Return True on success or False on failure
982 """
983 if self.state != STATE_IDLE:
984 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900985
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900986 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900987 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900988 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900989 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900990 return True
991
992 def poll(self):
993 """Check the status of the subprocess and handle it as needed.
994
995 Returns True if the slot is vacant (i.e. in idle state).
996 If the configuration is successfully finished, assign a new
997 subprocess to build include/autoconf.mk.
998 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900999 parse the .config and the include/autoconf.mk, moving
1000 config options to the .config as needed.
1001 If the .config was updated, run "make savedefconfig" to sync
1002 it, update the original defconfig, and then set the slot back
1003 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001004
1005 Returns:
1006 Return True if the subprocess is terminated, False otherwise
1007 """
1008 if self.state == STATE_IDLE:
1009 return True
1010
1011 if self.ps.poll() == None:
1012 return False
1013
1014 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001015 self.handle_error()
1016 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001017 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001018 self.do_savedefconfig()
1019 else:
1020 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001021 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001022 if self.current_src_dir:
1023 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001024 self.do_defconfig()
1025 else:
1026 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001027 elif self.state == STATE_SAVEDEFCONFIG:
1028 self.update_defconfig()
1029 else:
1030 sys.exit("Internal Error. This should not happen.")
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001031
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001032 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -05001033
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001034 def handle_error(self):
1035 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001036
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001037 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
1038 "Failed to process.\n")
1039 if self.options.verbose:
1040 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
1041 self.ps.stderr.read())
1042 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -05001043
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001044 def do_defconfig(self):
1045 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +09001046
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001047 cmd = list(self.make_cmd)
1048 cmd.append(self.defconfig)
1049 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001050 stderr=subprocess.PIPE,
1051 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001052 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +09001053
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001054 def do_autoconf(self):
1055 """Run 'make include/config/auto.conf'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001056
Joe Hershberger25400092015-05-19 13:21:23 -05001057 self.cross_compile = self.parser.get_cross_compile()
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +09001058 if self.cross_compile is None:
Masahiro Yamada1d085562016-05-19 15:52:02 +09001059 self.log += color_text(self.options.color, COLOR_YELLOW,
1060 "Compiler is missing. Do nothing.\n")
Masahiro Yamada4efef992016-05-19 15:52:03 +09001061 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001062 return
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +09001063
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001064 cmd = list(self.make_cmd)
Joe Hershberger25400092015-05-19 13:21:23 -05001065 if self.cross_compile:
1066 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
Joe Hershberger7740f652015-05-19 13:21:18 -05001067 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001068 cmd.append('include/config/auto.conf')
Joe Hershberger25400092015-05-19 13:21:23 -05001069 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001070 stderr=subprocess.PIPE,
1071 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001072 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001073
1074 def do_savedefconfig(self):
1075 """Update the .config and run 'make savedefconfig'."""
1076
Masahiro Yamada916224c2016-08-22 22:18:21 +09001077 (updated, suspicious, log) = self.parser.update_dotconfig()
1078 if suspicious:
1079 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001080 self.log += log
1081
1082 if not self.options.force_sync and not updated:
1083 self.finish(True)
1084 return
1085 if updated:
1086 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
1087 "Syncing by savedefconfig...\n")
1088 else:
1089 self.log += "Syncing by savedefconfig (forced by option)...\n"
1090
1091 cmd = list(self.make_cmd)
1092 cmd.append('savedefconfig')
1093 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1094 stderr=subprocess.PIPE)
1095 self.state = STATE_SAVEDEFCONFIG
1096
1097 def update_defconfig(self):
1098 """Update the input defconfig and go back to the idle state."""
1099
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001100 log = self.parser.check_defconfig()
1101 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001102 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001103 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001104 orig_defconfig = os.path.join('configs', self.defconfig)
1105 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1106 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1107
1108 if updated:
Joe Hershberger06cc1d32016-06-10 14:53:30 -05001109 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001110 "defconfig was updated.\n")
1111
1112 if not self.options.dry_run and updated:
1113 shutil.move(new_defconfig, orig_defconfig)
1114 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001115
Masahiro Yamada4efef992016-05-19 15:52:03 +09001116 def finish(self, success):
1117 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001118
1119 Arguments:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001120 success: Should be True when the defconfig was processed
1121 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001122 """
1123 # output at least 30 characters to hide the "* defconfigs out of *".
1124 log = self.defconfig.ljust(30) + '\n'
1125
1126 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1127 # Some threads are running in parallel.
1128 # Print log atomically to not mix up logs from different threads.
Masahiro Yamada4efef992016-05-19 15:52:03 +09001129 print >> (sys.stdout if success else sys.stderr), log
1130
1131 if not success:
1132 if self.options.exit_on_error:
1133 sys.exit("Exit on error.")
1134 # If --exit-on-error flag is not set, skip this board and continue.
1135 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001136 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001137
Masahiro Yamada1d085562016-05-19 15:52:02 +09001138 self.progress.inc()
1139 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001140 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001141
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001142 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001143 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001144 """
1145 return self.failed_boards
1146
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001147 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001148 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001149 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001150 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001151
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001152class Slots:
1153
1154 """Controller of the array of subprocess slots."""
1155
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001156 def __init__(self, configs, options, progress, reference_src_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001157 """Create a new slots controller.
1158
1159 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001160 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001161 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001162 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001163 reference_src_dir: Determine the true starting config state from this
1164 source tree.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001165 """
1166 self.options = options
1167 self.slots = []
1168 devnull = get_devnull()
1169 make_cmd = get_make_cmd()
1170 for i in range(options.jobs):
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001171 self.slots.append(Slot(configs, options, progress, devnull,
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001172 make_cmd, reference_src_dir))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001173
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001174 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001175 """Add a new subprocess if a vacant slot is found.
1176
1177 Arguments:
1178 defconfig: defconfig name to be put into.
1179
1180 Returns:
1181 Return True on success or False on failure
1182 """
1183 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001184 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001185 return True
1186 return False
1187
1188 def available(self):
1189 """Check if there is a vacant slot.
1190
1191 Returns:
1192 Return True if at lease one vacant slot is found, False otherwise.
1193 """
1194 for slot in self.slots:
1195 if slot.poll():
1196 return True
1197 return False
1198
1199 def empty(self):
1200 """Check if all slots are vacant.
1201
1202 Returns:
1203 Return True if all the slots are vacant, False otherwise.
1204 """
1205 ret = True
1206 for slot in self.slots:
1207 if not slot.poll():
1208 ret = False
1209 return ret
1210
1211 def show_failed_boards(self):
1212 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001213 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001214 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001215
1216 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001217 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001218
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001219 if boards:
1220 boards = '\n'.join(boards) + '\n'
1221 msg = "The following boards were not processed due to error:\n"
1222 msg += boards
1223 msg += "(the list has been saved in %s)\n" % output_file
1224 print >> sys.stderr, color_text(self.options.color, COLOR_LIGHT_RED,
1225 msg)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001226
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001227 with open(output_file, 'w') as f:
1228 f.write(boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001229
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001230 def show_suspicious_boards(self):
1231 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001232 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001233 output_file = 'moveconfig.suspicious'
1234
1235 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001236 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001237
1238 if boards:
1239 boards = '\n'.join(boards) + '\n'
1240 msg = "The following boards might have been converted incorrectly.\n"
1241 msg += "It is highly recommended to check them manually:\n"
1242 msg += boards
1243 msg += "(the list has been saved in %s)\n" % output_file
1244 print >> sys.stderr, color_text(self.options.color, COLOR_YELLOW,
1245 msg)
1246
1247 with open(output_file, 'w') as f:
1248 f.write(boards)
1249
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001250class ReferenceSource:
1251
1252 """Reference source against which original configs should be parsed."""
1253
1254 def __init__(self, commit):
1255 """Create a reference source directory based on a specified commit.
1256
1257 Arguments:
1258 commit: commit to git-clone
1259 """
1260 self.src_dir = tempfile.mkdtemp()
1261 print "Cloning git repo to a separate work directory..."
1262 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1263 cwd=self.src_dir)
1264 print "Checkout '%s' to build the original autoconf.mk." % \
1265 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip()
1266 subprocess.check_output(['git', 'checkout', commit],
1267 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001268
1269 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001270 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001271
1272 This function makes sure the temporary directory is cleaned away
1273 even if Python suddenly dies due to error. It should be done in here
1274 because it is guaranteed the destructor is always invoked when the
1275 instance of the class gets unreferenced.
1276 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001277 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001278
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001279 def get_dir(self):
1280 """Return the absolute path to the reference source directory."""
1281
1282 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001283
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001284def move_config(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001285 """Move config options to defconfig files.
1286
1287 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001288 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001289 options: option flags
1290 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001291 if len(configs) == 0:
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001292 if options.force_sync:
1293 print 'No CONFIG is specified. You are probably syncing defconfigs.',
1294 else:
1295 print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
1296 else:
1297 print 'Move ' + ', '.join(configs),
1298 print '(jobs: %d)\n' % options.jobs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001299
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001300 if options.git_ref:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001301 reference_src = ReferenceSource(options.git_ref)
1302 reference_src_dir = reference_src.get_dir()
1303 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001304 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001305
Joe Hershberger91040e82015-05-19 13:21:19 -05001306 if options.defconfigs:
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +09001307 defconfigs = get_matched_defconfigs(options.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001308 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001309 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001310
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001311 progress = Progress(len(defconfigs))
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001312 slots = Slots(configs, options, progress, reference_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001313
1314 # Main loop to process defconfig files:
1315 # Add a new subprocess into a vacant slot.
1316 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001317 for defconfig in defconfigs:
1318 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001319 while not slots.available():
1320 # No available slot: sleep for a while
1321 time.sleep(SLEEP_TIME)
1322
1323 # wait until all the subprocesses finish
1324 while not slots.empty():
1325 time.sleep(SLEEP_TIME)
1326
Joe Hershberger2e2ce6c2015-05-19 13:21:25 -05001327 print ''
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001328 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001329 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001330
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001331def main():
1332 try:
1333 cpu_count = multiprocessing.cpu_count()
1334 except NotImplementedError:
1335 cpu_count = 1
1336
1337 parser = optparse.OptionParser()
1338 # Add options here
1339 parser.add_option('-c', '--color', action='store_true', default=False,
1340 help='display the log in color')
Simon Glass9ede2122016-09-12 23:18:21 -06001341 parser.add_option('-C', '--commit', action='store_true', default=False,
1342 help='Create a git commit for the operation')
Joe Hershberger91040e82015-05-19 13:21:19 -05001343 parser.add_option('-d', '--defconfigs', type='string',
Simon Glassee4e61b2017-06-01 19:38:59 -06001344 help='a file containing a list of defconfigs to move, '
1345 "one per line (for example 'snow_defconfig') "
1346 "or '-' to read from stdin")
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001347 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1348 help='perform a trial run (show log with no changes)')
1349 parser.add_option('-e', '--exit-on-error', action='store_true',
1350 default=False,
1351 help='exit immediately on any error')
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001352 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1353 help='force sync by savedefconfig')
Masahiro Yamada07913d12016-08-22 22:18:22 +09001354 parser.add_option('-S', '--spl', action='store_true', default=False,
1355 help='parse config options defined for SPL build')
Joe Hershberger2144f882015-05-19 13:21:20 -05001356 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1357 action='store_true', default=False,
1358 help='only cleanup the headers')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001359 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1360 help='the number of jobs to run simultaneously')
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001361 parser.add_option('-r', '--git-ref', type='string',
1362 help='the git ref to clone for building the autoconf.mk')
Simon Glass6b403df2016-09-12 23:18:20 -06001363 parser.add_option('-y', '--yes', action='store_true', default=False,
1364 help="respond 'yes' to any prompts")
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001365 parser.add_option('-v', '--verbose', action='store_true', default=False,
1366 help='show any build errors as boards are built')
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001367 parser.usage += ' CONFIG ...'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001368
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001369 (options, configs) = parser.parse_args()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001370
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001371 if len(configs) == 0 and not options.force_sync:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001372 parser.print_usage()
1373 sys.exit(1)
1374
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001375 # prefix the option name with CONFIG_ if missing
1376 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1377 for config in configs ]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001378
Joe Hershberger2144f882015-05-19 13:21:20 -05001379 check_top_directory()
1380
1381 if not options.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001382 check_clean_directory()
1383 update_cross_compile(options.color)
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001384 move_config(configs, options)
Joe Hershberger2144f882015-05-19 13:21:20 -05001385
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001386 if configs:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +09001387 cleanup_headers(configs, options)
Masahiro Yamada9ab02962016-07-25 19:15:29 +09001388 cleanup_extra_options(configs, options)
Chris Packhamca438342017-05-02 21:30:47 +12001389 cleanup_whitelist(configs, options)
Chris Packhamf90df592017-05-02 21:30:48 +12001390 cleanup_readme(configs, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001391
Simon Glass9ede2122016-09-12 23:18:21 -06001392 if options.commit:
1393 subprocess.call(['git', 'add', '-u'])
1394 if configs:
1395 msg = 'Convert %s %sto Kconfig' % (configs[0],
1396 'et al ' if len(configs) > 1 else '')
1397 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1398 '\n '.join(configs))
1399 else:
1400 msg = 'configs: Resync with savedefconfig'
1401 msg += '\n\nRsync all defconfig files using moveconfig.py'
1402 subprocess.call(['git', 'commit', '-s', '-m', msg])
1403
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001404if __name__ == '__main__':
1405 main()