blob: a0837140e984f1cfcf077ae75ba0d077985f49a1 [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 Yamada5a27c732015-05-20 11:36:07 +090044It looks like one of the followings:
45
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.
50 The entry for this CONFIG was not found in Kconfig.
51 There are two common cases:
52 - You forgot to create an entry for the CONFIG before running
53 this tool, or made a typo in a CONFIG passed to this tool.
54 - The entry was hidden due to unmet 'depends on'.
55 This is correct behavior.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090056
Masahiro Yamadacc008292016-05-19 15:51:56 +090057 - 'CONFIG_...' is the same as the define in Kconfig. Do nothing.
58 The define in the config header matched the one in Kconfig.
59 We do not need to touch it.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090060
61 - Undefined. Do nothing.
62 This config option was not found in the config header.
63 Nothing to do.
64
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +090065 - Compiler is missing. Do nothing.
66 The compiler specified for this architecture was not found
67 in your PATH environment.
68 (If -e option is passed, the tool exits immediately.)
69
70 - Failed to process.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090071 An error occurred during processing this defconfig. Skipped.
72 (If -e option is passed, the tool exits immediately on error.)
73
74Finally, you will be asked, Clean up headers? [y/n]:
75
76If you say 'y' here, the unnecessary config defines are removed
77from the config headers (include/configs/*.h).
78It just uses the regex method, so you should not rely on it.
79Just in case, please do 'git diff' to see what happened.
80
81
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090082How does it work?
83-----------------
Masahiro Yamada5a27c732015-05-20 11:36:07 +090084
85This tool runs configuration and builds include/autoconf.mk for every
86defconfig. The config options defined in Kconfig appear in the .config
87file (unless they are hidden because of unmet dependency.)
88On the other hand, the config options defined by board headers are seen
89in include/autoconf.mk. The tool looks for the specified options in both
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090090of them to decide the appropriate action for the options. If the given
91config option is found in the .config, but its value does not match the
92one from the board header, the config option in the .config is replaced
93with the define in the board header. Then, the .config is synced by
94"make savedefconfig" and the defconfig is updated with it.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090095
96For faster processing, this tool handles multi-threading. It creates
97separate build directories where the out-of-tree build is run. The
98temporary build directories are automatically created and deleted as
99needed. The number of threads are chosen based on the number of the CPU
100cores of your system although you can change it via -j (--jobs) option.
101
102
103Toolchains
104----------
105
106Appropriate toolchain are necessary to generate include/autoconf.mk
107for all the architectures supported by U-Boot. Most of them are available
108at the kernel.org site, some are not provided by kernel.org.
109
110The default per-arch CROSS_COMPILE used by this tool is specified by
111the list below, CROSS_COMPILE. You may wish to update the list to
112use your own. Instead of modifying the list directly, you can give
113them via environments.
114
115
116Available options
117-----------------
118
119 -c, --color
120 Surround each portion of the log with escape sequences to display it
121 in color on the terminal.
122
Joe Hershberger91040e82015-05-19 13:21:19 -0500123 -d, --defconfigs
124 Specify a file containing a list of defconfigs to move
125
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900126 -n, --dry-run
Masahiro Yamadab6ef3932016-05-19 15:51:58 +0900127 Perform a trial run that does not make any changes. It is useful to
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900128 see what is going to happen before one actually runs it.
129
130 -e, --exit-on-error
131 Exit immediately if Make exits with a non-zero status while processing
132 a defconfig file.
133
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900134 -s, --force-sync
135 Do "make savedefconfig" forcibly for all the defconfig files.
136 If not specified, "make savedefconfig" only occurs for cases
137 where at least one CONFIG was moved.
138
Joe Hershberger2144f882015-05-19 13:21:20 -0500139 -H, --headers-only
140 Only cleanup the headers; skip the defconfig processing
141
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900142 -j, --jobs
143 Specify the number of threads to run simultaneously. If not specified,
144 the number of threads is the same as the number of CPU cores.
145
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500146 -r, --git-ref
147 Specify the git ref to clone for building the autoconf.mk. If unspecified
148 use the CWD. This is useful for when changes to the Kconfig affect the
149 default values and you want to capture the state of the defconfig from
150 before that change was in effect. If in doubt, specify a ref pre-Kconfig
151 changes (use HEAD if Kconfig changes are not committed). Worst case it will
152 take a bit longer to run, but will always do the right thing.
153
Joe Hershberger95bf9c72015-05-19 13:21:24 -0500154 -v, --verbose
155 Show any build errors as boards are built
156
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900157To see the complete list of supported options, run
158
159 $ tools/moveconfig.py -h
160
161"""
162
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900163import copy
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900164import difflib
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900165import filecmp
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900166import fnmatch
167import multiprocessing
168import optparse
169import os
170import re
171import shutil
172import subprocess
173import sys
174import tempfile
175import time
176
177SHOW_GNU_MAKE = 'scripts/show-gnu-make'
178SLEEP_TIME=0.03
179
180# Here is the list of cross-tools I use.
181# Most of them are available at kernel.org
182# (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the followings:
183# arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
184# blackfin: http://sourceforge.net/projects/adi-toolchain/files/
Bin Meng4440ece2015-09-25 01:22:39 -0700185# nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900186# nios2: https://sourcery.mentor.com/GNUToolchain/subscription42545
187# sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu
Bin Menge8aebc42016-02-21 21:18:02 -0800188#
189# openrisc kernel.org toolchain is out of date, download latest one from
190# http://opencores.org/or1k/OpenRISC_GNU_tool_chain#Prebuilt_versions
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900191CROSS_COMPILE = {
192 'arc': 'arc-linux-',
193 'aarch64': 'aarch64-linux-',
194 'arm': 'arm-unknown-linux-gnueabi-',
195 'avr32': 'avr32-linux-',
196 'blackfin': 'bfin-elf-',
197 'm68k': 'm68k-linux-',
198 'microblaze': 'microblaze-linux-',
199 'mips': 'mips-linux-',
200 'nds32': 'nds32le-linux-',
201 'nios2': 'nios2-linux-gnu-',
Bin Menge8aebc42016-02-21 21:18:02 -0800202 'openrisc': 'or1k-elf-',
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900203 'powerpc': 'powerpc-linux-',
204 'sh': 'sh-linux-gnu-',
205 'sparc': 'sparc-linux-',
206 'x86': 'i386-linux-'
207}
208
209STATE_IDLE = 0
210STATE_DEFCONFIG = 1
211STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -0500212STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900213
214ACTION_MOVE = 0
Masahiro Yamadacc008292016-05-19 15:51:56 +0900215ACTION_NO_ENTRY = 1
216ACTION_NO_CHANGE = 2
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900217
218COLOR_BLACK = '0;30'
219COLOR_RED = '0;31'
220COLOR_GREEN = '0;32'
221COLOR_BROWN = '0;33'
222COLOR_BLUE = '0;34'
223COLOR_PURPLE = '0;35'
224COLOR_CYAN = '0;36'
225COLOR_LIGHT_GRAY = '0;37'
226COLOR_DARK_GRAY = '1;30'
227COLOR_LIGHT_RED = '1;31'
228COLOR_LIGHT_GREEN = '1;32'
229COLOR_YELLOW = '1;33'
230COLOR_LIGHT_BLUE = '1;34'
231COLOR_LIGHT_PURPLE = '1;35'
232COLOR_LIGHT_CYAN = '1;36'
233COLOR_WHITE = '1;37'
234
235### helper functions ###
236def get_devnull():
237 """Get the file object of '/dev/null' device."""
238 try:
239 devnull = subprocess.DEVNULL # py3k
240 except AttributeError:
241 devnull = open(os.devnull, 'wb')
242 return devnull
243
244def check_top_directory():
245 """Exit if we are not at the top of source directory."""
246 for f in ('README', 'Licenses'):
247 if not os.path.exists(f):
248 sys.exit('Please run at the top of source directory.')
249
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900250def check_clean_directory():
251 """Exit if the source tree is not clean."""
252 for f in ('.config', 'include/config'):
253 if os.path.exists(f):
254 sys.exit("source tree is not clean, please run 'make mrproper'")
255
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900256def get_make_cmd():
257 """Get the command name of GNU Make.
258
259 U-Boot needs GNU Make for building, but the command name is not
260 necessarily "make". (for example, "gmake" on FreeBSD).
261 Returns the most appropriate command name on your system.
262 """
263 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
264 ret = process.communicate()
265 if process.returncode:
266 sys.exit('GNU Make not found')
267 return ret[0].rstrip()
268
269def color_text(color_enabled, color, string):
270 """Return colored string."""
271 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900272 # LF should not be surrounded by the escape sequence.
273 # Otherwise, additional whitespace or line-feed might be printed.
274 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
275 for s in string.split('\n') ])
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900276 else:
277 return string
278
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900279def show_diff(a, b, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900280 """Show unidified diff.
281
282 Arguments:
283 a: A list of lines (before)
284 b: A list of lines (after)
285 file_path: Path to the file
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900286 color_enabled: Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900287 """
288
289 diff = difflib.unified_diff(a, b,
290 fromfile=os.path.join('a', file_path),
291 tofile=os.path.join('b', file_path))
292
293 for line in diff:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900294 if line[0] == '-' and line[1] != '-':
295 print color_text(color_enabled, COLOR_RED, line),
296 elif line[0] == '+' and line[1] != '+':
297 print color_text(color_enabled, COLOR_GREEN, line),
298 else:
299 print line,
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900300
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900301def update_cross_compile(color_enabled):
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400302 """Update per-arch CROSS_COMPILE via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900303
304 The default CROSS_COMPILE values are available
305 in the CROSS_COMPILE list above.
306
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400307 You can override them via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900308 CROSS_COMPILE_{ARCH}.
309
310 For example, if you want to override toolchain prefixes
311 for ARM and PowerPC, you can do as follows in your shell:
312
313 export CROSS_COMPILE_ARM=...
314 export CROSS_COMPILE_POWERPC=...
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900315
316 Then, this function checks if specified compilers really exist in your
317 PATH environment.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900318 """
319 archs = []
320
321 for arch in os.listdir('arch'):
322 if os.path.exists(os.path.join('arch', arch, 'Makefile')):
323 archs.append(arch)
324
325 # arm64 is a special case
326 archs.append('aarch64')
327
328 for arch in archs:
329 env = 'CROSS_COMPILE_' + arch.upper()
330 cross_compile = os.environ.get(env)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900331 if not cross_compile:
332 cross_compile = CROSS_COMPILE.get(arch, '')
333
334 for path in os.environ["PATH"].split(os.pathsep):
335 gcc_path = os.path.join(path, cross_compile + 'gcc')
336 if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
337 break
338 else:
339 print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
340 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
341 % (cross_compile, arch))
342 cross_compile = None
343
344 CROSS_COMPILE[arch] = cross_compile
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900345
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900346def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
347 extend_post):
348 """Extend matched lines if desired patterns are found before/after already
349 matched lines.
350
351 Arguments:
352 lines: A list of lines handled.
353 matched: A list of line numbers that have been already matched.
354 (will be updated by this function)
355 pre_patterns: A list of regular expression that should be matched as
356 preamble.
357 post_patterns: A list of regular expression that should be matched as
358 postamble.
359 extend_pre: Add the line number of matched preamble to the matched list.
360 extend_post: Add the line number of matched postamble to the matched list.
361 """
362 extended_matched = []
363
364 j = matched[0]
365
366 for i in matched:
367 if i == 0 or i < j:
368 continue
369 j = i
370 while j in matched:
371 j += 1
372 if j >= len(lines):
373 break
374
375 for p in pre_patterns:
376 if p.search(lines[i - 1]):
377 break
378 else:
379 # not matched
380 continue
381
382 for p in post_patterns:
383 if p.search(lines[j]):
384 break
385 else:
386 # not matched
387 continue
388
389 if extend_pre:
390 extended_matched.append(i - 1)
391 if extend_post:
392 extended_matched.append(j)
393
394 matched += extended_matched
395 matched.sort()
396
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900397def cleanup_one_header(header_path, patterns, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900398 """Clean regex-matched lines away from a file.
399
400 Arguments:
401 header_path: path to the cleaned file.
402 patterns: list of regex patterns. Any lines matching to these
403 patterns are deleted.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900404 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900405 """
406 with open(header_path) as f:
407 lines = f.readlines()
408
409 matched = []
410 for i, line in enumerate(lines):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900411 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
412 matched.append(i)
413 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900414 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900415 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900416 matched.append(i)
417 break
418
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900419 if not matched:
420 return
421
422 # remove empty #ifdef ... #endif, successive blank lines
423 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
424 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
425 pattern_endif = re.compile(r'#\s*endif\W') # #endif
426 pattern_blank = re.compile(r'^\s*$') # empty line
427
428 while True:
429 old_matched = copy.copy(matched)
430 extend_matched_lines(lines, matched, [pattern_if],
431 [pattern_endif], True, True)
432 extend_matched_lines(lines, matched, [pattern_elif],
433 [pattern_elif, pattern_endif], True, False)
434 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
435 [pattern_blank], False, True)
436 extend_matched_lines(lines, matched, [pattern_blank],
437 [pattern_elif, pattern_endif], True, False)
438 extend_matched_lines(lines, matched, [pattern_blank],
439 [pattern_blank], True, False)
440 if matched == old_matched:
441 break
442
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900443 tolines = copy.copy(lines)
444
445 for i in reversed(matched):
446 tolines.pop(i)
447
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900448 show_diff(lines, tolines, header_path, options.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900449
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900450 if options.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900451 return
452
453 with open(header_path, 'w') as f:
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900454 for line in tolines:
455 f.write(line)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900456
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900457def cleanup_headers(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900458 """Delete config defines from board headers.
459
460 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900461 configs: A list of CONFIGs to remove.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900462 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900463 """
464 while True:
465 choice = raw_input('Clean up headers? [y/n]: ').lower()
466 print choice
467 if choice == 'y' or choice == 'n':
468 break
469
470 if choice == 'n':
471 return
472
473 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900474 for config in configs:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900475 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
476 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
477
Joe Hershberger60727f52015-05-19 13:21:21 -0500478 for dir in 'include', 'arch', 'board':
479 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900480 if dirpath == os.path.join('include', 'generated'):
481 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500482 for filename in filenames:
483 if not fnmatch.fnmatch(filename, '*~'):
484 cleanup_one_header(os.path.join(dirpath, filename),
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900485 patterns, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900486
487### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900488class Progress:
489
490 """Progress Indicator"""
491
492 def __init__(self, total):
493 """Create a new progress indicator.
494
495 Arguments:
496 total: A number of defconfig files to process.
497 """
498 self.current = 0
499 self.total = total
500
501 def inc(self):
502 """Increment the number of processed defconfig files."""
503
504 self.current += 1
505
506 def show(self):
507 """Display the progress."""
508 print ' %d defconfigs out of %d\r' % (self.current, self.total),
509 sys.stdout.flush()
510
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900511class KconfigParser:
512
513 """A parser of .config and include/autoconf.mk."""
514
515 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
516 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
517
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900518 def __init__(self, configs, options, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900519 """Create a new parser.
520
521 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900522 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900523 options: option flags.
524 build_dir: Build directory.
525 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900526 self.configs = configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900527 self.options = options
Masahiro Yamada1f169922016-05-19 15:52:00 +0900528 self.dotconfig = os.path.join(build_dir, '.config')
529 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
530 self.config_autoconf = os.path.join(build_dir, 'include', 'config',
531 'auto.conf')
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900532 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900533
534 def get_cross_compile(self):
535 """Parse .config file and return CROSS_COMPILE.
536
537 Returns:
538 A string storing the compiler prefix for the architecture.
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900539 Return a NULL string for architectures that do not require
540 compiler prefix (Sandbox and native build is the case).
541 Return None if the specified compiler is missing in your PATH.
542 Caller should distinguish '' and None.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900543 """
544 arch = ''
545 cpu = ''
Masahiro Yamada1f169922016-05-19 15:52:00 +0900546 for line in open(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900547 m = self.re_arch.match(line)
548 if m:
549 arch = m.group(1)
550 continue
551 m = self.re_cpu.match(line)
552 if m:
553 cpu = m.group(1)
554
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900555 if not arch:
556 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900557
558 # fix-up for aarch64
559 if arch == 'arm' and cpu == 'armv8':
560 arch = 'aarch64'
561
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900562 return CROSS_COMPILE.get(arch, None)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900563
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900564 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900565 """Parse .config, defconfig, include/autoconf.mk for one config.
566
567 This function looks for the config options in the lines from
568 defconfig, .config, and include/autoconf.mk in order to decide
569 which action should be taken for this defconfig.
570
571 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900572 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900573 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900574 autoconf_lines: lines from the include/autoconf.mk file.
575
576 Returns:
577 A tupple of the action for this defconfig and the line
578 matched for the config.
579 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900580 not_set = '# %s is not set' % config
581
Masahiro Yamadacc008292016-05-19 15:51:56 +0900582 for line in dotconfig_lines:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900583 line = line.rstrip()
584 if line.startswith(config + '=') or line == not_set:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900585 old_val = line
586 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900587 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900588 return (ACTION_NO_ENTRY, config)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900589
590 for line in autoconf_lines:
591 line = line.rstrip()
592 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900593 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900594 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900595 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900596 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900597
Masahiro Yamadacc008292016-05-19 15:51:56 +0900598 # If this CONFIG is neither bool nor trisate
599 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
600 # tools/scripts/define2mk.sed changes '1' to 'y'.
601 # This is a problem if the CONFIG is int type.
602 # Check the type in Kconfig and handle it correctly.
603 if new_val[-2:] == '=y':
604 new_val = new_val[:-1] + '1'
605
Masahiro Yamada50301592016-06-15 14:33:50 +0900606 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
607 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900608
Masahiro Yamada1d085562016-05-19 15:52:02 +0900609 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900610 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900611
Masahiro Yamadacc008292016-05-19 15:51:56 +0900612 This function parses the generated .config and include/autoconf.mk
613 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900614 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900615
616 Arguments:
617 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900618
619 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900620 Return a tuple of (updated flag, log string).
621 The "updated flag" is True if the .config was updated, False
622 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900623 """
624
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900625 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900626 updated = False
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900627
Masahiro Yamada1f169922016-05-19 15:52:00 +0900628 with open(self.dotconfig) as f:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900629 dotconfig_lines = f.readlines()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900630
Masahiro Yamada1f169922016-05-19 15:52:00 +0900631 with open(self.autoconf) as f:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900632 autoconf_lines = f.readlines()
633
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900634 for config in self.configs:
635 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500636 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900637 results.append(result)
638
639 log = ''
640
641 for (action, value) in results:
642 if action == ACTION_MOVE:
643 actlog = "Move '%s'" % value
644 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900645 elif action == ACTION_NO_ENTRY:
646 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900647 log_color = COLOR_LIGHT_BLUE
Masahiro Yamadacc008292016-05-19 15:51:56 +0900648 elif action == ACTION_NO_CHANGE:
649 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
650 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900651 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900652 else:
653 sys.exit("Internal Error. This should not happen.")
654
Masahiro Yamada1d085562016-05-19 15:52:02 +0900655 log += color_text(self.options.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900656
Masahiro Yamada1f169922016-05-19 15:52:00 +0900657 with open(self.dotconfig, 'a') as f:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900658 for (action, value) in results:
659 if action == ACTION_MOVE:
660 f.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900661 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900662
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900663 self.results = results
Masahiro Yamada1f169922016-05-19 15:52:00 +0900664 os.remove(self.config_autoconf)
665 os.remove(self.autoconf)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900666
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900667 return (updated, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900668
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900669 def check_defconfig(self):
670 """Check the defconfig after savedefconfig
671
672 Returns:
673 Return additional log if moved CONFIGs were removed again by
674 'make savedefconfig'.
675 """
676
677 log = ''
678
679 with open(self.defconfig) as f:
680 defconfig_lines = f.readlines()
681
682 for (action, value) in self.results:
683 if action != ACTION_MOVE:
684 continue
685 if not value + '\n' in defconfig_lines:
686 log += color_text(self.options.color, COLOR_YELLOW,
687 "'%s' was removed by savedefconfig.\n" %
688 value)
689
690 return log
691
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900692class Slot:
693
694 """A slot to store a subprocess.
695
696 Each instance of this class handles one subprocess.
697 This class is useful to control multiple threads
698 for faster processing.
699 """
700
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500701 def __init__(self, configs, options, progress, devnull, make_cmd, reference_src_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900702 """Create a new process slot.
703
704 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900705 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900706 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900707 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900708 devnull: A file object of '/dev/null'.
709 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500710 reference_src_dir: Determine the true starting config state from this
711 source tree.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900712 """
713 self.options = options
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900714 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900715 self.build_dir = tempfile.mkdtemp()
716 self.devnull = devnull
717 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500718 self.reference_src_dir = reference_src_dir
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900719 self.parser = KconfigParser(configs, options, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900720 self.state = STATE_IDLE
721 self.failed_boards = []
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900722 self.suspicious_boards = []
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900723
724 def __del__(self):
725 """Delete the working directory
726
727 This function makes sure the temporary directory is cleaned away
728 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500729 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900730 instance of the class gets unreferenced.
731
732 If the subprocess is still running, wait until it finishes.
733 """
734 if self.state != STATE_IDLE:
735 while self.ps.poll() == None:
736 pass
737 shutil.rmtree(self.build_dir)
738
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900739 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900740 """Assign a new subprocess for defconfig and add it to the slot.
741
742 If the slot is vacant, create a new subprocess for processing the
743 given defconfig and add it to the slot. Just returns False if
744 the slot is occupied (i.e. the current subprocess is still running).
745
746 Arguments:
747 defconfig: defconfig name.
748
749 Returns:
750 Return True on success or False on failure
751 """
752 if self.state != STATE_IDLE:
753 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900754
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900755 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +0900756 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900757 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900758 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900759 return True
760
761 def poll(self):
762 """Check the status of the subprocess and handle it as needed.
763
764 Returns True if the slot is vacant (i.e. in idle state).
765 If the configuration is successfully finished, assign a new
766 subprocess to build include/autoconf.mk.
767 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900768 parse the .config and the include/autoconf.mk, moving
769 config options to the .config as needed.
770 If the .config was updated, run "make savedefconfig" to sync
771 it, update the original defconfig, and then set the slot back
772 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900773
774 Returns:
775 Return True if the subprocess is terminated, False otherwise
776 """
777 if self.state == STATE_IDLE:
778 return True
779
780 if self.ps.poll() == None:
781 return False
782
783 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900784 self.handle_error()
785 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900786 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500787 self.do_savedefconfig()
788 else:
789 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900790 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900791 if self.current_src_dir:
792 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500793 self.do_defconfig()
794 else:
795 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900796 elif self.state == STATE_SAVEDEFCONFIG:
797 self.update_defconfig()
798 else:
799 sys.exit("Internal Error. This should not happen.")
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900800
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900801 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -0500802
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900803 def handle_error(self):
804 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900805
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900806 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
807 "Failed to process.\n")
808 if self.options.verbose:
809 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
810 self.ps.stderr.read())
811 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -0500812
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900813 def do_defconfig(self):
814 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900815
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900816 cmd = list(self.make_cmd)
817 cmd.append(self.defconfig)
818 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900819 stderr=subprocess.PIPE,
820 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900821 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900822
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900823 def do_autoconf(self):
824 """Run 'make include/config/auto.conf'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900825
Joe Hershberger25400092015-05-19 13:21:23 -0500826 self.cross_compile = self.parser.get_cross_compile()
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900827 if self.cross_compile is None:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900828 self.log += color_text(self.options.color, COLOR_YELLOW,
829 "Compiler is missing. Do nothing.\n")
Masahiro Yamada4efef992016-05-19 15:52:03 +0900830 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900831 return
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900832
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900833 cmd = list(self.make_cmd)
Joe Hershberger25400092015-05-19 13:21:23 -0500834 if self.cross_compile:
835 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
Joe Hershberger7740f652015-05-19 13:21:18 -0500836 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900837 cmd.append('include/config/auto.conf')
Joe Hershberger25400092015-05-19 13:21:23 -0500838 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +0900839 stderr=subprocess.PIPE,
840 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900841 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900842
843 def do_savedefconfig(self):
844 """Update the .config and run 'make savedefconfig'."""
845
846 (updated, log) = self.parser.update_dotconfig()
847 self.log += log
848
849 if not self.options.force_sync and not updated:
850 self.finish(True)
851 return
852 if updated:
853 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
854 "Syncing by savedefconfig...\n")
855 else:
856 self.log += "Syncing by savedefconfig (forced by option)...\n"
857
858 cmd = list(self.make_cmd)
859 cmd.append('savedefconfig')
860 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
861 stderr=subprocess.PIPE)
862 self.state = STATE_SAVEDEFCONFIG
863
864 def update_defconfig(self):
865 """Update the input defconfig and go back to the idle state."""
866
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900867 log = self.parser.check_defconfig()
868 if log:
869 self.suspicious_boards.append(self.defconfig)
870 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900871 orig_defconfig = os.path.join('configs', self.defconfig)
872 new_defconfig = os.path.join(self.build_dir, 'defconfig')
873 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
874
875 if updated:
Joe Hershberger06cc1d32016-06-10 14:53:30 -0500876 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
Masahiro Yamadae307fa92016-06-08 11:47:37 +0900877 "defconfig was updated.\n")
878
879 if not self.options.dry_run and updated:
880 shutil.move(new_defconfig, orig_defconfig)
881 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900882
Masahiro Yamada4efef992016-05-19 15:52:03 +0900883 def finish(self, success):
884 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +0900885
886 Arguments:
Masahiro Yamada4efef992016-05-19 15:52:03 +0900887 success: Should be True when the defconfig was processed
888 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +0900889 """
890 # output at least 30 characters to hide the "* defconfigs out of *".
891 log = self.defconfig.ljust(30) + '\n'
892
893 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
894 # Some threads are running in parallel.
895 # Print log atomically to not mix up logs from different threads.
Masahiro Yamada4efef992016-05-19 15:52:03 +0900896 print >> (sys.stdout if success else sys.stderr), log
897
898 if not success:
899 if self.options.exit_on_error:
900 sys.exit("Exit on error.")
901 # If --exit-on-error flag is not set, skip this board and continue.
902 # Record the failed board.
903 self.failed_boards.append(self.defconfig)
904
Masahiro Yamada1d085562016-05-19 15:52:02 +0900905 self.progress.inc()
906 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +0900907 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +0900908
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900909 def get_failed_boards(self):
910 """Returns a list of failed boards (defconfigs) in this slot.
911 """
912 return self.failed_boards
913
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900914 def get_suspicious_boards(self):
915 """Returns a list of boards (defconfigs) with possible misconversion.
916 """
917 return self.suspicious_boards
918
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900919class Slots:
920
921 """Controller of the array of subprocess slots."""
922
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500923 def __init__(self, configs, options, progress, reference_src_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900924 """Create a new slots controller.
925
926 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900927 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900928 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900929 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500930 reference_src_dir: Determine the true starting config state from this
931 source tree.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900932 """
933 self.options = options
934 self.slots = []
935 devnull = get_devnull()
936 make_cmd = get_make_cmd()
937 for i in range(options.jobs):
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900938 self.slots.append(Slot(configs, options, progress, devnull,
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500939 make_cmd, reference_src_dir))
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900940
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900941 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900942 """Add a new subprocess if a vacant slot is found.
943
944 Arguments:
945 defconfig: defconfig name to be put into.
946
947 Returns:
948 Return True on success or False on failure
949 """
950 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900951 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900952 return True
953 return False
954
955 def available(self):
956 """Check if there is a vacant slot.
957
958 Returns:
959 Return True if at lease one vacant slot is found, False otherwise.
960 """
961 for slot in self.slots:
962 if slot.poll():
963 return True
964 return False
965
966 def empty(self):
967 """Check if all slots are vacant.
968
969 Returns:
970 Return True if all the slots are vacant, False otherwise.
971 """
972 ret = True
973 for slot in self.slots:
974 if not slot.poll():
975 ret = False
976 return ret
977
978 def show_failed_boards(self):
979 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada96dccd92016-06-15 14:33:53 +0900980 boards = []
981 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900982
983 for slot in self.slots:
Masahiro Yamada96dccd92016-06-15 14:33:53 +0900984 boards += slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900985
Masahiro Yamada96dccd92016-06-15 14:33:53 +0900986 if boards:
987 boards = '\n'.join(boards) + '\n'
988 msg = "The following boards were not processed due to error:\n"
989 msg += boards
990 msg += "(the list has been saved in %s)\n" % output_file
991 print >> sys.stderr, color_text(self.options.color, COLOR_LIGHT_RED,
992 msg)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900993
Masahiro Yamada96dccd92016-06-15 14:33:53 +0900994 with open(output_file, 'w') as f:
995 f.write(boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -0500996
Masahiro Yamadafc2661e2016-06-15 14:33:54 +0900997 def show_suspicious_boards(self):
998 """Display all boards (defconfigs) with possible misconversion."""
999 boards = []
1000 output_file = 'moveconfig.suspicious'
1001
1002 for slot in self.slots:
1003 boards += slot.get_suspicious_boards()
1004
1005 if boards:
1006 boards = '\n'.join(boards) + '\n'
1007 msg = "The following boards might have been converted incorrectly.\n"
1008 msg += "It is highly recommended to check them manually:\n"
1009 msg += boards
1010 msg += "(the list has been saved in %s)\n" % output_file
1011 print >> sys.stderr, color_text(self.options.color, COLOR_YELLOW,
1012 msg)
1013
1014 with open(output_file, 'w') as f:
1015 f.write(boards)
1016
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001017class ReferenceSource:
1018
1019 """Reference source against which original configs should be parsed."""
1020
1021 def __init__(self, commit):
1022 """Create a reference source directory based on a specified commit.
1023
1024 Arguments:
1025 commit: commit to git-clone
1026 """
1027 self.src_dir = tempfile.mkdtemp()
1028 print "Cloning git repo to a separate work directory..."
1029 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1030 cwd=self.src_dir)
1031 print "Checkout '%s' to build the original autoconf.mk." % \
1032 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip()
1033 subprocess.check_output(['git', 'checkout', commit],
1034 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001035
1036 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001037 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001038
1039 This function makes sure the temporary directory is cleaned away
1040 even if Python suddenly dies due to error. It should be done in here
1041 because it is guaranteed the destructor is always invoked when the
1042 instance of the class gets unreferenced.
1043 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001044 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001045
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001046 def get_dir(self):
1047 """Return the absolute path to the reference source directory."""
1048
1049 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001050
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001051def move_config(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001052 """Move config options to defconfig files.
1053
1054 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001055 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001056 options: option flags
1057 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001058 if len(configs) == 0:
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001059 if options.force_sync:
1060 print 'No CONFIG is specified. You are probably syncing defconfigs.',
1061 else:
1062 print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
1063 else:
1064 print 'Move ' + ', '.join(configs),
1065 print '(jobs: %d)\n' % options.jobs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001066
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001067 if options.git_ref:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001068 reference_src = ReferenceSource(options.git_ref)
1069 reference_src_dir = reference_src.get_dir()
1070 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001071 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001072
Joe Hershberger91040e82015-05-19 13:21:19 -05001073 if options.defconfigs:
1074 defconfigs = [line.strip() for line in open(options.defconfigs)]
1075 for i, defconfig in enumerate(defconfigs):
1076 if not defconfig.endswith('_defconfig'):
1077 defconfigs[i] = defconfig + '_defconfig'
1078 if not os.path.exists(os.path.join('configs', defconfigs[i])):
1079 sys.exit('%s - defconfig does not exist. Stopping.' %
1080 defconfigs[i])
1081 else:
1082 # All the defconfig files to be processed
1083 defconfigs = []
1084 for (dirpath, dirnames, filenames) in os.walk('configs'):
1085 dirpath = dirpath[len('configs') + 1:]
1086 for filename in fnmatch.filter(filenames, '*_defconfig'):
1087 defconfigs.append(os.path.join(dirpath, filename))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001088
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001089 progress = Progress(len(defconfigs))
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001090 slots = Slots(configs, options, progress, reference_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001091
1092 # Main loop to process defconfig files:
1093 # Add a new subprocess into a vacant slot.
1094 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001095 for defconfig in defconfigs:
1096 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001097 while not slots.available():
1098 # No available slot: sleep for a while
1099 time.sleep(SLEEP_TIME)
1100
1101 # wait until all the subprocesses finish
1102 while not slots.empty():
1103 time.sleep(SLEEP_TIME)
1104
Joe Hershberger2e2ce6c2015-05-19 13:21:25 -05001105 print ''
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001106 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001107 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001108
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001109def main():
1110 try:
1111 cpu_count = multiprocessing.cpu_count()
1112 except NotImplementedError:
1113 cpu_count = 1
1114
1115 parser = optparse.OptionParser()
1116 # Add options here
1117 parser.add_option('-c', '--color', action='store_true', default=False,
1118 help='display the log in color')
Joe Hershberger91040e82015-05-19 13:21:19 -05001119 parser.add_option('-d', '--defconfigs', type='string',
1120 help='a file containing a list of defconfigs to move')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001121 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1122 help='perform a trial run (show log with no changes)')
1123 parser.add_option('-e', '--exit-on-error', action='store_true',
1124 default=False,
1125 help='exit immediately on any error')
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001126 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1127 help='force sync by savedefconfig')
Joe Hershberger2144f882015-05-19 13:21:20 -05001128 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1129 action='store_true', default=False,
1130 help='only cleanup the headers')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001131 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1132 help='the number of jobs to run simultaneously')
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001133 parser.add_option('-r', '--git-ref', type='string',
1134 help='the git ref to clone for building the autoconf.mk')
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001135 parser.add_option('-v', '--verbose', action='store_true', default=False,
1136 help='show any build errors as boards are built')
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001137 parser.usage += ' CONFIG ...'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001138
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001139 (options, configs) = parser.parse_args()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001140
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001141 if len(configs) == 0 and not options.force_sync:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001142 parser.print_usage()
1143 sys.exit(1)
1144
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001145 # prefix the option name with CONFIG_ if missing
1146 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1147 for config in configs ]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001148
Joe Hershberger2144f882015-05-19 13:21:20 -05001149 check_top_directory()
1150
1151 if not options.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001152 check_clean_directory()
1153 update_cross_compile(options.color)
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001154 move_config(configs, options)
Joe Hershberger2144f882015-05-19 13:21:20 -05001155
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001156 if configs:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +09001157 cleanup_headers(configs, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001158
1159if __name__ == '__main__':
1160 main()