blob: 5576b574a8f47d362bce2b91761f22c28f7e7d11 [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
Joe Hershberger91040e82015-05-19 13:21:19 -0500125 -d, --defconfigs
126 Specify a file containing a list of defconfigs to move
127
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900128 -n, --dry-run
Masahiro Yamadab6ef3932016-05-19 15:51:58 +0900129 Perform a trial run that does not make any changes. It is useful to
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900130 see what is going to happen before one actually runs it.
131
132 -e, --exit-on-error
133 Exit immediately if Make exits with a non-zero status while processing
134 a defconfig file.
135
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900136 -s, --force-sync
137 Do "make savedefconfig" forcibly for all the defconfig files.
138 If not specified, "make savedefconfig" only occurs for cases
139 where at least one CONFIG was moved.
140
Masahiro Yamada07913d12016-08-22 22:18:22 +0900141 -S, --spl
142 Look for moved config options in spl/include/autoconf.mk instead of
143 include/autoconf.mk. This is useful for moving options for SPL build
144 because SPL related options (mostly prefixed with CONFIG_SPL_) are
145 sometimes blocked by CONFIG_SPL_BUILD ifdef conditionals.
146
Joe Hershberger2144f882015-05-19 13:21:20 -0500147 -H, --headers-only
148 Only cleanup the headers; skip the defconfig processing
149
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900150 -j, --jobs
151 Specify the number of threads to run simultaneously. If not specified,
152 the number of threads is the same as the number of CPU cores.
153
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500154 -r, --git-ref
155 Specify the git ref to clone for building the autoconf.mk. If unspecified
156 use the CWD. This is useful for when changes to the Kconfig affect the
157 default values and you want to capture the state of the defconfig from
158 before that change was in effect. If in doubt, specify a ref pre-Kconfig
159 changes (use HEAD if Kconfig changes are not committed). Worst case it will
160 take a bit longer to run, but will always do the right thing.
161
Joe Hershberger95bf9c72015-05-19 13:21:24 -0500162 -v, --verbose
163 Show any build errors as boards are built
164
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900165To see the complete list of supported options, run
166
167 $ tools/moveconfig.py -h
168
169"""
170
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900171import copy
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900172import difflib
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900173import filecmp
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900174import fnmatch
175import multiprocessing
176import optparse
177import os
178import re
179import shutil
180import subprocess
181import sys
182import tempfile
183import time
184
185SHOW_GNU_MAKE = 'scripts/show-gnu-make'
186SLEEP_TIME=0.03
187
188# Here is the list of cross-tools I use.
189# Most of them are available at kernel.org
Masahiro Yamadac21fc7e2016-08-21 16:12:36 +0900190# (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the following:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900191# arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
192# blackfin: http://sourceforge.net/projects/adi-toolchain/files/
Bin Meng4440ece2015-09-25 01:22:39 -0700193# nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900194# nios2: https://sourcery.mentor.com/GNUToolchain/subscription42545
195# sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu
Bin Menge8aebc42016-02-21 21:18:02 -0800196#
197# openrisc kernel.org toolchain is out of date, download latest one from
198# http://opencores.org/or1k/OpenRISC_GNU_tool_chain#Prebuilt_versions
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900199CROSS_COMPILE = {
200 'arc': 'arc-linux-',
201 'aarch64': 'aarch64-linux-',
202 'arm': 'arm-unknown-linux-gnueabi-',
203 'avr32': 'avr32-linux-',
204 'blackfin': 'bfin-elf-',
205 'm68k': 'm68k-linux-',
206 'microblaze': 'microblaze-linux-',
207 'mips': 'mips-linux-',
208 'nds32': 'nds32le-linux-',
209 'nios2': 'nios2-linux-gnu-',
Bin Menge8aebc42016-02-21 21:18:02 -0800210 'openrisc': 'or1k-elf-',
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900211 'powerpc': 'powerpc-linux-',
212 'sh': 'sh-linux-gnu-',
213 'sparc': 'sparc-linux-',
Masahiro Yamada88e13462016-08-21 16:03:08 +0900214 'x86': 'i386-linux-',
215 'xtensa': 'xtensa-linux-'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900216}
217
218STATE_IDLE = 0
219STATE_DEFCONFIG = 1
220STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -0500221STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900222
223ACTION_MOVE = 0
Masahiro Yamadacc008292016-05-19 15:51:56 +0900224ACTION_NO_ENTRY = 1
Masahiro Yamada916224c2016-08-22 22:18:21 +0900225ACTION_NO_ENTRY_WARN = 2
226ACTION_NO_CHANGE = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900227
228COLOR_BLACK = '0;30'
229COLOR_RED = '0;31'
230COLOR_GREEN = '0;32'
231COLOR_BROWN = '0;33'
232COLOR_BLUE = '0;34'
233COLOR_PURPLE = '0;35'
234COLOR_CYAN = '0;36'
235COLOR_LIGHT_GRAY = '0;37'
236COLOR_DARK_GRAY = '1;30'
237COLOR_LIGHT_RED = '1;31'
238COLOR_LIGHT_GREEN = '1;32'
239COLOR_YELLOW = '1;33'
240COLOR_LIGHT_BLUE = '1;34'
241COLOR_LIGHT_PURPLE = '1;35'
242COLOR_LIGHT_CYAN = '1;36'
243COLOR_WHITE = '1;37'
244
245### helper functions ###
246def get_devnull():
247 """Get the file object of '/dev/null' device."""
248 try:
249 devnull = subprocess.DEVNULL # py3k
250 except AttributeError:
251 devnull = open(os.devnull, 'wb')
252 return devnull
253
254def check_top_directory():
255 """Exit if we are not at the top of source directory."""
256 for f in ('README', 'Licenses'):
257 if not os.path.exists(f):
258 sys.exit('Please run at the top of source directory.')
259
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900260def check_clean_directory():
261 """Exit if the source tree is not clean."""
262 for f in ('.config', 'include/config'):
263 if os.path.exists(f):
264 sys.exit("source tree is not clean, please run 'make mrproper'")
265
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900266def get_make_cmd():
267 """Get the command name of GNU Make.
268
269 U-Boot needs GNU Make for building, but the command name is not
270 necessarily "make". (for example, "gmake" on FreeBSD).
271 Returns the most appropriate command name on your system.
272 """
273 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
274 ret = process.communicate()
275 if process.returncode:
276 sys.exit('GNU Make not found')
277 return ret[0].rstrip()
278
Masahiro Yamada684c3062016-07-25 19:15:28 +0900279def get_all_defconfigs():
280 """Get all the defconfig files under the configs/ directory."""
281 defconfigs = []
282 for (dirpath, dirnames, filenames) in os.walk('configs'):
283 dirpath = dirpath[len('configs') + 1:]
284 for filename in fnmatch.filter(filenames, '*_defconfig'):
285 defconfigs.append(os.path.join(dirpath, filename))
286
287 return defconfigs
288
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900289def color_text(color_enabled, color, string):
290 """Return colored string."""
291 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900292 # LF should not be surrounded by the escape sequence.
293 # Otherwise, additional whitespace or line-feed might be printed.
294 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
295 for s in string.split('\n') ])
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900296 else:
297 return string
298
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900299def show_diff(a, b, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900300 """Show unidified diff.
301
302 Arguments:
303 a: A list of lines (before)
304 b: A list of lines (after)
305 file_path: Path to the file
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900306 color_enabled: Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900307 """
308
309 diff = difflib.unified_diff(a, b,
310 fromfile=os.path.join('a', file_path),
311 tofile=os.path.join('b', file_path))
312
313 for line in diff:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900314 if line[0] == '-' and line[1] != '-':
315 print color_text(color_enabled, COLOR_RED, line),
316 elif line[0] == '+' and line[1] != '+':
317 print color_text(color_enabled, COLOR_GREEN, line),
318 else:
319 print line,
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900320
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900321def update_cross_compile(color_enabled):
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400322 """Update per-arch CROSS_COMPILE via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900323
324 The default CROSS_COMPILE values are available
325 in the CROSS_COMPILE list above.
326
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400327 You can override them via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900328 CROSS_COMPILE_{ARCH}.
329
330 For example, if you want to override toolchain prefixes
331 for ARM and PowerPC, you can do as follows in your shell:
332
333 export CROSS_COMPILE_ARM=...
334 export CROSS_COMPILE_POWERPC=...
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900335
336 Then, this function checks if specified compilers really exist in your
337 PATH environment.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900338 """
339 archs = []
340
341 for arch in os.listdir('arch'):
342 if os.path.exists(os.path.join('arch', arch, 'Makefile')):
343 archs.append(arch)
344
345 # arm64 is a special case
346 archs.append('aarch64')
347
348 for arch in archs:
349 env = 'CROSS_COMPILE_' + arch.upper()
350 cross_compile = os.environ.get(env)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900351 if not cross_compile:
352 cross_compile = CROSS_COMPILE.get(arch, '')
353
354 for path in os.environ["PATH"].split(os.pathsep):
355 gcc_path = os.path.join(path, cross_compile + 'gcc')
356 if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
357 break
358 else:
359 print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
360 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
361 % (cross_compile, arch))
362 cross_compile = None
363
364 CROSS_COMPILE[arch] = cross_compile
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900365
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900366def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
367 extend_post):
368 """Extend matched lines if desired patterns are found before/after already
369 matched lines.
370
371 Arguments:
372 lines: A list of lines handled.
373 matched: A list of line numbers that have been already matched.
374 (will be updated by this function)
375 pre_patterns: A list of regular expression that should be matched as
376 preamble.
377 post_patterns: A list of regular expression that should be matched as
378 postamble.
379 extend_pre: Add the line number of matched preamble to the matched list.
380 extend_post: Add the line number of matched postamble to the matched list.
381 """
382 extended_matched = []
383
384 j = matched[0]
385
386 for i in matched:
387 if i == 0 or i < j:
388 continue
389 j = i
390 while j in matched:
391 j += 1
392 if j >= len(lines):
393 break
394
395 for p in pre_patterns:
396 if p.search(lines[i - 1]):
397 break
398 else:
399 # not matched
400 continue
401
402 for p in post_patterns:
403 if p.search(lines[j]):
404 break
405 else:
406 # not matched
407 continue
408
409 if extend_pre:
410 extended_matched.append(i - 1)
411 if extend_post:
412 extended_matched.append(j)
413
414 matched += extended_matched
415 matched.sort()
416
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900417def cleanup_one_header(header_path, patterns, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900418 """Clean regex-matched lines away from a file.
419
420 Arguments:
421 header_path: path to the cleaned file.
422 patterns: list of regex patterns. Any lines matching to these
423 patterns are deleted.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900424 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900425 """
426 with open(header_path) as f:
427 lines = f.readlines()
428
429 matched = []
430 for i, line in enumerate(lines):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900431 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
432 matched.append(i)
433 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900434 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900435 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900436 matched.append(i)
437 break
438
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900439 if not matched:
440 return
441
442 # remove empty #ifdef ... #endif, successive blank lines
443 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
444 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
445 pattern_endif = re.compile(r'#\s*endif\W') # #endif
446 pattern_blank = re.compile(r'^\s*$') # empty line
447
448 while True:
449 old_matched = copy.copy(matched)
450 extend_matched_lines(lines, matched, [pattern_if],
451 [pattern_endif], True, True)
452 extend_matched_lines(lines, matched, [pattern_elif],
453 [pattern_elif, pattern_endif], True, False)
454 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
455 [pattern_blank], False, True)
456 extend_matched_lines(lines, matched, [pattern_blank],
457 [pattern_elif, pattern_endif], True, False)
458 extend_matched_lines(lines, matched, [pattern_blank],
459 [pattern_blank], True, False)
460 if matched == old_matched:
461 break
462
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900463 tolines = copy.copy(lines)
464
465 for i in reversed(matched):
466 tolines.pop(i)
467
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900468 show_diff(lines, tolines, header_path, options.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900469
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900470 if options.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900471 return
472
473 with open(header_path, 'w') as f:
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900474 for line in tolines:
475 f.write(line)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900476
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900477def cleanup_headers(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900478 """Delete config defines from board headers.
479
480 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900481 configs: A list of CONFIGs to remove.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900482 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900483 """
484 while True:
485 choice = raw_input('Clean up headers? [y/n]: ').lower()
486 print choice
487 if choice == 'y' or choice == 'n':
488 break
489
490 if choice == 'n':
491 return
492
493 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900494 for config in configs:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900495 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
496 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
497
Joe Hershberger60727f52015-05-19 13:21:21 -0500498 for dir in 'include', 'arch', 'board':
499 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900500 if dirpath == os.path.join('include', 'generated'):
501 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500502 for filename in filenames:
503 if not fnmatch.fnmatch(filename, '*~'):
504 cleanup_one_header(os.path.join(dirpath, filename),
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900505 patterns, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900506
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900507def cleanup_one_extra_option(defconfig_path, configs, options):
508 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
509
510 Arguments:
511 defconfig_path: path to the cleaned defconfig file.
512 configs: A list of CONFIGs to remove.
513 options: option flags.
514 """
515
516 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
517 end = '"\n'
518
519 with open(defconfig_path) as f:
520 lines = f.readlines()
521
522 for i, line in enumerate(lines):
523 if line.startswith(start) and line.endswith(end):
524 break
525 else:
526 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
527 return
528
529 old_tokens = line[len(start):-len(end)].split(',')
530 new_tokens = []
531
532 for token in old_tokens:
533 pos = token.find('=')
534 if not (token[:pos] if pos >= 0 else token) in configs:
535 new_tokens.append(token)
536
537 if new_tokens == old_tokens:
538 return
539
540 tolines = copy.copy(lines)
541
542 if new_tokens:
543 tolines[i] = start + ','.join(new_tokens) + end
544 else:
545 tolines.pop(i)
546
547 show_diff(lines, tolines, defconfig_path, options.color)
548
549 if options.dry_run:
550 return
551
552 with open(defconfig_path, 'w') as f:
553 for line in tolines:
554 f.write(line)
555
556def cleanup_extra_options(configs, options):
557 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
558
559 Arguments:
560 configs: A list of CONFIGs to remove.
561 options: option flags.
562 """
563 while True:
564 choice = raw_input('Clean up CONFIG_SYS_EXTRA_OPTIONS? [y/n]: ').lower()
565 print choice
566 if choice == 'y' or choice == 'n':
567 break
568
569 if choice == 'n':
570 return
571
572 configs = [ config[len('CONFIG_'):] for config in configs ]
573
574 defconfigs = get_all_defconfigs()
575
576 for defconfig in defconfigs:
577 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
578 options)
579
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900580### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900581class Progress:
582
583 """Progress Indicator"""
584
585 def __init__(self, total):
586 """Create a new progress indicator.
587
588 Arguments:
589 total: A number of defconfig files to process.
590 """
591 self.current = 0
592 self.total = total
593
594 def inc(self):
595 """Increment the number of processed defconfig files."""
596
597 self.current += 1
598
599 def show(self):
600 """Display the progress."""
601 print ' %d defconfigs out of %d\r' % (self.current, self.total),
602 sys.stdout.flush()
603
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900604class KconfigParser:
605
606 """A parser of .config and include/autoconf.mk."""
607
608 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
609 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
610
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900611 def __init__(self, configs, options, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900612 """Create a new parser.
613
614 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900615 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900616 options: option flags.
617 build_dir: Build directory.
618 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900619 self.configs = configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900620 self.options = options
Masahiro Yamada1f169922016-05-19 15:52:00 +0900621 self.dotconfig = os.path.join(build_dir, '.config')
622 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900623 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
624 'autoconf.mk')
Masahiro Yamada1f169922016-05-19 15:52:00 +0900625 self.config_autoconf = os.path.join(build_dir, 'include', 'config',
626 'auto.conf')
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900627 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900628
629 def get_cross_compile(self):
630 """Parse .config file and return CROSS_COMPILE.
631
632 Returns:
633 A string storing the compiler prefix for the architecture.
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900634 Return a NULL string for architectures that do not require
635 compiler prefix (Sandbox and native build is the case).
636 Return None if the specified compiler is missing in your PATH.
637 Caller should distinguish '' and None.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900638 """
639 arch = ''
640 cpu = ''
Masahiro Yamada1f169922016-05-19 15:52:00 +0900641 for line in open(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900642 m = self.re_arch.match(line)
643 if m:
644 arch = m.group(1)
645 continue
646 m = self.re_cpu.match(line)
647 if m:
648 cpu = m.group(1)
649
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900650 if not arch:
651 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900652
653 # fix-up for aarch64
654 if arch == 'arm' and cpu == 'armv8':
655 arch = 'aarch64'
656
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900657 return CROSS_COMPILE.get(arch, None)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900658
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900659 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900660 """Parse .config, defconfig, include/autoconf.mk for one config.
661
662 This function looks for the config options in the lines from
663 defconfig, .config, and include/autoconf.mk in order to decide
664 which action should be taken for this defconfig.
665
666 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900667 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900668 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900669 autoconf_lines: lines from the include/autoconf.mk file.
670
671 Returns:
672 A tupple of the action for this defconfig and the line
673 matched for the config.
674 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900675 not_set = '# %s is not set' % config
676
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900677 for line in autoconf_lines:
678 line = line.rstrip()
679 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900680 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900681 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900682 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900683 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900684
Masahiro Yamada916224c2016-08-22 22:18:21 +0900685 for line in dotconfig_lines:
686 line = line.rstrip()
687 if line.startswith(config + '=') or line == not_set:
688 old_val = line
689 break
690 else:
691 if new_val == not_set:
692 return (ACTION_NO_ENTRY, config)
693 else:
694 return (ACTION_NO_ENTRY_WARN, config)
695
Masahiro Yamadacc008292016-05-19 15:51:56 +0900696 # If this CONFIG is neither bool nor trisate
697 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
698 # tools/scripts/define2mk.sed changes '1' to 'y'.
699 # This is a problem if the CONFIG is int type.
700 # Check the type in Kconfig and handle it correctly.
701 if new_val[-2:] == '=y':
702 new_val = new_val[:-1] + '1'
703
Masahiro Yamada50301592016-06-15 14:33:50 +0900704 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
705 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900706
Masahiro Yamada1d085562016-05-19 15:52:02 +0900707 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900708 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900709
Masahiro Yamadacc008292016-05-19 15:51:56 +0900710 This function parses the generated .config and include/autoconf.mk
711 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900712 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900713
714 Arguments:
715 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900716
717 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900718 Return a tuple of (updated flag, log string).
719 The "updated flag" is True if the .config was updated, False
720 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900721 """
722
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900723 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900724 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900725 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900726 rm_files = [self.config_autoconf, self.autoconf]
727
728 if self.options.spl:
729 if os.path.exists(self.spl_autoconf):
730 autoconf_path = self.spl_autoconf
731 rm_files.append(self.spl_autoconf)
732 else:
733 for f in rm_files:
734 os.remove(f)
735 return (updated, suspicious,
736 color_text(self.options.color, COLOR_BROWN,
737 "SPL is not enabled. Skipped.") + '\n')
738 else:
739 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900740
Masahiro Yamada1f169922016-05-19 15:52:00 +0900741 with open(self.dotconfig) as f:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900742 dotconfig_lines = f.readlines()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900743
Masahiro Yamada07913d12016-08-22 22:18:22 +0900744 with open(autoconf_path) as f:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900745 autoconf_lines = f.readlines()
746
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900747 for config in self.configs:
748 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500749 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900750 results.append(result)
751
752 log = ''
753
754 for (action, value) in results:
755 if action == ACTION_MOVE:
756 actlog = "Move '%s'" % value
757 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900758 elif action == ACTION_NO_ENTRY:
759 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900760 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900761 elif action == ACTION_NO_ENTRY_WARN:
762 actlog = "%s is not defined in Kconfig (suspicious). Do nothing." % value
763 log_color = COLOR_YELLOW
764 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900765 elif action == ACTION_NO_CHANGE:
766 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
767 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900768 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada07913d12016-08-22 22:18:22 +0900769 elif action == ACTION_SPL_NOT_EXIST:
770 actlog = "SPL is not enabled for this defconfig. Skip."
771 log_color = COLOR_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900772 else:
773 sys.exit("Internal Error. This should not happen.")
774
Masahiro Yamada1d085562016-05-19 15:52:02 +0900775 log += color_text(self.options.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900776
Masahiro Yamada1f169922016-05-19 15:52:00 +0900777 with open(self.dotconfig, 'a') as f:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900778 for (action, value) in results:
779 if action == ACTION_MOVE:
780 f.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900781 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900782
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900783 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900784 for f in rm_files:
785 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900786
Masahiro Yamada916224c2016-08-22 22:18:21 +0900787 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900788
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900789 def check_defconfig(self):
790 """Check the defconfig after savedefconfig
791
792 Returns:
793 Return additional log if moved CONFIGs were removed again by
794 'make savedefconfig'.
795 """
796
797 log = ''
798
799 with open(self.defconfig) as f:
800 defconfig_lines = f.readlines()
801
802 for (action, value) in self.results:
803 if action != ACTION_MOVE:
804 continue
805 if not value + '\n' in defconfig_lines:
806 log += color_text(self.options.color, COLOR_YELLOW,
807 "'%s' was removed by savedefconfig.\n" %
808 value)
809
810 return log
811
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900812class Slot:
813
814 """A slot to store a subprocess.
815
816 Each instance of this class handles one subprocess.
817 This class is useful to control multiple threads
818 for faster processing.
819 """
820
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500821 def __init__(self, configs, options, progress, devnull, make_cmd, reference_src_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900822 """Create a new process slot.
823
824 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900825 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900826 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900827 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900828 devnull: A file object of '/dev/null'.
829 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500830 reference_src_dir: Determine the true starting config state from this
831 source tree.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900832 """
833 self.options = options
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900834 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900835 self.build_dir = tempfile.mkdtemp()
836 self.devnull = devnull
837 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500838 self.reference_src_dir = reference_src_dir
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900839 self.parser = KconfigParser(configs, options, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900840 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900841 self.failed_boards = set()
842 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900843
844 def __del__(self):
845 """Delete the working directory
846
847 This function makes sure the temporary directory is cleaned away
848 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500849 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900850 instance of the class gets unreferenced.
851
852 If the subprocess is still running, wait until it finishes.
853 """
854 if self.state != STATE_IDLE:
855 while self.ps.poll() == None:
856 pass
857 shutil.rmtree(self.build_dir)
858
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900859 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900860 """Assign a new subprocess for defconfig and add it to the slot.
861
862 If the slot is vacant, create a new subprocess for processing the
863 given defconfig and add it to the slot. Just returns False if
864 the slot is occupied (i.e. the current subprocess is still running).
865
866 Arguments:
867 defconfig: defconfig name.
868
869 Returns:
870 Return True on success or False on failure
871 """
872 if self.state != STATE_IDLE:
873 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900874
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900875 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900876 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900877 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900878 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900879 return True
880
881 def poll(self):
882 """Check the status of the subprocess and handle it as needed.
883
884 Returns True if the slot is vacant (i.e. in idle state).
885 If the configuration is successfully finished, assign a new
886 subprocess to build include/autoconf.mk.
887 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900888 parse the .config and the include/autoconf.mk, moving
889 config options to the .config as needed.
890 If the .config was updated, run "make savedefconfig" to sync
891 it, update the original defconfig, and then set the slot back
892 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900893
894 Returns:
895 Return True if the subprocess is terminated, False otherwise
896 """
897 if self.state == STATE_IDLE:
898 return True
899
900 if self.ps.poll() == None:
901 return False
902
903 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900904 self.handle_error()
905 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900906 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500907 self.do_savedefconfig()
908 else:
909 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900910 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900911 if self.current_src_dir:
912 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500913 self.do_defconfig()
914 else:
915 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900916 elif self.state == STATE_SAVEDEFCONFIG:
917 self.update_defconfig()
918 else:
919 sys.exit("Internal Error. This should not happen.")
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900920
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900921 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500922
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900923 def handle_error(self):
924 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900925
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900926 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
927 "Failed to process.\n")
928 if self.options.verbose:
929 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
930 self.ps.stderr.read())
931 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500932
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900933 def do_defconfig(self):
934 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900935
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900936 cmd = list(self.make_cmd)
937 cmd.append(self.defconfig)
938 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900939 stderr=subprocess.PIPE,
940 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900941 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900942
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900943 def do_autoconf(self):
944 """Run 'make include/config/auto.conf'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900945
Joe Hershberger25400092015-05-19 13:21:23 -0500946 self.cross_compile = self.parser.get_cross_compile()
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900947 if self.cross_compile is None:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900948 self.log += color_text(self.options.color, COLOR_YELLOW,
949 "Compiler is missing. Do nothing.\n")
Masahiro Yamada4efef992016-05-19 15:52:03 +0900950 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900951 return
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900952
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900953 cmd = list(self.make_cmd)
Joe Hershberger25400092015-05-19 13:21:23 -0500954 if self.cross_compile:
955 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
Joe Hershberger7740f652015-05-19 13:21:18 -0500956 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900957 cmd.append('include/config/auto.conf')
Joe Hershberger25400092015-05-19 13:21:23 -0500958 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900959 stderr=subprocess.PIPE,
960 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900961 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900962
963 def do_savedefconfig(self):
964 """Update the .config and run 'make savedefconfig'."""
965
Masahiro Yamada916224c2016-08-22 22:18:21 +0900966 (updated, suspicious, log) = self.parser.update_dotconfig()
967 if suspicious:
968 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900969 self.log += log
970
971 if not self.options.force_sync and not updated:
972 self.finish(True)
973 return
974 if updated:
975 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
976 "Syncing by savedefconfig...\n")
977 else:
978 self.log += "Syncing by savedefconfig (forced by option)...\n"
979
980 cmd = list(self.make_cmd)
981 cmd.append('savedefconfig')
982 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
983 stderr=subprocess.PIPE)
984 self.state = STATE_SAVEDEFCONFIG
985
986 def update_defconfig(self):
987 """Update the input defconfig and go back to the idle state."""
988
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900989 log = self.parser.check_defconfig()
990 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900991 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900992 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900993 orig_defconfig = os.path.join('configs', self.defconfig)
994 new_defconfig = os.path.join(self.build_dir, 'defconfig')
995 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
996
997 if updated:
Joe Hershberger06cc1d32016-06-10 14:53:30 -0500998 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900999 "defconfig was updated.\n")
1000
1001 if not self.options.dry_run and updated:
1002 shutil.move(new_defconfig, orig_defconfig)
1003 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001004
Masahiro Yamada4efef992016-05-19 15:52:03 +09001005 def finish(self, success):
1006 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001007
1008 Arguments:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001009 success: Should be True when the defconfig was processed
1010 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001011 """
1012 # output at least 30 characters to hide the "* defconfigs out of *".
1013 log = self.defconfig.ljust(30) + '\n'
1014
1015 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1016 # Some threads are running in parallel.
1017 # Print log atomically to not mix up logs from different threads.
Masahiro Yamada4efef992016-05-19 15:52:03 +09001018 print >> (sys.stdout if success else sys.stderr), log
1019
1020 if not success:
1021 if self.options.exit_on_error:
1022 sys.exit("Exit on error.")
1023 # If --exit-on-error flag is not set, skip this board and continue.
1024 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001025 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001026
Masahiro Yamada1d085562016-05-19 15:52:02 +09001027 self.progress.inc()
1028 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001029 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001030
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001031 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001032 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001033 """
1034 return self.failed_boards
1035
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001036 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001037 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001038 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001039 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001040
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001041class Slots:
1042
1043 """Controller of the array of subprocess slots."""
1044
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001045 def __init__(self, configs, options, progress, reference_src_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001046 """Create a new slots controller.
1047
1048 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001049 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001050 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001051 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001052 reference_src_dir: Determine the true starting config state from this
1053 source tree.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001054 """
1055 self.options = options
1056 self.slots = []
1057 devnull = get_devnull()
1058 make_cmd = get_make_cmd()
1059 for i in range(options.jobs):
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001060 self.slots.append(Slot(configs, options, progress, devnull,
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001061 make_cmd, reference_src_dir))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001062
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001063 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001064 """Add a new subprocess if a vacant slot is found.
1065
1066 Arguments:
1067 defconfig: defconfig name to be put into.
1068
1069 Returns:
1070 Return True on success or False on failure
1071 """
1072 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001073 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001074 return True
1075 return False
1076
1077 def available(self):
1078 """Check if there is a vacant slot.
1079
1080 Returns:
1081 Return True if at lease one vacant slot is found, False otherwise.
1082 """
1083 for slot in self.slots:
1084 if slot.poll():
1085 return True
1086 return False
1087
1088 def empty(self):
1089 """Check if all slots are vacant.
1090
1091 Returns:
1092 Return True if all the slots are vacant, False otherwise.
1093 """
1094 ret = True
1095 for slot in self.slots:
1096 if not slot.poll():
1097 ret = False
1098 return ret
1099
1100 def show_failed_boards(self):
1101 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001102 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001103 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001104
1105 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001106 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001107
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001108 if boards:
1109 boards = '\n'.join(boards) + '\n'
1110 msg = "The following boards were not processed due to error:\n"
1111 msg += boards
1112 msg += "(the list has been saved in %s)\n" % output_file
1113 print >> sys.stderr, color_text(self.options.color, COLOR_LIGHT_RED,
1114 msg)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001115
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001116 with open(output_file, 'w') as f:
1117 f.write(boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001118
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001119 def show_suspicious_boards(self):
1120 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001121 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001122 output_file = 'moveconfig.suspicious'
1123
1124 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001125 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001126
1127 if boards:
1128 boards = '\n'.join(boards) + '\n'
1129 msg = "The following boards might have been converted incorrectly.\n"
1130 msg += "It is highly recommended to check them manually:\n"
1131 msg += boards
1132 msg += "(the list has been saved in %s)\n" % output_file
1133 print >> sys.stderr, color_text(self.options.color, COLOR_YELLOW,
1134 msg)
1135
1136 with open(output_file, 'w') as f:
1137 f.write(boards)
1138
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001139class ReferenceSource:
1140
1141 """Reference source against which original configs should be parsed."""
1142
1143 def __init__(self, commit):
1144 """Create a reference source directory based on a specified commit.
1145
1146 Arguments:
1147 commit: commit to git-clone
1148 """
1149 self.src_dir = tempfile.mkdtemp()
1150 print "Cloning git repo to a separate work directory..."
1151 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1152 cwd=self.src_dir)
1153 print "Checkout '%s' to build the original autoconf.mk." % \
1154 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip()
1155 subprocess.check_output(['git', 'checkout', commit],
1156 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001157
1158 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001159 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001160
1161 This function makes sure the temporary directory is cleaned away
1162 even if Python suddenly dies due to error. It should be done in here
1163 because it is guaranteed the destructor is always invoked when the
1164 instance of the class gets unreferenced.
1165 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001166 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001167
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001168 def get_dir(self):
1169 """Return the absolute path to the reference source directory."""
1170
1171 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001172
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001173def move_config(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001174 """Move config options to defconfig files.
1175
1176 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001177 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001178 options: option flags
1179 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001180 if len(configs) == 0:
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001181 if options.force_sync:
1182 print 'No CONFIG is specified. You are probably syncing defconfigs.',
1183 else:
1184 print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
1185 else:
1186 print 'Move ' + ', '.join(configs),
1187 print '(jobs: %d)\n' % options.jobs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001188
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001189 if options.git_ref:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001190 reference_src = ReferenceSource(options.git_ref)
1191 reference_src_dir = reference_src.get_dir()
1192 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001193 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001194
Joe Hershberger91040e82015-05-19 13:21:19 -05001195 if options.defconfigs:
1196 defconfigs = [line.strip() for line in open(options.defconfigs)]
1197 for i, defconfig in enumerate(defconfigs):
1198 if not defconfig.endswith('_defconfig'):
1199 defconfigs[i] = defconfig + '_defconfig'
1200 if not os.path.exists(os.path.join('configs', defconfigs[i])):
1201 sys.exit('%s - defconfig does not exist. Stopping.' %
1202 defconfigs[i])
1203 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001204 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001205
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001206 progress = Progress(len(defconfigs))
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001207 slots = Slots(configs, options, progress, reference_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001208
1209 # Main loop to process defconfig files:
1210 # Add a new subprocess into a vacant slot.
1211 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001212 for defconfig in defconfigs:
1213 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001214 while not slots.available():
1215 # No available slot: sleep for a while
1216 time.sleep(SLEEP_TIME)
1217
1218 # wait until all the subprocesses finish
1219 while not slots.empty():
1220 time.sleep(SLEEP_TIME)
1221
Joe Hershberger2e2ce6c2015-05-19 13:21:25 -05001222 print ''
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001223 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001224 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001225
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001226def main():
1227 try:
1228 cpu_count = multiprocessing.cpu_count()
1229 except NotImplementedError:
1230 cpu_count = 1
1231
1232 parser = optparse.OptionParser()
1233 # Add options here
1234 parser.add_option('-c', '--color', action='store_true', default=False,
1235 help='display the log in color')
Joe Hershberger91040e82015-05-19 13:21:19 -05001236 parser.add_option('-d', '--defconfigs', type='string',
1237 help='a file containing a list of defconfigs to move')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001238 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1239 help='perform a trial run (show log with no changes)')
1240 parser.add_option('-e', '--exit-on-error', action='store_true',
1241 default=False,
1242 help='exit immediately on any error')
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001243 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1244 help='force sync by savedefconfig')
Masahiro Yamada07913d12016-08-22 22:18:22 +09001245 parser.add_option('-S', '--spl', action='store_true', default=False,
1246 help='parse config options defined for SPL build')
Joe Hershberger2144f882015-05-19 13:21:20 -05001247 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1248 action='store_true', default=False,
1249 help='only cleanup the headers')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001250 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1251 help='the number of jobs to run simultaneously')
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001252 parser.add_option('-r', '--git-ref', type='string',
1253 help='the git ref to clone for building the autoconf.mk')
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001254 parser.add_option('-v', '--verbose', action='store_true', default=False,
1255 help='show any build errors as boards are built')
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001256 parser.usage += ' CONFIG ...'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001257
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001258 (options, configs) = parser.parse_args()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001259
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001260 if len(configs) == 0 and not options.force_sync:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001261 parser.print_usage()
1262 sys.exit(1)
1263
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001264 # prefix the option name with CONFIG_ if missing
1265 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1266 for config in configs ]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001267
Joe Hershberger2144f882015-05-19 13:21:20 -05001268 check_top_directory()
1269
1270 if not options.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001271 check_clean_directory()
1272 update_cross_compile(options.color)
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001273 move_config(configs, options)
Joe Hershberger2144f882015-05-19 13:21:20 -05001274
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001275 if configs:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +09001276 cleanup_headers(configs, options)
Masahiro Yamada9ab02962016-07-25 19:15:29 +09001277 cleanup_extra_options(configs, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001278
1279if __name__ == '__main__':
1280 main()