blob: 2361a430dc845f28d98bbb3ad1cd3c3f8b9eb92b [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-',
209 'avr32': 'avr32-linux-',
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900210 'm68k': 'm68k-linux-',
211 'microblaze': 'microblaze-linux-',
212 'mips': 'mips-linux-',
213 'nds32': 'nds32le-linux-',
214 'nios2': 'nios2-linux-gnu-',
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900215 'powerpc': 'powerpc-linux-',
216 'sh': 'sh-linux-gnu-',
Masahiro Yamada88e13462016-08-21 16:03:08 +0900217 'x86': 'i386-linux-',
218 'xtensa': 'xtensa-linux-'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900219}
220
221STATE_IDLE = 0
222STATE_DEFCONFIG = 1
223STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -0500224STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900225
226ACTION_MOVE = 0
Masahiro Yamadacc008292016-05-19 15:51:56 +0900227ACTION_NO_ENTRY = 1
Masahiro Yamada916224c2016-08-22 22:18:21 +0900228ACTION_NO_ENTRY_WARN = 2
229ACTION_NO_CHANGE = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900230
231COLOR_BLACK = '0;30'
232COLOR_RED = '0;31'
233COLOR_GREEN = '0;32'
234COLOR_BROWN = '0;33'
235COLOR_BLUE = '0;34'
236COLOR_PURPLE = '0;35'
237COLOR_CYAN = '0;36'
238COLOR_LIGHT_GRAY = '0;37'
239COLOR_DARK_GRAY = '1;30'
240COLOR_LIGHT_RED = '1;31'
241COLOR_LIGHT_GREEN = '1;32'
242COLOR_YELLOW = '1;33'
243COLOR_LIGHT_BLUE = '1;34'
244COLOR_LIGHT_PURPLE = '1;35'
245COLOR_LIGHT_CYAN = '1;36'
246COLOR_WHITE = '1;37'
247
248### helper functions ###
249def get_devnull():
250 """Get the file object of '/dev/null' device."""
251 try:
252 devnull = subprocess.DEVNULL # py3k
253 except AttributeError:
254 devnull = open(os.devnull, 'wb')
255 return devnull
256
257def check_top_directory():
258 """Exit if we are not at the top of source directory."""
259 for f in ('README', 'Licenses'):
260 if not os.path.exists(f):
261 sys.exit('Please run at the top of source directory.')
262
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900263def check_clean_directory():
264 """Exit if the source tree is not clean."""
265 for f in ('.config', 'include/config'):
266 if os.path.exists(f):
267 sys.exit("source tree is not clean, please run 'make mrproper'")
268
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900269def get_make_cmd():
270 """Get the command name of GNU Make.
271
272 U-Boot needs GNU Make for building, but the command name is not
273 necessarily "make". (for example, "gmake" on FreeBSD).
274 Returns the most appropriate command name on your system.
275 """
276 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
277 ret = process.communicate()
278 if process.returncode:
279 sys.exit('GNU Make not found')
280 return ret[0].rstrip()
281
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900282def get_matched_defconfigs(defconfigs_file):
283 """Get all the defconfig files that match the patterns in a file."""
284 defconfigs = []
285 for i, line in enumerate(open(defconfigs_file)):
286 line = line.strip()
287 if not line:
288 continue # skip blank lines silently
289 pattern = os.path.join('configs', line)
290 matched = glob.glob(pattern) + glob.glob(pattern + '_defconfig')
291 if not matched:
292 print >> sys.stderr, "warning: %s:%d: no defconfig matched '%s'" % \
293 (defconfigs_file, i + 1, line)
294
295 defconfigs += matched
296
297 # use set() to drop multiple matching
298 return [ defconfig[len('configs') + 1:] for defconfig in set(defconfigs) ]
299
Masahiro Yamada684c3062016-07-25 19:15:28 +0900300def get_all_defconfigs():
301 """Get all the defconfig files under the configs/ directory."""
302 defconfigs = []
303 for (dirpath, dirnames, filenames) in os.walk('configs'):
304 dirpath = dirpath[len('configs') + 1:]
305 for filename in fnmatch.filter(filenames, '*_defconfig'):
306 defconfigs.append(os.path.join(dirpath, filename))
307
308 return defconfigs
309
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900310def color_text(color_enabled, color, string):
311 """Return colored string."""
312 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900313 # LF should not be surrounded by the escape sequence.
314 # Otherwise, additional whitespace or line-feed might be printed.
315 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
316 for s in string.split('\n') ])
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900317 else:
318 return string
319
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900320def show_diff(a, b, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900321 """Show unidified diff.
322
323 Arguments:
324 a: A list of lines (before)
325 b: A list of lines (after)
326 file_path: Path to the file
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900327 color_enabled: Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900328 """
329
330 diff = difflib.unified_diff(a, b,
331 fromfile=os.path.join('a', file_path),
332 tofile=os.path.join('b', file_path))
333
334 for line in diff:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900335 if line[0] == '-' and line[1] != '-':
336 print color_text(color_enabled, COLOR_RED, line),
337 elif line[0] == '+' and line[1] != '+':
338 print color_text(color_enabled, COLOR_GREEN, line),
339 else:
340 print line,
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900341
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900342def update_cross_compile(color_enabled):
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400343 """Update per-arch CROSS_COMPILE via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900344
345 The default CROSS_COMPILE values are available
346 in the CROSS_COMPILE list above.
347
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400348 You can override them via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900349 CROSS_COMPILE_{ARCH}.
350
351 For example, if you want to override toolchain prefixes
352 for ARM and PowerPC, you can do as follows in your shell:
353
354 export CROSS_COMPILE_ARM=...
355 export CROSS_COMPILE_POWERPC=...
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900356
357 Then, this function checks if specified compilers really exist in your
358 PATH environment.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900359 """
360 archs = []
361
362 for arch in os.listdir('arch'):
363 if os.path.exists(os.path.join('arch', arch, 'Makefile')):
364 archs.append(arch)
365
366 # arm64 is a special case
367 archs.append('aarch64')
368
369 for arch in archs:
370 env = 'CROSS_COMPILE_' + arch.upper()
371 cross_compile = os.environ.get(env)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900372 if not cross_compile:
373 cross_compile = CROSS_COMPILE.get(arch, '')
374
375 for path in os.environ["PATH"].split(os.pathsep):
376 gcc_path = os.path.join(path, cross_compile + 'gcc')
377 if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
378 break
379 else:
380 print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
381 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
382 % (cross_compile, arch))
383 cross_compile = None
384
385 CROSS_COMPILE[arch] = cross_compile
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900386
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900387def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
388 extend_post):
389 """Extend matched lines if desired patterns are found before/after already
390 matched lines.
391
392 Arguments:
393 lines: A list of lines handled.
394 matched: A list of line numbers that have been already matched.
395 (will be updated by this function)
396 pre_patterns: A list of regular expression that should be matched as
397 preamble.
398 post_patterns: A list of regular expression that should be matched as
399 postamble.
400 extend_pre: Add the line number of matched preamble to the matched list.
401 extend_post: Add the line number of matched postamble to the matched list.
402 """
403 extended_matched = []
404
405 j = matched[0]
406
407 for i in matched:
408 if i == 0 or i < j:
409 continue
410 j = i
411 while j in matched:
412 j += 1
413 if j >= len(lines):
414 break
415
416 for p in pre_patterns:
417 if p.search(lines[i - 1]):
418 break
419 else:
420 # not matched
421 continue
422
423 for p in post_patterns:
424 if p.search(lines[j]):
425 break
426 else:
427 # not matched
428 continue
429
430 if extend_pre:
431 extended_matched.append(i - 1)
432 if extend_post:
433 extended_matched.append(j)
434
435 matched += extended_matched
436 matched.sort()
437
Chris Packham85edfc12017-05-02 21:30:46 +1200438def confirm(options, prompt):
439 if not options.yes:
440 while True:
441 choice = raw_input('{} [y/n]: '.format(prompt))
442 choice = choice.lower()
443 print choice
444 if choice == 'y' or choice == 'n':
445 break
446
447 if choice == 'n':
448 return False
449
450 return True
451
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900452def cleanup_one_header(header_path, patterns, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900453 """Clean regex-matched lines away from a file.
454
455 Arguments:
456 header_path: path to the cleaned file.
457 patterns: list of regex patterns. Any lines matching to these
458 patterns are deleted.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900459 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900460 """
461 with open(header_path) as f:
462 lines = f.readlines()
463
464 matched = []
465 for i, line in enumerate(lines):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900466 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
467 matched.append(i)
468 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900469 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900470 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900471 matched.append(i)
472 break
473
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900474 if not matched:
475 return
476
477 # remove empty #ifdef ... #endif, successive blank lines
478 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
479 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
480 pattern_endif = re.compile(r'#\s*endif\W') # #endif
481 pattern_blank = re.compile(r'^\s*$') # empty line
482
483 while True:
484 old_matched = copy.copy(matched)
485 extend_matched_lines(lines, matched, [pattern_if],
486 [pattern_endif], True, True)
487 extend_matched_lines(lines, matched, [pattern_elif],
488 [pattern_elif, pattern_endif], True, False)
489 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
490 [pattern_blank], False, True)
491 extend_matched_lines(lines, matched, [pattern_blank],
492 [pattern_elif, pattern_endif], True, False)
493 extend_matched_lines(lines, matched, [pattern_blank],
494 [pattern_blank], True, False)
495 if matched == old_matched:
496 break
497
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900498 tolines = copy.copy(lines)
499
500 for i in reversed(matched):
501 tolines.pop(i)
502
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900503 show_diff(lines, tolines, header_path, options.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900504
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900505 if options.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900506 return
507
508 with open(header_path, 'w') as f:
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900509 for line in tolines:
510 f.write(line)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900511
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900512def cleanup_headers(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900513 """Delete config defines from board headers.
514
515 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900516 configs: A list of CONFIGs to remove.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900517 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900518 """
Chris Packham85edfc12017-05-02 21:30:46 +1200519 if not confirm(options, 'Clean up headers?'):
520 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900521
522 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900523 for config in configs:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900524 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
525 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
526
Joe Hershberger60727f52015-05-19 13:21:21 -0500527 for dir in 'include', 'arch', 'board':
528 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900529 if dirpath == os.path.join('include', 'generated'):
530 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500531 for filename in filenames:
532 if not fnmatch.fnmatch(filename, '*~'):
533 cleanup_one_header(os.path.join(dirpath, filename),
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900534 patterns, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900535
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900536def cleanup_one_extra_option(defconfig_path, configs, options):
537 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
538
539 Arguments:
540 defconfig_path: path to the cleaned defconfig file.
541 configs: A list of CONFIGs to remove.
542 options: option flags.
543 """
544
545 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
546 end = '"\n'
547
548 with open(defconfig_path) as f:
549 lines = f.readlines()
550
551 for i, line in enumerate(lines):
552 if line.startswith(start) and line.endswith(end):
553 break
554 else:
555 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
556 return
557
558 old_tokens = line[len(start):-len(end)].split(',')
559 new_tokens = []
560
561 for token in old_tokens:
562 pos = token.find('=')
563 if not (token[:pos] if pos >= 0 else token) in configs:
564 new_tokens.append(token)
565
566 if new_tokens == old_tokens:
567 return
568
569 tolines = copy.copy(lines)
570
571 if new_tokens:
572 tolines[i] = start + ','.join(new_tokens) + end
573 else:
574 tolines.pop(i)
575
576 show_diff(lines, tolines, defconfig_path, options.color)
577
578 if options.dry_run:
579 return
580
581 with open(defconfig_path, 'w') as f:
582 for line in tolines:
583 f.write(line)
584
585def cleanup_extra_options(configs, options):
586 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
587
588 Arguments:
589 configs: A list of CONFIGs to remove.
590 options: option flags.
591 """
Chris Packham85edfc12017-05-02 21:30:46 +1200592 if not confirm(options, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
593 return
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900594
595 configs = [ config[len('CONFIG_'):] for config in configs ]
596
597 defconfigs = get_all_defconfigs()
598
599 for defconfig in defconfigs:
600 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
601 options)
602
Chris Packhamca438342017-05-02 21:30:47 +1200603def cleanup_whitelist(configs, options):
604 """Delete config whitelist entries
605
606 Arguments:
607 configs: A list of CONFIGs to remove.
608 options: option flags.
609 """
610 if not confirm(options, 'Clean up whitelist entries?'):
611 return
612
613 with open(os.path.join('scripts', 'config_whitelist.txt')) as f:
614 lines = f.readlines()
615
616 lines = [x for x in lines if x.strip() not in configs]
617
618 with open(os.path.join('scripts', 'config_whitelist.txt'), 'w') as f:
619 f.write(''.join(lines))
620
621
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900622### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900623class Progress:
624
625 """Progress Indicator"""
626
627 def __init__(self, total):
628 """Create a new progress indicator.
629
630 Arguments:
631 total: A number of defconfig files to process.
632 """
633 self.current = 0
634 self.total = total
635
636 def inc(self):
637 """Increment the number of processed defconfig files."""
638
639 self.current += 1
640
641 def show(self):
642 """Display the progress."""
643 print ' %d defconfigs out of %d\r' % (self.current, self.total),
644 sys.stdout.flush()
645
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900646class KconfigParser:
647
648 """A parser of .config and include/autoconf.mk."""
649
650 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
651 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
652
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900653 def __init__(self, configs, options, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900654 """Create a new parser.
655
656 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900657 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900658 options: option flags.
659 build_dir: Build directory.
660 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900661 self.configs = configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900662 self.options = options
Masahiro Yamada1f169922016-05-19 15:52:00 +0900663 self.dotconfig = os.path.join(build_dir, '.config')
664 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900665 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
666 'autoconf.mk')
Masahiro Yamada1f169922016-05-19 15:52:00 +0900667 self.config_autoconf = os.path.join(build_dir, 'include', 'config',
668 'auto.conf')
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900669 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900670
671 def get_cross_compile(self):
672 """Parse .config file and return CROSS_COMPILE.
673
674 Returns:
675 A string storing the compiler prefix for the architecture.
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900676 Return a NULL string for architectures that do not require
677 compiler prefix (Sandbox and native build is the case).
678 Return None if the specified compiler is missing in your PATH.
679 Caller should distinguish '' and None.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900680 """
681 arch = ''
682 cpu = ''
Masahiro Yamada1f169922016-05-19 15:52:00 +0900683 for line in open(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900684 m = self.re_arch.match(line)
685 if m:
686 arch = m.group(1)
687 continue
688 m = self.re_cpu.match(line)
689 if m:
690 cpu = m.group(1)
691
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900692 if not arch:
693 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900694
695 # fix-up for aarch64
696 if arch == 'arm' and cpu == 'armv8':
697 arch = 'aarch64'
698
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900699 return CROSS_COMPILE.get(arch, None)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900700
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900701 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900702 """Parse .config, defconfig, include/autoconf.mk for one config.
703
704 This function looks for the config options in the lines from
705 defconfig, .config, and include/autoconf.mk in order to decide
706 which action should be taken for this defconfig.
707
708 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900709 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900710 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900711 autoconf_lines: lines from the include/autoconf.mk file.
712
713 Returns:
714 A tupple of the action for this defconfig and the line
715 matched for the config.
716 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900717 not_set = '# %s is not set' % config
718
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900719 for line in autoconf_lines:
720 line = line.rstrip()
721 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900722 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900723 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900724 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900725 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900726
Masahiro Yamada916224c2016-08-22 22:18:21 +0900727 for line in dotconfig_lines:
728 line = line.rstrip()
729 if line.startswith(config + '=') or line == not_set:
730 old_val = line
731 break
732 else:
733 if new_val == not_set:
734 return (ACTION_NO_ENTRY, config)
735 else:
736 return (ACTION_NO_ENTRY_WARN, config)
737
Masahiro Yamadacc008292016-05-19 15:51:56 +0900738 # If this CONFIG is neither bool nor trisate
739 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
740 # tools/scripts/define2mk.sed changes '1' to 'y'.
741 # This is a problem if the CONFIG is int type.
742 # Check the type in Kconfig and handle it correctly.
743 if new_val[-2:] == '=y':
744 new_val = new_val[:-1] + '1'
745
Masahiro Yamada50301592016-06-15 14:33:50 +0900746 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
747 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900748
Masahiro Yamada1d085562016-05-19 15:52:02 +0900749 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900750 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900751
Masahiro Yamadacc008292016-05-19 15:51:56 +0900752 This function parses the generated .config and include/autoconf.mk
753 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900754 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900755
756 Arguments:
757 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900758
759 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900760 Return a tuple of (updated flag, log string).
761 The "updated flag" is True if the .config was updated, False
762 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900763 """
764
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900765 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900766 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900767 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900768 rm_files = [self.config_autoconf, self.autoconf]
769
770 if self.options.spl:
771 if os.path.exists(self.spl_autoconf):
772 autoconf_path = self.spl_autoconf
773 rm_files.append(self.spl_autoconf)
774 else:
775 for f in rm_files:
776 os.remove(f)
777 return (updated, suspicious,
778 color_text(self.options.color, COLOR_BROWN,
779 "SPL is not enabled. Skipped.") + '\n')
780 else:
781 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900782
Masahiro Yamada1f169922016-05-19 15:52:00 +0900783 with open(self.dotconfig) as f:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900784 dotconfig_lines = f.readlines()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900785
Masahiro Yamada07913d12016-08-22 22:18:22 +0900786 with open(autoconf_path) as f:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900787 autoconf_lines = f.readlines()
788
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900789 for config in self.configs:
790 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500791 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900792 results.append(result)
793
794 log = ''
795
796 for (action, value) in results:
797 if action == ACTION_MOVE:
798 actlog = "Move '%s'" % value
799 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900800 elif action == ACTION_NO_ENTRY:
801 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900802 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900803 elif action == ACTION_NO_ENTRY_WARN:
804 actlog = "%s is not defined in Kconfig (suspicious). Do nothing." % value
805 log_color = COLOR_YELLOW
806 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900807 elif action == ACTION_NO_CHANGE:
808 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
809 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900810 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada07913d12016-08-22 22:18:22 +0900811 elif action == ACTION_SPL_NOT_EXIST:
812 actlog = "SPL is not enabled for this defconfig. Skip."
813 log_color = COLOR_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900814 else:
815 sys.exit("Internal Error. This should not happen.")
816
Masahiro Yamada1d085562016-05-19 15:52:02 +0900817 log += color_text(self.options.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900818
Masahiro Yamada1f169922016-05-19 15:52:00 +0900819 with open(self.dotconfig, 'a') as f:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900820 for (action, value) in results:
821 if action == ACTION_MOVE:
822 f.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900823 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900824
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900825 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900826 for f in rm_files:
827 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900828
Masahiro Yamada916224c2016-08-22 22:18:21 +0900829 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900830
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900831 def check_defconfig(self):
832 """Check the defconfig after savedefconfig
833
834 Returns:
835 Return additional log if moved CONFIGs were removed again by
836 'make savedefconfig'.
837 """
838
839 log = ''
840
841 with open(self.defconfig) as f:
842 defconfig_lines = f.readlines()
843
844 for (action, value) in self.results:
845 if action != ACTION_MOVE:
846 continue
847 if not value + '\n' in defconfig_lines:
848 log += color_text(self.options.color, COLOR_YELLOW,
849 "'%s' was removed by savedefconfig.\n" %
850 value)
851
852 return log
853
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900854class Slot:
855
856 """A slot to store a subprocess.
857
858 Each instance of this class handles one subprocess.
859 This class is useful to control multiple threads
860 for faster processing.
861 """
862
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500863 def __init__(self, configs, options, progress, devnull, make_cmd, reference_src_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900864 """Create a new process slot.
865
866 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900867 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900868 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900869 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900870 devnull: A file object of '/dev/null'.
871 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500872 reference_src_dir: Determine the true starting config state from this
873 source tree.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900874 """
875 self.options = options
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900876 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900877 self.build_dir = tempfile.mkdtemp()
878 self.devnull = devnull
879 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500880 self.reference_src_dir = reference_src_dir
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900881 self.parser = KconfigParser(configs, options, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900882 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900883 self.failed_boards = set()
884 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900885
886 def __del__(self):
887 """Delete the working directory
888
889 This function makes sure the temporary directory is cleaned away
890 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500891 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900892 instance of the class gets unreferenced.
893
894 If the subprocess is still running, wait until it finishes.
895 """
896 if self.state != STATE_IDLE:
897 while self.ps.poll() == None:
898 pass
899 shutil.rmtree(self.build_dir)
900
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900901 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900902 """Assign a new subprocess for defconfig and add it to the slot.
903
904 If the slot is vacant, create a new subprocess for processing the
905 given defconfig and add it to the slot. Just returns False if
906 the slot is occupied (i.e. the current subprocess is still running).
907
908 Arguments:
909 defconfig: defconfig name.
910
911 Returns:
912 Return True on success or False on failure
913 """
914 if self.state != STATE_IDLE:
915 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900916
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900917 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900918 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900919 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900920 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900921 return True
922
923 def poll(self):
924 """Check the status of the subprocess and handle it as needed.
925
926 Returns True if the slot is vacant (i.e. in idle state).
927 If the configuration is successfully finished, assign a new
928 subprocess to build include/autoconf.mk.
929 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900930 parse the .config and the include/autoconf.mk, moving
931 config options to the .config as needed.
932 If the .config was updated, run "make savedefconfig" to sync
933 it, update the original defconfig, and then set the slot back
934 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900935
936 Returns:
937 Return True if the subprocess is terminated, False otherwise
938 """
939 if self.state == STATE_IDLE:
940 return True
941
942 if self.ps.poll() == None:
943 return False
944
945 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900946 self.handle_error()
947 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900948 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500949 self.do_savedefconfig()
950 else:
951 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900952 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900953 if self.current_src_dir:
954 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500955 self.do_defconfig()
956 else:
957 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900958 elif self.state == STATE_SAVEDEFCONFIG:
959 self.update_defconfig()
960 else:
961 sys.exit("Internal Error. This should not happen.")
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900962
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900963 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500964
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900965 def handle_error(self):
966 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900967
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900968 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
969 "Failed to process.\n")
970 if self.options.verbose:
971 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
972 self.ps.stderr.read())
973 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500974
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900975 def do_defconfig(self):
976 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900977
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900978 cmd = list(self.make_cmd)
979 cmd.append(self.defconfig)
980 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900981 stderr=subprocess.PIPE,
982 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900983 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900984
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900985 def do_autoconf(self):
986 """Run 'make include/config/auto.conf'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900987
Joe Hershberger25400092015-05-19 13:21:23 -0500988 self.cross_compile = self.parser.get_cross_compile()
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900989 if self.cross_compile is None:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900990 self.log += color_text(self.options.color, COLOR_YELLOW,
991 "Compiler is missing. Do nothing.\n")
Masahiro Yamada4efef992016-05-19 15:52:03 +0900992 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900993 return
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900994
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900995 cmd = list(self.make_cmd)
Joe Hershberger25400092015-05-19 13:21:23 -0500996 if self.cross_compile:
997 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
Joe Hershberger7740f652015-05-19 13:21:18 -0500998 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900999 cmd.append('include/config/auto.conf')
Joe Hershberger25400092015-05-19 13:21:23 -05001000 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001001 stderr=subprocess.PIPE,
1002 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001003 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001004
1005 def do_savedefconfig(self):
1006 """Update the .config and run 'make savedefconfig'."""
1007
Masahiro Yamada916224c2016-08-22 22:18:21 +09001008 (updated, suspicious, log) = self.parser.update_dotconfig()
1009 if suspicious:
1010 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001011 self.log += log
1012
1013 if not self.options.force_sync and not updated:
1014 self.finish(True)
1015 return
1016 if updated:
1017 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
1018 "Syncing by savedefconfig...\n")
1019 else:
1020 self.log += "Syncing by savedefconfig (forced by option)...\n"
1021
1022 cmd = list(self.make_cmd)
1023 cmd.append('savedefconfig')
1024 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1025 stderr=subprocess.PIPE)
1026 self.state = STATE_SAVEDEFCONFIG
1027
1028 def update_defconfig(self):
1029 """Update the input defconfig and go back to the idle state."""
1030
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001031 log = self.parser.check_defconfig()
1032 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001033 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001034 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001035 orig_defconfig = os.path.join('configs', self.defconfig)
1036 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1037 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1038
1039 if updated:
Joe Hershberger06cc1d32016-06-10 14:53:30 -05001040 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001041 "defconfig was updated.\n")
1042
1043 if not self.options.dry_run and updated:
1044 shutil.move(new_defconfig, orig_defconfig)
1045 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001046
Masahiro Yamada4efef992016-05-19 15:52:03 +09001047 def finish(self, success):
1048 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001049
1050 Arguments:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001051 success: Should be True when the defconfig was processed
1052 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001053 """
1054 # output at least 30 characters to hide the "* defconfigs out of *".
1055 log = self.defconfig.ljust(30) + '\n'
1056
1057 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1058 # Some threads are running in parallel.
1059 # Print log atomically to not mix up logs from different threads.
Masahiro Yamada4efef992016-05-19 15:52:03 +09001060 print >> (sys.stdout if success else sys.stderr), log
1061
1062 if not success:
1063 if self.options.exit_on_error:
1064 sys.exit("Exit on error.")
1065 # If --exit-on-error flag is not set, skip this board and continue.
1066 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001067 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001068
Masahiro Yamada1d085562016-05-19 15:52:02 +09001069 self.progress.inc()
1070 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001071 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001072
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001073 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001074 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001075 """
1076 return self.failed_boards
1077
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001078 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001079 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001080 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001081 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001082
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001083class Slots:
1084
1085 """Controller of the array of subprocess slots."""
1086
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001087 def __init__(self, configs, options, progress, reference_src_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001088 """Create a new slots controller.
1089
1090 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001091 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001092 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001093 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001094 reference_src_dir: Determine the true starting config state from this
1095 source tree.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001096 """
1097 self.options = options
1098 self.slots = []
1099 devnull = get_devnull()
1100 make_cmd = get_make_cmd()
1101 for i in range(options.jobs):
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001102 self.slots.append(Slot(configs, options, progress, devnull,
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001103 make_cmd, reference_src_dir))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001104
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001105 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001106 """Add a new subprocess if a vacant slot is found.
1107
1108 Arguments:
1109 defconfig: defconfig name to be put into.
1110
1111 Returns:
1112 Return True on success or False on failure
1113 """
1114 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001115 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001116 return True
1117 return False
1118
1119 def available(self):
1120 """Check if there is a vacant slot.
1121
1122 Returns:
1123 Return True if at lease one vacant slot is found, False otherwise.
1124 """
1125 for slot in self.slots:
1126 if slot.poll():
1127 return True
1128 return False
1129
1130 def empty(self):
1131 """Check if all slots are vacant.
1132
1133 Returns:
1134 Return True if all the slots are vacant, False otherwise.
1135 """
1136 ret = True
1137 for slot in self.slots:
1138 if not slot.poll():
1139 ret = False
1140 return ret
1141
1142 def show_failed_boards(self):
1143 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001144 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001145 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001146
1147 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001148 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001149
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001150 if boards:
1151 boards = '\n'.join(boards) + '\n'
1152 msg = "The following boards were not processed due to error:\n"
1153 msg += boards
1154 msg += "(the list has been saved in %s)\n" % output_file
1155 print >> sys.stderr, color_text(self.options.color, COLOR_LIGHT_RED,
1156 msg)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001157
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001158 with open(output_file, 'w') as f:
1159 f.write(boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001160
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001161 def show_suspicious_boards(self):
1162 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001163 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001164 output_file = 'moveconfig.suspicious'
1165
1166 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001167 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001168
1169 if boards:
1170 boards = '\n'.join(boards) + '\n'
1171 msg = "The following boards might have been converted incorrectly.\n"
1172 msg += "It is highly recommended to check them manually:\n"
1173 msg += boards
1174 msg += "(the list has been saved in %s)\n" % output_file
1175 print >> sys.stderr, color_text(self.options.color, COLOR_YELLOW,
1176 msg)
1177
1178 with open(output_file, 'w') as f:
1179 f.write(boards)
1180
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001181class ReferenceSource:
1182
1183 """Reference source against which original configs should be parsed."""
1184
1185 def __init__(self, commit):
1186 """Create a reference source directory based on a specified commit.
1187
1188 Arguments:
1189 commit: commit to git-clone
1190 """
1191 self.src_dir = tempfile.mkdtemp()
1192 print "Cloning git repo to a separate work directory..."
1193 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1194 cwd=self.src_dir)
1195 print "Checkout '%s' to build the original autoconf.mk." % \
1196 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip()
1197 subprocess.check_output(['git', 'checkout', commit],
1198 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001199
1200 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001201 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001202
1203 This function makes sure the temporary directory is cleaned away
1204 even if Python suddenly dies due to error. It should be done in here
1205 because it is guaranteed the destructor is always invoked when the
1206 instance of the class gets unreferenced.
1207 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001208 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001209
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001210 def get_dir(self):
1211 """Return the absolute path to the reference source directory."""
1212
1213 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001214
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001215def move_config(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001216 """Move config options to defconfig files.
1217
1218 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001219 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001220 options: option flags
1221 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001222 if len(configs) == 0:
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001223 if options.force_sync:
1224 print 'No CONFIG is specified. You are probably syncing defconfigs.',
1225 else:
1226 print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
1227 else:
1228 print 'Move ' + ', '.join(configs),
1229 print '(jobs: %d)\n' % options.jobs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001230
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001231 if options.git_ref:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001232 reference_src = ReferenceSource(options.git_ref)
1233 reference_src_dir = reference_src.get_dir()
1234 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001235 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001236
Joe Hershberger91040e82015-05-19 13:21:19 -05001237 if options.defconfigs:
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +09001238 defconfigs = get_matched_defconfigs(options.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001239 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001240 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001241
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001242 progress = Progress(len(defconfigs))
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001243 slots = Slots(configs, options, progress, reference_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001244
1245 # Main loop to process defconfig files:
1246 # Add a new subprocess into a vacant slot.
1247 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001248 for defconfig in defconfigs:
1249 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001250 while not slots.available():
1251 # No available slot: sleep for a while
1252 time.sleep(SLEEP_TIME)
1253
1254 # wait until all the subprocesses finish
1255 while not slots.empty():
1256 time.sleep(SLEEP_TIME)
1257
Joe Hershberger2e2ce6c2015-05-19 13:21:25 -05001258 print ''
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001259 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001260 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001261
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001262def main():
1263 try:
1264 cpu_count = multiprocessing.cpu_count()
1265 except NotImplementedError:
1266 cpu_count = 1
1267
1268 parser = optparse.OptionParser()
1269 # Add options here
1270 parser.add_option('-c', '--color', action='store_true', default=False,
1271 help='display the log in color')
Simon Glass9ede2122016-09-12 23:18:21 -06001272 parser.add_option('-C', '--commit', action='store_true', default=False,
1273 help='Create a git commit for the operation')
Joe Hershberger91040e82015-05-19 13:21:19 -05001274 parser.add_option('-d', '--defconfigs', type='string',
1275 help='a file containing a list of defconfigs to move')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001276 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1277 help='perform a trial run (show log with no changes)')
1278 parser.add_option('-e', '--exit-on-error', action='store_true',
1279 default=False,
1280 help='exit immediately on any error')
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001281 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1282 help='force sync by savedefconfig')
Masahiro Yamada07913d12016-08-22 22:18:22 +09001283 parser.add_option('-S', '--spl', action='store_true', default=False,
1284 help='parse config options defined for SPL build')
Joe Hershberger2144f882015-05-19 13:21:20 -05001285 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1286 action='store_true', default=False,
1287 help='only cleanup the headers')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001288 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1289 help='the number of jobs to run simultaneously')
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001290 parser.add_option('-r', '--git-ref', type='string',
1291 help='the git ref to clone for building the autoconf.mk')
Simon Glass6b403df2016-09-12 23:18:20 -06001292 parser.add_option('-y', '--yes', action='store_true', default=False,
1293 help="respond 'yes' to any prompts")
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001294 parser.add_option('-v', '--verbose', action='store_true', default=False,
1295 help='show any build errors as boards are built')
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001296 parser.usage += ' CONFIG ...'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001297
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001298 (options, configs) = parser.parse_args()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001299
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001300 if len(configs) == 0 and not options.force_sync:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001301 parser.print_usage()
1302 sys.exit(1)
1303
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001304 # prefix the option name with CONFIG_ if missing
1305 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1306 for config in configs ]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001307
Joe Hershberger2144f882015-05-19 13:21:20 -05001308 check_top_directory()
1309
1310 if not options.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001311 check_clean_directory()
1312 update_cross_compile(options.color)
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001313 move_config(configs, options)
Joe Hershberger2144f882015-05-19 13:21:20 -05001314
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001315 if configs:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +09001316 cleanup_headers(configs, options)
Masahiro Yamada9ab02962016-07-25 19:15:29 +09001317 cleanup_extra_options(configs, options)
Chris Packhamca438342017-05-02 21:30:47 +12001318 cleanup_whitelist(configs, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001319
Simon Glass9ede2122016-09-12 23:18:21 -06001320 if options.commit:
1321 subprocess.call(['git', 'add', '-u'])
1322 if configs:
1323 msg = 'Convert %s %sto Kconfig' % (configs[0],
1324 'et al ' if len(configs) > 1 else '')
1325 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1326 '\n '.join(configs))
1327 else:
1328 msg = 'configs: Resync with savedefconfig'
1329 msg += '\n\nRsync all defconfig files using moveconfig.py'
1330 subprocess.call(['git', 'commit', '-s', '-m', msg])
1331
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001332if __name__ == '__main__':
1333 main()