blob: 390aac6f74edee8171e9c5df815f4956ee048cd7 [file] [log] [blame]
Masahiro Yamadab6160812015-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 Yamadab903c4e2016-05-19 15:51:58 +090020First, you must edit the Kconfig to add the menu entries for the configs
Joe Hershberger166edec2015-05-19 13:21:17 -050021you are moving.
22
Masahiro Yamadab903c4e2016-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 Yamadab6160812015-05-20 11:36:07 +090026
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090027 $ tools/moveconfig.py CONFIG_CMD_USB CONFIG_SYS_TEXT_BASE
Masahiro Yamadab6160812015-05-20 11:36:07 +090028
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090029The tool walks through all the defconfig files and move the given CONFIGs.
Masahiro Yamadab6160812015-05-20 11:36:07 +090030
31The log is also displayed on the terminal.
32
Masahiro Yamada465b7c02016-05-19 15:52:02 +090033The log is printed for each defconfig as follows:
Masahiro Yamadab6160812015-05-20 11:36:07 +090034
Masahiro Yamada465b7c02016-05-19 15:52:02 +090035<defconfig_name>
36 <action1>
37 <action2>
38 <action3>
39 ...
Masahiro Yamadab6160812015-05-20 11:36:07 +090040
Masahiro Yamada465b7c02016-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 Yamada7facf882016-08-21 16:12:36 +090044It looks like one of the following:
Masahiro Yamadab6160812015-05-20 11:36:07 +090045
46 - Move 'CONFIG_... '
47 This config option was moved to the defconfig
48
Masahiro Yamada5643d6e2016-05-19 15:51:56 +090049 - CONFIG_... is not defined in Kconfig. Do nothing.
Masahiro Yamada35204d92016-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 Yamada5643d6e2016-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 Yamada35204d92016-08-22 22:18:21 +090060 The tool does not know if the result is reasonable, so please check it
61 manually.
Masahiro Yamadab6160812015-05-20 11:36:07 +090062
Masahiro Yamada5643d6e2016-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 Yamadab6160812015-05-20 11:36:07 +090066
Masahiro Yamadac4d76eb2016-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 Yamadab6160812015-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 Yamadab903c4e2016-05-19 15:51:58 +090084How does it work?
85-----------------
Masahiro Yamadab6160812015-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 Yamadab903c4e2016-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 Yamadab6160812015-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 Glass8bf41c22016-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 Hershbergerc6e043a2015-05-19 13:21:19 -0500129 -d, --defconfigs
Masahiro Yamada3984d6e2016-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 Hershbergerc6e043a2015-05-19 13:21:19 -0500132
Masahiro Yamadab6160812015-05-20 11:36:07 +0900133 -n, --dry-run
Masahiro Yamadab903c4e2016-05-19 15:51:58 +0900134 Perform a trial run that does not make any changes. It is useful to
Masahiro Yamadab6160812015-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 Yamada83c17672016-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 Yamada6d139172016-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 Hershberger23475932015-05-19 13:21:20 -0500152 -H, --headers-only
153 Only cleanup the headers; skip the defconfig processing
154
Masahiro Yamadab6160812015-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 Hershbergerb1a570f2016-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 Hershberger808b63f2015-05-19 13:21:24 -0500167 -v, --verbose
168 Show any build errors as boards are built
169
Simon Glass13e05a02016-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 Yamadab6160812015-05-20 11:36:07 +0900174To see the complete list of supported options, run
175
176 $ tools/moveconfig.py -h
177
178"""
179
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900180import copy
Masahiro Yamada573b3902016-07-25 19:15:25 +0900181import difflib
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900182import filecmp
Masahiro Yamadab6160812015-05-20 11:36:07 +0900183import fnmatch
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900184import glob
Masahiro Yamadab6160812015-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 Yamada7facf882016-08-21 16:12:36 +0900200# (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the following:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900201# arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
Bin Meng3bb02f62015-09-25 01:22:39 -0700202# nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
Masahiro Yamadab6160812015-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 Yamadab6160812015-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 Yamadab6160812015-05-20 11:36:07 +0900214 'powerpc': 'powerpc-linux-',
215 'sh': 'sh-linux-gnu-',
Masahiro Yamadad1d9d602016-08-21 16:03:08 +0900216 'x86': 'i386-linux-',
217 'xtensa': 'xtensa-linux-'
Masahiro Yamadab6160812015-05-20 11:36:07 +0900218}
219
220STATE_IDLE = 0
221STATE_DEFCONFIG = 1
222STATE_AUTOCONF = 2
Joe Hershberger166edec2015-05-19 13:21:17 -0500223STATE_SAVEDEFCONFIG = 3
Masahiro Yamadab6160812015-05-20 11:36:07 +0900224
225ACTION_MOVE = 0
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900226ACTION_NO_ENTRY = 1
Masahiro Yamada35204d92016-08-22 22:18:21 +0900227ACTION_NO_ENTRY_WARN = 2
228ACTION_NO_CHANGE = 3
Masahiro Yamadab6160812015-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 Yamada990e6772016-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 Yamadab6160812015-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 Glass18774bc2017-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 Yamada3984d6e2016-10-19 14:39:54 +0900299def get_matched_defconfigs(defconfigs_file):
300 """Get all the defconfig files that match the patterns in a file."""
301 defconfigs = []
302 for i, line in enumerate(open(defconfigs_file)):
303 line = line.strip()
304 if not line:
305 continue # skip blank lines silently
Simon Glass18774bc2017-06-01 19:38:58 -0600306 matched = get_matched_defconfig(line)
Masahiro Yamada3984d6e2016-10-19 14:39:54 +0900307 if not matched:
308 print >> sys.stderr, "warning: %s:%d: no defconfig matched '%s'" % \
309 (defconfigs_file, i + 1, line)
310
311 defconfigs += matched
312
313 # use set() to drop multiple matching
314 return [ defconfig[len('configs') + 1:] for defconfig in set(defconfigs) ]
315
Masahiro Yamada58175e32016-07-25 19:15:28 +0900316def get_all_defconfigs():
317 """Get all the defconfig files under the configs/ directory."""
318 defconfigs = []
319 for (dirpath, dirnames, filenames) in os.walk('configs'):
320 dirpath = dirpath[len('configs') + 1:]
321 for filename in fnmatch.filter(filenames, '*_defconfig'):
322 defconfigs.append(os.path.join(dirpath, filename))
323
324 return defconfigs
325
Masahiro Yamadab6160812015-05-20 11:36:07 +0900326def color_text(color_enabled, color, string):
327 """Return colored string."""
328 if color_enabled:
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900329 # LF should not be surrounded by the escape sequence.
330 # Otherwise, additional whitespace or line-feed might be printed.
331 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
332 for s in string.split('\n') ])
Masahiro Yamadab6160812015-05-20 11:36:07 +0900333 else:
334 return string
335
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900336def show_diff(a, b, file_path, color_enabled):
Masahiro Yamada573b3902016-07-25 19:15:25 +0900337 """Show unidified diff.
338
339 Arguments:
340 a: A list of lines (before)
341 b: A list of lines (after)
342 file_path: Path to the file
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900343 color_enabled: Display the diff in color
Masahiro Yamada573b3902016-07-25 19:15:25 +0900344 """
345
346 diff = difflib.unified_diff(a, b,
347 fromfile=os.path.join('a', file_path),
348 tofile=os.path.join('b', file_path))
349
350 for line in diff:
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900351 if line[0] == '-' and line[1] != '-':
352 print color_text(color_enabled, COLOR_RED, line),
353 elif line[0] == '+' and line[1] != '+':
354 print color_text(color_enabled, COLOR_GREEN, line),
355 else:
356 print line,
Masahiro Yamada573b3902016-07-25 19:15:25 +0900357
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900358def update_cross_compile(color_enabled):
Robert P. J. Day8c60f922016-05-04 04:47:31 -0400359 """Update per-arch CROSS_COMPILE via environment variables
Masahiro Yamadab6160812015-05-20 11:36:07 +0900360
361 The default CROSS_COMPILE values are available
362 in the CROSS_COMPILE list above.
363
Robert P. J. Day8c60f922016-05-04 04:47:31 -0400364 You can override them via environment variables
Masahiro Yamadab6160812015-05-20 11:36:07 +0900365 CROSS_COMPILE_{ARCH}.
366
367 For example, if you want to override toolchain prefixes
368 for ARM and PowerPC, you can do as follows in your shell:
369
370 export CROSS_COMPILE_ARM=...
371 export CROSS_COMPILE_POWERPC=...
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900372
373 Then, this function checks if specified compilers really exist in your
374 PATH environment.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900375 """
376 archs = []
377
378 for arch in os.listdir('arch'):
379 if os.path.exists(os.path.join('arch', arch, 'Makefile')):
380 archs.append(arch)
381
382 # arm64 is a special case
383 archs.append('aarch64')
384
385 for arch in archs:
386 env = 'CROSS_COMPILE_' + arch.upper()
387 cross_compile = os.environ.get(env)
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900388 if not cross_compile:
389 cross_compile = CROSS_COMPILE.get(arch, '')
390
391 for path in os.environ["PATH"].split(os.pathsep):
392 gcc_path = os.path.join(path, cross_compile + 'gcc')
393 if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
394 break
395 else:
396 print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
397 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
398 % (cross_compile, arch))
399 cross_compile = None
400
401 CROSS_COMPILE[arch] = cross_compile
Masahiro Yamadab6160812015-05-20 11:36:07 +0900402
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900403def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
404 extend_post):
405 """Extend matched lines if desired patterns are found before/after already
406 matched lines.
407
408 Arguments:
409 lines: A list of lines handled.
410 matched: A list of line numbers that have been already matched.
411 (will be updated by this function)
412 pre_patterns: A list of regular expression that should be matched as
413 preamble.
414 post_patterns: A list of regular expression that should be matched as
415 postamble.
416 extend_pre: Add the line number of matched preamble to the matched list.
417 extend_post: Add the line number of matched postamble to the matched list.
418 """
419 extended_matched = []
420
421 j = matched[0]
422
423 for i in matched:
424 if i == 0 or i < j:
425 continue
426 j = i
427 while j in matched:
428 j += 1
429 if j >= len(lines):
430 break
431
432 for p in pre_patterns:
433 if p.search(lines[i - 1]):
434 break
435 else:
436 # not matched
437 continue
438
439 for p in post_patterns:
440 if p.search(lines[j]):
441 break
442 else:
443 # not matched
444 continue
445
446 if extend_pre:
447 extended_matched.append(i - 1)
448 if extend_post:
449 extended_matched.append(j)
450
451 matched += extended_matched
452 matched.sort()
453
Chris Packham85e15c52017-05-02 21:30:46 +1200454def confirm(options, prompt):
455 if not options.yes:
456 while True:
457 choice = raw_input('{} [y/n]: '.format(prompt))
458 choice = choice.lower()
459 print choice
460 if choice == 'y' or choice == 'n':
461 break
462
463 if choice == 'n':
464 return False
465
466 return True
467
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900468def cleanup_one_header(header_path, patterns, options):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900469 """Clean regex-matched lines away from a file.
470
471 Arguments:
472 header_path: path to the cleaned file.
473 patterns: list of regex patterns. Any lines matching to these
474 patterns are deleted.
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900475 options: option flags.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900476 """
477 with open(header_path) as f:
478 lines = f.readlines()
479
480 matched = []
481 for i, line in enumerate(lines):
Masahiro Yamada6d798ba2016-07-25 19:15:27 +0900482 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
483 matched.append(i)
484 continue
Masahiro Yamadab6160812015-05-20 11:36:07 +0900485 for pattern in patterns:
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900486 if pattern.search(line):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900487 matched.append(i)
488 break
489
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900490 if not matched:
491 return
492
493 # remove empty #ifdef ... #endif, successive blank lines
494 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
495 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
496 pattern_endif = re.compile(r'#\s*endif\W') # #endif
497 pattern_blank = re.compile(r'^\s*$') # empty line
498
499 while True:
500 old_matched = copy.copy(matched)
501 extend_matched_lines(lines, matched, [pattern_if],
502 [pattern_endif], True, True)
503 extend_matched_lines(lines, matched, [pattern_elif],
504 [pattern_elif, pattern_endif], True, False)
505 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
506 [pattern_blank], False, True)
507 extend_matched_lines(lines, matched, [pattern_blank],
508 [pattern_elif, pattern_endif], True, False)
509 extend_matched_lines(lines, matched, [pattern_blank],
510 [pattern_blank], True, False)
511 if matched == old_matched:
512 break
513
Masahiro Yamada573b3902016-07-25 19:15:25 +0900514 tolines = copy.copy(lines)
515
516 for i in reversed(matched):
517 tolines.pop(i)
518
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900519 show_diff(lines, tolines, header_path, options.color)
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900520
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900521 if options.dry_run:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900522 return
523
524 with open(header_path, 'w') as f:
Masahiro Yamada573b3902016-07-25 19:15:25 +0900525 for line in tolines:
526 f.write(line)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900527
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900528def cleanup_headers(configs, options):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900529 """Delete config defines from board headers.
530
531 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900532 configs: A list of CONFIGs to remove.
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900533 options: option flags.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900534 """
Chris Packham85e15c52017-05-02 21:30:46 +1200535 if not confirm(options, 'Clean up headers?'):
536 return
Masahiro Yamadab6160812015-05-20 11:36:07 +0900537
538 patterns = []
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900539 for config in configs:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900540 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
541 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
542
Joe Hershbergerb78ad422015-05-19 13:21:21 -0500543 for dir in 'include', 'arch', 'board':
544 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamada28a6d352016-07-25 19:15:22 +0900545 if dirpath == os.path.join('include', 'generated'):
546 continue
Joe Hershbergerb78ad422015-05-19 13:21:21 -0500547 for filename in filenames:
548 if not fnmatch.fnmatch(filename, '*~'):
549 cleanup_one_header(os.path.join(dirpath, filename),
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900550 patterns, options)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900551
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900552def cleanup_one_extra_option(defconfig_path, configs, options):
553 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
554
555 Arguments:
556 defconfig_path: path to the cleaned defconfig file.
557 configs: A list of CONFIGs to remove.
558 options: option flags.
559 """
560
561 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
562 end = '"\n'
563
564 with open(defconfig_path) as f:
565 lines = f.readlines()
566
567 for i, line in enumerate(lines):
568 if line.startswith(start) and line.endswith(end):
569 break
570 else:
571 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
572 return
573
574 old_tokens = line[len(start):-len(end)].split(',')
575 new_tokens = []
576
577 for token in old_tokens:
578 pos = token.find('=')
579 if not (token[:pos] if pos >= 0 else token) in configs:
580 new_tokens.append(token)
581
582 if new_tokens == old_tokens:
583 return
584
585 tolines = copy.copy(lines)
586
587 if new_tokens:
588 tolines[i] = start + ','.join(new_tokens) + end
589 else:
590 tolines.pop(i)
591
592 show_diff(lines, tolines, defconfig_path, options.color)
593
594 if options.dry_run:
595 return
596
597 with open(defconfig_path, 'w') as f:
598 for line in tolines:
599 f.write(line)
600
601def cleanup_extra_options(configs, options):
602 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
603
604 Arguments:
605 configs: A list of CONFIGs to remove.
606 options: option flags.
607 """
Chris Packham85e15c52017-05-02 21:30:46 +1200608 if not confirm(options, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
609 return
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900610
611 configs = [ config[len('CONFIG_'):] for config in configs ]
612
613 defconfigs = get_all_defconfigs()
614
615 for defconfig in defconfigs:
616 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
617 options)
618
Chris Packham9d5274f2017-05-02 21:30:47 +1200619def cleanup_whitelist(configs, options):
620 """Delete config whitelist entries
621
622 Arguments:
623 configs: A list of CONFIGs to remove.
624 options: option flags.
625 """
626 if not confirm(options, 'Clean up whitelist entries?'):
627 return
628
629 with open(os.path.join('scripts', 'config_whitelist.txt')) as f:
630 lines = f.readlines()
631
632 lines = [x for x in lines if x.strip() not in configs]
633
634 with open(os.path.join('scripts', 'config_whitelist.txt'), 'w') as f:
635 f.write(''.join(lines))
636
Chris Packham0e6deff2017-05-02 21:30:48 +1200637def find_matching(patterns, line):
638 for pat in patterns:
639 if pat.search(line):
640 return True
641 return False
642
643def cleanup_readme(configs, options):
644 """Delete config description in README
645
646 Arguments:
647 configs: A list of CONFIGs to remove.
648 options: option flags.
649 """
650 if not confirm(options, 'Clean up README?'):
651 return
652
653 patterns = []
654 for config in configs:
655 patterns.append(re.compile(r'^\s+%s' % config))
656
657 with open('README') as f:
658 lines = f.readlines()
659
660 found = False
661 newlines = []
662 for line in lines:
663 if not found:
664 found = find_matching(patterns, line)
665 if found:
666 continue
667
668 if found and re.search(r'^\s+CONFIG', line):
669 found = False
670
671 if not found:
672 newlines.append(line)
673
674 with open('README', 'w') as f:
675 f.write(''.join(newlines))
676
Chris Packham9d5274f2017-05-02 21:30:47 +1200677
Masahiro Yamadab6160812015-05-20 11:36:07 +0900678### classes ###
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900679class Progress:
680
681 """Progress Indicator"""
682
683 def __init__(self, total):
684 """Create a new progress indicator.
685
686 Arguments:
687 total: A number of defconfig files to process.
688 """
689 self.current = 0
690 self.total = total
691
692 def inc(self):
693 """Increment the number of processed defconfig files."""
694
695 self.current += 1
696
697 def show(self):
698 """Display the progress."""
699 print ' %d defconfigs out of %d\r' % (self.current, self.total),
700 sys.stdout.flush()
701
Masahiro Yamadab6160812015-05-20 11:36:07 +0900702class KconfigParser:
703
704 """A parser of .config and include/autoconf.mk."""
705
706 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
707 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
708
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900709 def __init__(self, configs, options, build_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900710 """Create a new parser.
711
712 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900713 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900714 options: option flags.
715 build_dir: Build directory.
716 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900717 self.configs = configs
Masahiro Yamadab6160812015-05-20 11:36:07 +0900718 self.options = options
Masahiro Yamada5393b612016-05-19 15:52:00 +0900719 self.dotconfig = os.path.join(build_dir, '.config')
720 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada6d139172016-08-22 22:18:22 +0900721 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
722 'autoconf.mk')
Masahiro Yamada5393b612016-05-19 15:52:00 +0900723 self.config_autoconf = os.path.join(build_dir, 'include', 'config',
724 'auto.conf')
Masahiro Yamada07f98522016-05-19 15:52:06 +0900725 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900726
727 def get_cross_compile(self):
728 """Parse .config file and return CROSS_COMPILE.
729
730 Returns:
731 A string storing the compiler prefix for the architecture.
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900732 Return a NULL string for architectures that do not require
733 compiler prefix (Sandbox and native build is the case).
734 Return None if the specified compiler is missing in your PATH.
735 Caller should distinguish '' and None.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900736 """
737 arch = ''
738 cpu = ''
Masahiro Yamada5393b612016-05-19 15:52:00 +0900739 for line in open(self.dotconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900740 m = self.re_arch.match(line)
741 if m:
742 arch = m.group(1)
743 continue
744 m = self.re_cpu.match(line)
745 if m:
746 cpu = m.group(1)
747
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900748 if not arch:
749 return None
Masahiro Yamadab6160812015-05-20 11:36:07 +0900750
751 # fix-up for aarch64
752 if arch == 'arm' and cpu == 'armv8':
753 arch = 'aarch64'
754
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900755 return CROSS_COMPILE.get(arch, None)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900756
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900757 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900758 """Parse .config, defconfig, include/autoconf.mk for one config.
759
760 This function looks for the config options in the lines from
761 defconfig, .config, and include/autoconf.mk in order to decide
762 which action should be taken for this defconfig.
763
764 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900765 config: CONFIG name to parse.
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900766 dotconfig_lines: lines from the .config file.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900767 autoconf_lines: lines from the include/autoconf.mk file.
768
769 Returns:
770 A tupple of the action for this defconfig and the line
771 matched for the config.
772 """
Masahiro Yamadab6160812015-05-20 11:36:07 +0900773 not_set = '# %s is not set' % config
774
Masahiro Yamadab6160812015-05-20 11:36:07 +0900775 for line in autoconf_lines:
776 line = line.rstrip()
777 if line.startswith(config + '='):
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900778 new_val = line
Masahiro Yamadab6160812015-05-20 11:36:07 +0900779 break
Masahiro Yamadab6160812015-05-20 11:36:07 +0900780 else:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900781 new_val = not_set
782
Masahiro Yamada35204d92016-08-22 22:18:21 +0900783 for line in dotconfig_lines:
784 line = line.rstrip()
785 if line.startswith(config + '=') or line == not_set:
786 old_val = line
787 break
788 else:
789 if new_val == not_set:
790 return (ACTION_NO_ENTRY, config)
791 else:
792 return (ACTION_NO_ENTRY_WARN, config)
793
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900794 # If this CONFIG is neither bool nor trisate
795 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
796 # tools/scripts/define2mk.sed changes '1' to 'y'.
797 # This is a problem if the CONFIG is int type.
798 # Check the type in Kconfig and handle it correctly.
799 if new_val[-2:] == '=y':
800 new_val = new_val[:-1] + '1'
801
Masahiro Yamadab48387f2016-06-15 14:33:50 +0900802 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
803 new_val)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900804
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900805 def update_dotconfig(self):
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900806 """Parse files for the config options and update the .config.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900807
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900808 This function parses the generated .config and include/autoconf.mk
809 searching the target options.
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900810 Move the config option(s) to the .config as needed.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900811
812 Arguments:
813 defconfig: defconfig name.
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900814
815 Returns:
Masahiro Yamada263d1372016-05-19 15:52:04 +0900816 Return a tuple of (updated flag, log string).
817 The "updated flag" is True if the .config was updated, False
818 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900819 """
820
Masahiro Yamadab6160812015-05-20 11:36:07 +0900821 results = []
Masahiro Yamada263d1372016-05-19 15:52:04 +0900822 updated = False
Masahiro Yamada35204d92016-08-22 22:18:21 +0900823 suspicious = False
Masahiro Yamada6d139172016-08-22 22:18:22 +0900824 rm_files = [self.config_autoconf, self.autoconf]
825
826 if self.options.spl:
827 if os.path.exists(self.spl_autoconf):
828 autoconf_path = self.spl_autoconf
829 rm_files.append(self.spl_autoconf)
830 else:
831 for f in rm_files:
832 os.remove(f)
833 return (updated, suspicious,
834 color_text(self.options.color, COLOR_BROWN,
835 "SPL is not enabled. Skipped.") + '\n')
836 else:
837 autoconf_path = self.autoconf
Masahiro Yamadab6160812015-05-20 11:36:07 +0900838
Masahiro Yamada5393b612016-05-19 15:52:00 +0900839 with open(self.dotconfig) as f:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900840 dotconfig_lines = f.readlines()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900841
Masahiro Yamada6d139172016-08-22 22:18:22 +0900842 with open(autoconf_path) as f:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900843 autoconf_lines = f.readlines()
844
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900845 for config in self.configs:
846 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger166edec2015-05-19 13:21:17 -0500847 autoconf_lines)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900848 results.append(result)
849
850 log = ''
851
852 for (action, value) in results:
853 if action == ACTION_MOVE:
854 actlog = "Move '%s'" % value
855 log_color = COLOR_LIGHT_GREEN
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900856 elif action == ACTION_NO_ENTRY:
857 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamadab6160812015-05-20 11:36:07 +0900858 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada35204d92016-08-22 22:18:21 +0900859 elif action == ACTION_NO_ENTRY_WARN:
860 actlog = "%s is not defined in Kconfig (suspicious). Do nothing." % value
861 log_color = COLOR_YELLOW
862 suspicious = True
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900863 elif action == ACTION_NO_CHANGE:
864 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
865 % value
Masahiro Yamadab6160812015-05-20 11:36:07 +0900866 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada6d139172016-08-22 22:18:22 +0900867 elif action == ACTION_SPL_NOT_EXIST:
868 actlog = "SPL is not enabled for this defconfig. Skip."
869 log_color = COLOR_PURPLE
Masahiro Yamadab6160812015-05-20 11:36:07 +0900870 else:
871 sys.exit("Internal Error. This should not happen.")
872
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900873 log += color_text(self.options.color, log_color, actlog) + '\n'
Masahiro Yamadab6160812015-05-20 11:36:07 +0900874
Masahiro Yamada5393b612016-05-19 15:52:00 +0900875 with open(self.dotconfig, 'a') as f:
Masahiro Yamada953d93b2016-05-19 15:51:49 +0900876 for (action, value) in results:
877 if action == ACTION_MOVE:
878 f.write(value + '\n')
Masahiro Yamada263d1372016-05-19 15:52:04 +0900879 updated = True
Masahiro Yamadab6160812015-05-20 11:36:07 +0900880
Masahiro Yamada07f98522016-05-19 15:52:06 +0900881 self.results = results
Masahiro Yamada6d139172016-08-22 22:18:22 +0900882 for f in rm_files:
883 os.remove(f)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900884
Masahiro Yamada35204d92016-08-22 22:18:21 +0900885 return (updated, suspicious, log)
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900886
Masahiro Yamada07f98522016-05-19 15:52:06 +0900887 def check_defconfig(self):
888 """Check the defconfig after savedefconfig
889
890 Returns:
891 Return additional log if moved CONFIGs were removed again by
892 'make savedefconfig'.
893 """
894
895 log = ''
896
897 with open(self.defconfig) as f:
898 defconfig_lines = f.readlines()
899
900 for (action, value) in self.results:
901 if action != ACTION_MOVE:
902 continue
903 if not value + '\n' in defconfig_lines:
904 log += color_text(self.options.color, COLOR_YELLOW,
905 "'%s' was removed by savedefconfig.\n" %
906 value)
907
908 return log
909
Masahiro Yamadab6160812015-05-20 11:36:07 +0900910class Slot:
911
912 """A slot to store a subprocess.
913
914 Each instance of this class handles one subprocess.
915 This class is useful to control multiple threads
916 for faster processing.
917 """
918
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500919 def __init__(self, configs, options, progress, devnull, make_cmd, reference_src_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900920 """Create a new process slot.
921
922 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900923 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900924 options: option flags.
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900925 progress: A progress indicator.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900926 devnull: A file object of '/dev/null'.
927 make_cmd: command name of GNU Make.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500928 reference_src_dir: Determine the true starting config state from this
929 source tree.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900930 """
931 self.options = options
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900932 self.progress = progress
Masahiro Yamadab6160812015-05-20 11:36:07 +0900933 self.build_dir = tempfile.mkdtemp()
934 self.devnull = devnull
935 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500936 self.reference_src_dir = reference_src_dir
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900937 self.parser = KconfigParser(configs, options, self.build_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900938 self.state = STATE_IDLE
Masahiro Yamada1271b672016-08-22 22:18:20 +0900939 self.failed_boards = set()
940 self.suspicious_boards = set()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900941
942 def __del__(self):
943 """Delete the working directory
944
945 This function makes sure the temporary directory is cleaned away
946 even if Python suddenly dies due to error. It should be done in here
Joe Hershberger640de872016-06-10 14:53:29 -0500947 because it is guaranteed the destructor is always invoked when the
Masahiro Yamadab6160812015-05-20 11:36:07 +0900948 instance of the class gets unreferenced.
949
950 If the subprocess is still running, wait until it finishes.
951 """
952 if self.state != STATE_IDLE:
953 while self.ps.poll() == None:
954 pass
955 shutil.rmtree(self.build_dir)
956
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900957 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900958 """Assign a new subprocess for defconfig and add it to the slot.
959
960 If the slot is vacant, create a new subprocess for processing the
961 given defconfig and add it to the slot. Just returns False if
962 the slot is occupied (i.e. the current subprocess is still running).
963
964 Arguments:
965 defconfig: defconfig name.
966
967 Returns:
968 Return True on success or False on failure
969 """
970 if self.state != STATE_IDLE:
971 return False
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900972
Masahiro Yamadab6160812015-05-20 11:36:07 +0900973 self.defconfig = defconfig
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900974 self.log = ''
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900975 self.current_src_dir = self.reference_src_dir
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900976 self.do_defconfig()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900977 return True
978
979 def poll(self):
980 """Check the status of the subprocess and handle it as needed.
981
982 Returns True if the slot is vacant (i.e. in idle state).
983 If the configuration is successfully finished, assign a new
984 subprocess to build include/autoconf.mk.
985 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada263d1372016-05-19 15:52:04 +0900986 parse the .config and the include/autoconf.mk, moving
987 config options to the .config as needed.
988 If the .config was updated, run "make savedefconfig" to sync
989 it, update the original defconfig, and then set the slot back
990 to the idle state.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900991
992 Returns:
993 Return True if the subprocess is terminated, False otherwise
994 """
995 if self.state == STATE_IDLE:
996 return True
997
998 if self.ps.poll() == None:
999 return False
1000
1001 if self.ps.poll() != 0:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001002 self.handle_error()
1003 elif self.state == STATE_DEFCONFIG:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001004 if self.reference_src_dir and not self.current_src_dir:
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001005 self.do_savedefconfig()
1006 else:
1007 self.do_autoconf()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001008 elif self.state == STATE_AUTOCONF:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001009 if self.current_src_dir:
1010 self.current_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001011 self.do_defconfig()
1012 else:
1013 self.do_savedefconfig()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001014 elif self.state == STATE_SAVEDEFCONFIG:
1015 self.update_defconfig()
1016 else:
1017 sys.exit("Internal Error. This should not happen.")
Masahiro Yamadab6160812015-05-20 11:36:07 +09001018
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001019 return True if self.state == STATE_IDLE else False
Joe Hershberger166edec2015-05-19 13:21:17 -05001020
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001021 def handle_error(self):
1022 """Handle error cases."""
Masahiro Yamada83c17672016-05-19 15:52:08 +09001023
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001024 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
1025 "Failed to process.\n")
1026 if self.options.verbose:
1027 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
1028 self.ps.stderr.read())
1029 self.finish(False)
Joe Hershberger166edec2015-05-19 13:21:17 -05001030
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001031 def do_defconfig(self):
1032 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamada0f6beda2016-05-19 15:52:07 +09001033
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001034 cmd = list(self.make_cmd)
1035 cmd.append(self.defconfig)
1036 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001037 stderr=subprocess.PIPE,
1038 cwd=self.current_src_dir)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001039 self.state = STATE_DEFCONFIG
Masahiro Yamada0f6beda2016-05-19 15:52:07 +09001040
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001041 def do_autoconf(self):
1042 """Run 'make include/config/auto.conf'."""
Masahiro Yamadab6160812015-05-20 11:36:07 +09001043
Joe Hershberger11b02702015-05-19 13:21:23 -05001044 self.cross_compile = self.parser.get_cross_compile()
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +09001045 if self.cross_compile is None:
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001046 self.log += color_text(self.options.color, COLOR_YELLOW,
1047 "Compiler is missing. Do nothing.\n")
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001048 self.finish(False)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001049 return
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +09001050
Masahiro Yamadab6160812015-05-20 11:36:07 +09001051 cmd = list(self.make_cmd)
Joe Hershberger11b02702015-05-19 13:21:23 -05001052 if self.cross_compile:
1053 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
Joe Hershberger765442b2015-05-19 13:21:18 -05001054 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001055 cmd.append('include/config/auto.conf')
Joe Hershberger11b02702015-05-19 13:21:23 -05001056 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001057 stderr=subprocess.PIPE,
1058 cwd=self.current_src_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001059 self.state = STATE_AUTOCONF
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001060
1061 def do_savedefconfig(self):
1062 """Update the .config and run 'make savedefconfig'."""
1063
Masahiro Yamada35204d92016-08-22 22:18:21 +09001064 (updated, suspicious, log) = self.parser.update_dotconfig()
1065 if suspicious:
1066 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001067 self.log += log
1068
1069 if not self.options.force_sync and not updated:
1070 self.finish(True)
1071 return
1072 if updated:
1073 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
1074 "Syncing by savedefconfig...\n")
1075 else:
1076 self.log += "Syncing by savedefconfig (forced by option)...\n"
1077
1078 cmd = list(self.make_cmd)
1079 cmd.append('savedefconfig')
1080 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1081 stderr=subprocess.PIPE)
1082 self.state = STATE_SAVEDEFCONFIG
1083
1084 def update_defconfig(self):
1085 """Update the input defconfig and go back to the idle state."""
1086
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001087 log = self.parser.check_defconfig()
1088 if log:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001089 self.suspicious_boards.add(self.defconfig)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001090 self.log += log
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001091 orig_defconfig = os.path.join('configs', self.defconfig)
1092 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1093 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1094
1095 if updated:
Joe Hershberger93f1c2d2016-06-10 14:53:30 -05001096 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
Masahiro Yamadacb256cb2016-06-08 11:47:37 +09001097 "defconfig was updated.\n")
1098
1099 if not self.options.dry_run and updated:
1100 shutil.move(new_defconfig, orig_defconfig)
1101 self.finish(True)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001102
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001103 def finish(self, success):
1104 """Display log along with progress and go to the idle state.
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001105
1106 Arguments:
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001107 success: Should be True when the defconfig was processed
1108 successfully, or False when it fails.
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001109 """
1110 # output at least 30 characters to hide the "* defconfigs out of *".
1111 log = self.defconfig.ljust(30) + '\n'
1112
1113 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1114 # Some threads are running in parallel.
1115 # Print log atomically to not mix up logs from different threads.
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001116 print >> (sys.stdout if success else sys.stderr), log
1117
1118 if not success:
1119 if self.options.exit_on_error:
1120 sys.exit("Exit on error.")
1121 # If --exit-on-error flag is not set, skip this board and continue.
1122 # Record the failed board.
Masahiro Yamada1271b672016-08-22 22:18:20 +09001123 self.failed_boards.add(self.defconfig)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001124
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001125 self.progress.inc()
1126 self.progress.show()
Masahiro Yamada274a5ee2016-05-19 15:52:03 +09001127 self.state = STATE_IDLE
Masahiro Yamada465b7c02016-05-19 15:52:02 +09001128
Masahiro Yamadab6160812015-05-20 11:36:07 +09001129 def get_failed_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001130 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001131 """
1132 return self.failed_boards
1133
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001134 def get_suspicious_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +09001135 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001136 """
Masahiro Yamada35204d92016-08-22 22:18:21 +09001137 return self.suspicious_boards - self.failed_boards
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001138
Masahiro Yamadab6160812015-05-20 11:36:07 +09001139class Slots:
1140
1141 """Controller of the array of subprocess slots."""
1142
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001143 def __init__(self, configs, options, progress, reference_src_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001144 """Create a new slots controller.
1145
1146 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001147 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001148 options: option flags.
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001149 progress: A progress indicator.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001150 reference_src_dir: Determine the true starting config state from this
1151 source tree.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001152 """
1153 self.options = options
1154 self.slots = []
1155 devnull = get_devnull()
1156 make_cmd = get_make_cmd()
1157 for i in range(options.jobs):
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001158 self.slots.append(Slot(configs, options, progress, devnull,
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001159 make_cmd, reference_src_dir))
Masahiro Yamadab6160812015-05-20 11:36:07 +09001160
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001161 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001162 """Add a new subprocess if a vacant slot is found.
1163
1164 Arguments:
1165 defconfig: defconfig name to be put into.
1166
1167 Returns:
1168 Return True on success or False on failure
1169 """
1170 for slot in self.slots:
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001171 if slot.add(defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001172 return True
1173 return False
1174
1175 def available(self):
1176 """Check if there is a vacant slot.
1177
1178 Returns:
1179 Return True if at lease one vacant slot is found, False otherwise.
1180 """
1181 for slot in self.slots:
1182 if slot.poll():
1183 return True
1184 return False
1185
1186 def empty(self):
1187 """Check if all slots are vacant.
1188
1189 Returns:
1190 Return True if all the slots are vacant, False otherwise.
1191 """
1192 ret = True
1193 for slot in self.slots:
1194 if not slot.poll():
1195 ret = False
1196 return ret
1197
1198 def show_failed_boards(self):
1199 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada1271b672016-08-22 22:18:20 +09001200 boards = set()
Masahiro Yamada0153f032016-06-15 14:33:53 +09001201 output_file = 'moveconfig.failed'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001202
1203 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001204 boards |= slot.get_failed_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001205
Masahiro Yamada0153f032016-06-15 14:33:53 +09001206 if boards:
1207 boards = '\n'.join(boards) + '\n'
1208 msg = "The following boards were not processed due to error:\n"
1209 msg += boards
1210 msg += "(the list has been saved in %s)\n" % output_file
1211 print >> sys.stderr, color_text(self.options.color, COLOR_LIGHT_RED,
1212 msg)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001213
Masahiro Yamada0153f032016-06-15 14:33:53 +09001214 with open(output_file, 'w') as f:
1215 f.write(boards)
Joe Hershbergerdade12e2015-05-19 13:21:22 -05001216
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001217 def show_suspicious_boards(self):
1218 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada1271b672016-08-22 22:18:20 +09001219 boards = set()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001220 output_file = 'moveconfig.suspicious'
1221
1222 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001223 boards |= slot.get_suspicious_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001224
1225 if boards:
1226 boards = '\n'.join(boards) + '\n'
1227 msg = "The following boards might have been converted incorrectly.\n"
1228 msg += "It is highly recommended to check them manually:\n"
1229 msg += boards
1230 msg += "(the list has been saved in %s)\n" % output_file
1231 print >> sys.stderr, color_text(self.options.color, COLOR_YELLOW,
1232 msg)
1233
1234 with open(output_file, 'w') as f:
1235 f.write(boards)
1236
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001237class ReferenceSource:
1238
1239 """Reference source against which original configs should be parsed."""
1240
1241 def __init__(self, commit):
1242 """Create a reference source directory based on a specified commit.
1243
1244 Arguments:
1245 commit: commit to git-clone
1246 """
1247 self.src_dir = tempfile.mkdtemp()
1248 print "Cloning git repo to a separate work directory..."
1249 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1250 cwd=self.src_dir)
1251 print "Checkout '%s' to build the original autoconf.mk." % \
1252 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip()
1253 subprocess.check_output(['git', 'checkout', commit],
1254 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001255
1256 def __del__(self):
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001257 """Delete the reference source directory
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001258
1259 This function makes sure the temporary directory is cleaned away
1260 even if Python suddenly dies due to error. It should be done in here
1261 because it is guaranteed the destructor is always invoked when the
1262 instance of the class gets unreferenced.
1263 """
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001264 shutil.rmtree(self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001265
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001266 def get_dir(self):
1267 """Return the absolute path to the reference source directory."""
1268
1269 return self.src_dir
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001270
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001271def move_config(configs, options):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001272 """Move config options to defconfig files.
1273
1274 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001275 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001276 options: option flags
1277 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001278 if len(configs) == 0:
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001279 if options.force_sync:
1280 print 'No CONFIG is specified. You are probably syncing defconfigs.',
1281 else:
1282 print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
1283 else:
1284 print 'Move ' + ', '.join(configs),
1285 print '(jobs: %d)\n' % options.jobs
Masahiro Yamadab6160812015-05-20 11:36:07 +09001286
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001287 if options.git_ref:
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001288 reference_src = ReferenceSource(options.git_ref)
1289 reference_src_dir = reference_src.get_dir()
1290 else:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001291 reference_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001292
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001293 if options.defconfigs:
Masahiro Yamada3984d6e2016-10-19 14:39:54 +09001294 defconfigs = get_matched_defconfigs(options.defconfigs)
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001295 else:
Masahiro Yamada58175e32016-07-25 19:15:28 +09001296 defconfigs = get_all_defconfigs()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001297
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001298 progress = Progress(len(defconfigs))
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001299 slots = Slots(configs, options, progress, reference_src_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001300
1301 # Main loop to process defconfig files:
1302 # Add a new subprocess into a vacant slot.
1303 # Sleep if there is no available slot.
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001304 for defconfig in defconfigs:
1305 while not slots.add(defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001306 while not slots.available():
1307 # No available slot: sleep for a while
1308 time.sleep(SLEEP_TIME)
1309
1310 # wait until all the subprocesses finish
1311 while not slots.empty():
1312 time.sleep(SLEEP_TIME)
1313
Joe Hershberger3fa1ab72015-05-19 13:21:25 -05001314 print ''
Masahiro Yamadab6160812015-05-20 11:36:07 +09001315 slots.show_failed_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001316 slots.show_suspicious_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001317
Masahiro Yamadab6160812015-05-20 11:36:07 +09001318def main():
1319 try:
1320 cpu_count = multiprocessing.cpu_count()
1321 except NotImplementedError:
1322 cpu_count = 1
1323
1324 parser = optparse.OptionParser()
1325 # Add options here
1326 parser.add_option('-c', '--color', action='store_true', default=False,
1327 help='display the log in color')
Simon Glass8bf41c22016-09-12 23:18:21 -06001328 parser.add_option('-C', '--commit', action='store_true', default=False,
1329 help='Create a git commit for the operation')
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001330 parser.add_option('-d', '--defconfigs', type='string',
1331 help='a file containing a list of defconfigs to move')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001332 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1333 help='perform a trial run (show log with no changes)')
1334 parser.add_option('-e', '--exit-on-error', action='store_true',
1335 default=False,
1336 help='exit immediately on any error')
Masahiro Yamada83c17672016-05-19 15:52:08 +09001337 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1338 help='force sync by savedefconfig')
Masahiro Yamada6d139172016-08-22 22:18:22 +09001339 parser.add_option('-S', '--spl', action='store_true', default=False,
1340 help='parse config options defined for SPL build')
Joe Hershberger23475932015-05-19 13:21:20 -05001341 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1342 action='store_true', default=False,
1343 help='only cleanup the headers')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001344 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1345 help='the number of jobs to run simultaneously')
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001346 parser.add_option('-r', '--git-ref', type='string',
1347 help='the git ref to clone for building the autoconf.mk')
Simon Glass13e05a02016-09-12 23:18:20 -06001348 parser.add_option('-y', '--yes', action='store_true', default=False,
1349 help="respond 'yes' to any prompts")
Joe Hershberger808b63f2015-05-19 13:21:24 -05001350 parser.add_option('-v', '--verbose', action='store_true', default=False,
1351 help='show any build errors as boards are built')
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001352 parser.usage += ' CONFIG ...'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001353
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001354 (options, configs) = parser.parse_args()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001355
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001356 if len(configs) == 0 and not options.force_sync:
Masahiro Yamadab6160812015-05-20 11:36:07 +09001357 parser.print_usage()
1358 sys.exit(1)
1359
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001360 # prefix the option name with CONFIG_ if missing
1361 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1362 for config in configs ]
Masahiro Yamadab6160812015-05-20 11:36:07 +09001363
Joe Hershberger23475932015-05-19 13:21:20 -05001364 check_top_directory()
1365
1366 if not options.cleanup_headers_only:
Masahiro Yamadad0a9d2a2016-07-25 19:15:23 +09001367 check_clean_directory()
1368 update_cross_compile(options.color)
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001369 move_config(configs, options)
Joe Hershberger23475932015-05-19 13:21:20 -05001370
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001371 if configs:
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +09001372 cleanup_headers(configs, options)
Masahiro Yamadadce28de2016-07-25 19:15:29 +09001373 cleanup_extra_options(configs, options)
Chris Packham9d5274f2017-05-02 21:30:47 +12001374 cleanup_whitelist(configs, options)
Chris Packham0e6deff2017-05-02 21:30:48 +12001375 cleanup_readme(configs, options)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001376
Simon Glass8bf41c22016-09-12 23:18:21 -06001377 if options.commit:
1378 subprocess.call(['git', 'add', '-u'])
1379 if configs:
1380 msg = 'Convert %s %sto Kconfig' % (configs[0],
1381 'et al ' if len(configs) > 1 else '')
1382 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1383 '\n '.join(configs))
1384 else:
1385 msg = 'configs: Resync with savedefconfig'
1386 msg += '\n\nRsync all defconfig files using moveconfig.py'
1387 subprocess.call(['git', 'commit', '-s', '-m', msg])
1388
Masahiro Yamadab6160812015-05-20 11:36:07 +09001389if __name__ == '__main__':
1390 main()