blob: 93f3781462be519bb4d60822025ef8bce2e0ab82 [file] [log] [blame]
Masahiro Yamadab6160812015-05-20 11:36:07 +09001#!/usr/bin/env python2
2#
3# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
4#
5# SPDX-License-Identifier: GPL-2.0+
6#
7
8"""
9Move config options from headers to defconfig files.
10
11Since Kconfig was introduced to U-Boot, we have worked on moving
12config options from headers to Kconfig (defconfig).
13
14This tool intends to help this tremendous work.
15
16
17Usage
18-----
19
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090020First, you must edit the Kconfig to add the menu entries for the configs
Joe Hershberger166edec2015-05-19 13:21:17 -050021you are moving.
22
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090023And then run this tool giving CONFIG names you want to move.
24For example, if you want to move CONFIG_CMD_USB and CONFIG_SYS_TEXT_BASE,
25simply type as follows:
Masahiro Yamadab6160812015-05-20 11:36:07 +090026
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090027 $ tools/moveconfig.py CONFIG_CMD_USB CONFIG_SYS_TEXT_BASE
Masahiro Yamadab6160812015-05-20 11:36:07 +090028
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090029The tool walks through all the defconfig files and move the given CONFIGs.
Masahiro Yamadab6160812015-05-20 11:36:07 +090030
31The log is also displayed on the terminal.
32
Masahiro Yamada465b7c02016-05-19 15:52:02 +090033The log is printed for each defconfig as follows:
Masahiro Yamadab6160812015-05-20 11:36:07 +090034
Masahiro Yamada465b7c02016-05-19 15:52:02 +090035<defconfig_name>
36 <action1>
37 <action2>
38 <action3>
39 ...
Masahiro Yamadab6160812015-05-20 11:36:07 +090040
Masahiro Yamada465b7c02016-05-19 15:52:02 +090041<defconfig_name> is the name of the defconfig.
42
43<action*> shows what the tool did for that defconfig.
Masahiro Yamada7facf882016-08-21 16:12:36 +090044It looks like one of the following:
Masahiro Yamadab6160812015-05-20 11:36:07 +090045
46 - Move 'CONFIG_... '
47 This config option was moved to the defconfig
48
Masahiro Yamada5643d6e2016-05-19 15:51:56 +090049 - CONFIG_... is not defined in Kconfig. Do nothing.
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 Yamadab6160812015-05-20 11:36:07 +090056
Masahiro Yamada5643d6e2016-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 Yamadab6160812015-05-20 11:36:07 +090060
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +090061 - Compiler is missing. Do nothing.
62 The compiler specified for this architecture was not found
63 in your PATH environment.
64 (If -e option is passed, the tool exits immediately.)
65
66 - Failed to process.
Masahiro Yamadab6160812015-05-20 11:36:07 +090067 An error occurred during processing this defconfig. Skipped.
68 (If -e option is passed, the tool exits immediately on error.)
69
70Finally, you will be asked, Clean up headers? [y/n]:
71
72If you say 'y' here, the unnecessary config defines are removed
73from the config headers (include/configs/*.h).
74It just uses the regex method, so you should not rely on it.
75Just in case, please do 'git diff' to see what happened.
76
77
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090078How does it work?
79-----------------
Masahiro Yamadab6160812015-05-20 11:36:07 +090080
81This tool runs configuration and builds include/autoconf.mk for every
82defconfig. The config options defined in Kconfig appear in the .config
83file (unless they are hidden because of unmet dependency.)
84On the other hand, the config options defined by board headers are seen
85in include/autoconf.mk. The tool looks for the specified options in both
Masahiro Yamadab903c4e2016-05-19 15:51:58 +090086of them to decide the appropriate action for the options. If the given
87config option is found in the .config, but its value does not match the
88one from the board header, the config option in the .config is replaced
89with the define in the board header. Then, the .config is synced by
90"make savedefconfig" and the defconfig is updated with it.
Masahiro Yamadab6160812015-05-20 11:36:07 +090091
92For faster processing, this tool handles multi-threading. It creates
93separate build directories where the out-of-tree build is run. The
94temporary build directories are automatically created and deleted as
95needed. The number of threads are chosen based on the number of the CPU
96cores of your system although you can change it via -j (--jobs) option.
97
98
99Toolchains
100----------
101
102Appropriate toolchain are necessary to generate include/autoconf.mk
103for all the architectures supported by U-Boot. Most of them are available
104at the kernel.org site, some are not provided by kernel.org.
105
106The default per-arch CROSS_COMPILE used by this tool is specified by
107the list below, CROSS_COMPILE. You may wish to update the list to
108use your own. Instead of modifying the list directly, you can give
109them via environments.
110
111
112Available options
113-----------------
114
115 -c, --color
116 Surround each portion of the log with escape sequences to display it
117 in color on the terminal.
118
Joe Hershbergerc6e043a2015-05-19 13:21:19 -0500119 -d, --defconfigs
120 Specify a file containing a list of defconfigs to move
121
Masahiro Yamadab6160812015-05-20 11:36:07 +0900122 -n, --dry-run
Masahiro Yamadab903c4e2016-05-19 15:51:58 +0900123 Perform a trial run that does not make any changes. It is useful to
Masahiro Yamadab6160812015-05-20 11:36:07 +0900124 see what is going to happen before one actually runs it.
125
126 -e, --exit-on-error
127 Exit immediately if Make exits with a non-zero status while processing
128 a defconfig file.
129
Masahiro Yamada83c17672016-05-19 15:52:08 +0900130 -s, --force-sync
131 Do "make savedefconfig" forcibly for all the defconfig files.
132 If not specified, "make savedefconfig" only occurs for cases
133 where at least one CONFIG was moved.
134
Joe Hershberger23475932015-05-19 13:21:20 -0500135 -H, --headers-only
136 Only cleanup the headers; skip the defconfig processing
137
Masahiro Yamadab6160812015-05-20 11:36:07 +0900138 -j, --jobs
139 Specify the number of threads to run simultaneously. If not specified,
140 the number of threads is the same as the number of CPU cores.
141
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500142 -r, --git-ref
143 Specify the git ref to clone for building the autoconf.mk. If unspecified
144 use the CWD. This is useful for when changes to the Kconfig affect the
145 default values and you want to capture the state of the defconfig from
146 before that change was in effect. If in doubt, specify a ref pre-Kconfig
147 changes (use HEAD if Kconfig changes are not committed). Worst case it will
148 take a bit longer to run, but will always do the right thing.
149
Joe Hershberger808b63f2015-05-19 13:21:24 -0500150 -v, --verbose
151 Show any build errors as boards are built
152
Masahiro Yamadab6160812015-05-20 11:36:07 +0900153To see the complete list of supported options, run
154
155 $ tools/moveconfig.py -h
156
157"""
158
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900159import copy
Masahiro Yamada573b3902016-07-25 19:15:25 +0900160import difflib
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900161import filecmp
Masahiro Yamadab6160812015-05-20 11:36:07 +0900162import fnmatch
163import multiprocessing
164import optparse
165import os
166import re
167import shutil
168import subprocess
169import sys
170import tempfile
171import time
172
173SHOW_GNU_MAKE = 'scripts/show-gnu-make'
174SLEEP_TIME=0.03
175
176# Here is the list of cross-tools I use.
177# Most of them are available at kernel.org
Masahiro Yamada7facf882016-08-21 16:12:36 +0900178# (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the following:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900179# arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
180# blackfin: http://sourceforge.net/projects/adi-toolchain/files/
Bin Meng3bb02f62015-09-25 01:22:39 -0700181# nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
Masahiro Yamadab6160812015-05-20 11:36:07 +0900182# nios2: https://sourcery.mentor.com/GNUToolchain/subscription42545
183# sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu
Bin Meng07a50f92016-02-21 21:18:02 -0800184#
185# openrisc kernel.org toolchain is out of date, download latest one from
186# http://opencores.org/or1k/OpenRISC_GNU_tool_chain#Prebuilt_versions
Masahiro Yamadab6160812015-05-20 11:36:07 +0900187CROSS_COMPILE = {
188 'arc': 'arc-linux-',
189 'aarch64': 'aarch64-linux-',
190 'arm': 'arm-unknown-linux-gnueabi-',
191 'avr32': 'avr32-linux-',
192 'blackfin': 'bfin-elf-',
193 'm68k': 'm68k-linux-',
194 'microblaze': 'microblaze-linux-',
195 'mips': 'mips-linux-',
196 'nds32': 'nds32le-linux-',
197 'nios2': 'nios2-linux-gnu-',
Bin Meng07a50f92016-02-21 21:18:02 -0800198 'openrisc': 'or1k-elf-',
Masahiro Yamadab6160812015-05-20 11:36:07 +0900199 'powerpc': 'powerpc-linux-',
200 'sh': 'sh-linux-gnu-',
201 'sparc': 'sparc-linux-',
Masahiro Yamadad1d9d602016-08-21 16:03:08 +0900202 'x86': 'i386-linux-',
203 'xtensa': 'xtensa-linux-'
Masahiro Yamadab6160812015-05-20 11:36:07 +0900204}
205
206STATE_IDLE = 0
207STATE_DEFCONFIG = 1
208STATE_AUTOCONF = 2
Joe Hershberger166edec2015-05-19 13:21:17 -0500209STATE_SAVEDEFCONFIG = 3
Masahiro Yamadab6160812015-05-20 11:36:07 +0900210
211ACTION_MOVE = 0
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900212ACTION_NO_ENTRY = 1
213ACTION_NO_CHANGE = 2
Masahiro Yamadab6160812015-05-20 11:36:07 +0900214
215COLOR_BLACK = '0;30'
216COLOR_RED = '0;31'
217COLOR_GREEN = '0;32'
218COLOR_BROWN = '0;33'
219COLOR_BLUE = '0;34'
220COLOR_PURPLE = '0;35'
221COLOR_CYAN = '0;36'
222COLOR_LIGHT_GRAY = '0;37'
223COLOR_DARK_GRAY = '1;30'
224COLOR_LIGHT_RED = '1;31'
225COLOR_LIGHT_GREEN = '1;32'
226COLOR_YELLOW = '1;33'
227COLOR_LIGHT_BLUE = '1;34'
228COLOR_LIGHT_PURPLE = '1;35'
229COLOR_LIGHT_CYAN = '1;36'
230COLOR_WHITE = '1;37'
231
232### helper functions ###
233def get_devnull():
234 """Get the file object of '/dev/null' device."""
235 try:
236 devnull = subprocess.DEVNULL # py3k
237 except AttributeError:
238 devnull = open(os.devnull, 'wb')
239 return devnull
240
241def check_top_directory():
242 """Exit if we are not at the top of source directory."""
243 for f in ('README', 'Licenses'):
244 if not os.path.exists(f):
245 sys.exit('Please run at the top of source directory.')
246
Masahiro Yamada990e6772016-05-19 15:51:54 +0900247def check_clean_directory():
248 """Exit if the source tree is not clean."""
249 for f in ('.config', 'include/config'):
250 if os.path.exists(f):
251 sys.exit("source tree is not clean, please run 'make mrproper'")
252
Masahiro Yamadab6160812015-05-20 11:36:07 +0900253def get_make_cmd():
254 """Get the command name of GNU Make.
255
256 U-Boot needs GNU Make for building, but the command name is not
257 necessarily "make". (for example, "gmake" on FreeBSD).
258 Returns the most appropriate command name on your system.
259 """
260 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
261 ret = process.communicate()
262 if process.returncode:
263 sys.exit('GNU Make not found')
264 return ret[0].rstrip()
265
Masahiro Yamada58175e32016-07-25 19:15:28 +0900266def get_all_defconfigs():
267 """Get all the defconfig files under the configs/ directory."""
268 defconfigs = []
269 for (dirpath, dirnames, filenames) in os.walk('configs'):
270 dirpath = dirpath[len('configs') + 1:]
271 for filename in fnmatch.filter(filenames, '*_defconfig'):
272 defconfigs.append(os.path.join(dirpath, filename))
273
274 return defconfigs
275
Masahiro Yamadab6160812015-05-20 11:36:07 +0900276def color_text(color_enabled, color, string):
277 """Return colored string."""
278 if color_enabled:
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900279 # LF should not be surrounded by the escape sequence.
280 # Otherwise, additional whitespace or line-feed might be printed.
281 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
282 for s in string.split('\n') ])
Masahiro Yamadab6160812015-05-20 11:36:07 +0900283 else:
284 return string
285
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900286def show_diff(a, b, file_path, color_enabled):
Masahiro Yamada573b3902016-07-25 19:15:25 +0900287 """Show unidified diff.
288
289 Arguments:
290 a: A list of lines (before)
291 b: A list of lines (after)
292 file_path: Path to the file
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900293 color_enabled: Display the diff in color
Masahiro Yamada573b3902016-07-25 19:15:25 +0900294 """
295
296 diff = difflib.unified_diff(a, b,
297 fromfile=os.path.join('a', file_path),
298 tofile=os.path.join('b', file_path))
299
300 for line in diff:
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900301 if line[0] == '-' and line[1] != '-':
302 print color_text(color_enabled, COLOR_RED, line),
303 elif line[0] == '+' and line[1] != '+':
304 print color_text(color_enabled, COLOR_GREEN, line),
305 else:
306 print line,
Masahiro Yamada573b3902016-07-25 19:15:25 +0900307
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900308def update_cross_compile(color_enabled):
Robert P. J. Day8c60f922016-05-04 04:47:31 -0400309 """Update per-arch CROSS_COMPILE via environment variables
Masahiro Yamadab6160812015-05-20 11:36:07 +0900310
311 The default CROSS_COMPILE values are available
312 in the CROSS_COMPILE list above.
313
Robert P. J. Day8c60f922016-05-04 04:47:31 -0400314 You can override them via environment variables
Masahiro Yamadab6160812015-05-20 11:36:07 +0900315 CROSS_COMPILE_{ARCH}.
316
317 For example, if you want to override toolchain prefixes
318 for ARM and PowerPC, you can do as follows in your shell:
319
320 export CROSS_COMPILE_ARM=...
321 export CROSS_COMPILE_POWERPC=...
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900322
323 Then, this function checks if specified compilers really exist in your
324 PATH environment.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900325 """
326 archs = []
327
328 for arch in os.listdir('arch'):
329 if os.path.exists(os.path.join('arch', arch, 'Makefile')):
330 archs.append(arch)
331
332 # arm64 is a special case
333 archs.append('aarch64')
334
335 for arch in archs:
336 env = 'CROSS_COMPILE_' + arch.upper()
337 cross_compile = os.environ.get(env)
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900338 if not cross_compile:
339 cross_compile = CROSS_COMPILE.get(arch, '')
340
341 for path in os.environ["PATH"].split(os.pathsep):
342 gcc_path = os.path.join(path, cross_compile + 'gcc')
343 if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
344 break
345 else:
346 print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
347 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
348 % (cross_compile, arch))
349 cross_compile = None
350
351 CROSS_COMPILE[arch] = cross_compile
Masahiro Yamadab6160812015-05-20 11:36:07 +0900352
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900353def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
354 extend_post):
355 """Extend matched lines if desired patterns are found before/after already
356 matched lines.
357
358 Arguments:
359 lines: A list of lines handled.
360 matched: A list of line numbers that have been already matched.
361 (will be updated by this function)
362 pre_patterns: A list of regular expression that should be matched as
363 preamble.
364 post_patterns: A list of regular expression that should be matched as
365 postamble.
366 extend_pre: Add the line number of matched preamble to the matched list.
367 extend_post: Add the line number of matched postamble to the matched list.
368 """
369 extended_matched = []
370
371 j = matched[0]
372
373 for i in matched:
374 if i == 0 or i < j:
375 continue
376 j = i
377 while j in matched:
378 j += 1
379 if j >= len(lines):
380 break
381
382 for p in pre_patterns:
383 if p.search(lines[i - 1]):
384 break
385 else:
386 # not matched
387 continue
388
389 for p in post_patterns:
390 if p.search(lines[j]):
391 break
392 else:
393 # not matched
394 continue
395
396 if extend_pre:
397 extended_matched.append(i - 1)
398 if extend_post:
399 extended_matched.append(j)
400
401 matched += extended_matched
402 matched.sort()
403
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900404def cleanup_one_header(header_path, patterns, options):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900405 """Clean regex-matched lines away from a file.
406
407 Arguments:
408 header_path: path to the cleaned file.
409 patterns: list of regex patterns. Any lines matching to these
410 patterns are deleted.
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900411 options: option flags.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900412 """
413 with open(header_path) as f:
414 lines = f.readlines()
415
416 matched = []
417 for i, line in enumerate(lines):
Masahiro Yamada6d798ba2016-07-25 19:15:27 +0900418 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
419 matched.append(i)
420 continue
Masahiro Yamadab6160812015-05-20 11:36:07 +0900421 for pattern in patterns:
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900422 if pattern.search(line):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900423 matched.append(i)
424 break
425
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900426 if not matched:
427 return
428
429 # remove empty #ifdef ... #endif, successive blank lines
430 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
431 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
432 pattern_endif = re.compile(r'#\s*endif\W') # #endif
433 pattern_blank = re.compile(r'^\s*$') # empty line
434
435 while True:
436 old_matched = copy.copy(matched)
437 extend_matched_lines(lines, matched, [pattern_if],
438 [pattern_endif], True, True)
439 extend_matched_lines(lines, matched, [pattern_elif],
440 [pattern_elif, pattern_endif], True, False)
441 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
442 [pattern_blank], False, True)
443 extend_matched_lines(lines, matched, [pattern_blank],
444 [pattern_elif, pattern_endif], True, False)
445 extend_matched_lines(lines, matched, [pattern_blank],
446 [pattern_blank], True, False)
447 if matched == old_matched:
448 break
449
Masahiro Yamada573b3902016-07-25 19:15:25 +0900450 tolines = copy.copy(lines)
451
452 for i in reversed(matched):
453 tolines.pop(i)
454
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900455 show_diff(lines, tolines, header_path, options.color)
Masahiro Yamadaea8f5342016-07-25 19:15:24 +0900456
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900457 if options.dry_run:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900458 return
459
460 with open(header_path, 'w') as f:
Masahiro Yamada573b3902016-07-25 19:15:25 +0900461 for line in tolines:
462 f.write(line)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900463
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900464def cleanup_headers(configs, options):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900465 """Delete config defines from board headers.
466
467 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900468 configs: A list of CONFIGs to remove.
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900469 options: option flags.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900470 """
471 while True:
472 choice = raw_input('Clean up headers? [y/n]: ').lower()
473 print choice
474 if choice == 'y' or choice == 'n':
475 break
476
477 if choice == 'n':
478 return
479
480 patterns = []
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900481 for config in configs:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900482 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
483 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
484
Joe Hershbergerb78ad422015-05-19 13:21:21 -0500485 for dir in 'include', 'arch', 'board':
486 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamada28a6d352016-07-25 19:15:22 +0900487 if dirpath == os.path.join('include', 'generated'):
488 continue
Joe Hershbergerb78ad422015-05-19 13:21:21 -0500489 for filename in filenames:
490 if not fnmatch.fnmatch(filename, '*~'):
491 cleanup_one_header(os.path.join(dirpath, filename),
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +0900492 patterns, options)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900493
Masahiro Yamadadce28de2016-07-25 19:15:29 +0900494def cleanup_one_extra_option(defconfig_path, configs, options):
495 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
496
497 Arguments:
498 defconfig_path: path to the cleaned defconfig file.
499 configs: A list of CONFIGs to remove.
500 options: option flags.
501 """
502
503 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
504 end = '"\n'
505
506 with open(defconfig_path) as f:
507 lines = f.readlines()
508
509 for i, line in enumerate(lines):
510 if line.startswith(start) and line.endswith(end):
511 break
512 else:
513 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
514 return
515
516 old_tokens = line[len(start):-len(end)].split(',')
517 new_tokens = []
518
519 for token in old_tokens:
520 pos = token.find('=')
521 if not (token[:pos] if pos >= 0 else token) in configs:
522 new_tokens.append(token)
523
524 if new_tokens == old_tokens:
525 return
526
527 tolines = copy.copy(lines)
528
529 if new_tokens:
530 tolines[i] = start + ','.join(new_tokens) + end
531 else:
532 tolines.pop(i)
533
534 show_diff(lines, tolines, defconfig_path, options.color)
535
536 if options.dry_run:
537 return
538
539 with open(defconfig_path, 'w') as f:
540 for line in tolines:
541 f.write(line)
542
543def cleanup_extra_options(configs, options):
544 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
545
546 Arguments:
547 configs: A list of CONFIGs to remove.
548 options: option flags.
549 """
550 while True:
551 choice = raw_input('Clean up CONFIG_SYS_EXTRA_OPTIONS? [y/n]: ').lower()
552 print choice
553 if choice == 'y' or choice == 'n':
554 break
555
556 if choice == 'n':
557 return
558
559 configs = [ config[len('CONFIG_'):] for config in configs ]
560
561 defconfigs = get_all_defconfigs()
562
563 for defconfig in defconfigs:
564 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
565 options)
566
Masahiro Yamadab6160812015-05-20 11:36:07 +0900567### classes ###
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900568class Progress:
569
570 """Progress Indicator"""
571
572 def __init__(self, total):
573 """Create a new progress indicator.
574
575 Arguments:
576 total: A number of defconfig files to process.
577 """
578 self.current = 0
579 self.total = total
580
581 def inc(self):
582 """Increment the number of processed defconfig files."""
583
584 self.current += 1
585
586 def show(self):
587 """Display the progress."""
588 print ' %d defconfigs out of %d\r' % (self.current, self.total),
589 sys.stdout.flush()
590
Masahiro Yamadab6160812015-05-20 11:36:07 +0900591class KconfigParser:
592
593 """A parser of .config and include/autoconf.mk."""
594
595 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
596 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
597
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900598 def __init__(self, configs, options, build_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900599 """Create a new parser.
600
601 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900602 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900603 options: option flags.
604 build_dir: Build directory.
605 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900606 self.configs = configs
Masahiro Yamadab6160812015-05-20 11:36:07 +0900607 self.options = options
Masahiro Yamada5393b612016-05-19 15:52:00 +0900608 self.dotconfig = os.path.join(build_dir, '.config')
609 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
610 self.config_autoconf = os.path.join(build_dir, 'include', 'config',
611 'auto.conf')
Masahiro Yamada07f98522016-05-19 15:52:06 +0900612 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900613
614 def get_cross_compile(self):
615 """Parse .config file and return CROSS_COMPILE.
616
617 Returns:
618 A string storing the compiler prefix for the architecture.
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900619 Return a NULL string for architectures that do not require
620 compiler prefix (Sandbox and native build is the case).
621 Return None if the specified compiler is missing in your PATH.
622 Caller should distinguish '' and None.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900623 """
624 arch = ''
625 cpu = ''
Masahiro Yamada5393b612016-05-19 15:52:00 +0900626 for line in open(self.dotconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900627 m = self.re_arch.match(line)
628 if m:
629 arch = m.group(1)
630 continue
631 m = self.re_cpu.match(line)
632 if m:
633 cpu = m.group(1)
634
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900635 if not arch:
636 return None
Masahiro Yamadab6160812015-05-20 11:36:07 +0900637
638 # fix-up for aarch64
639 if arch == 'arm' and cpu == 'armv8':
640 arch = 'aarch64'
641
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900642 return CROSS_COMPILE.get(arch, None)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900643
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900644 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900645 """Parse .config, defconfig, include/autoconf.mk for one config.
646
647 This function looks for the config options in the lines from
648 defconfig, .config, and include/autoconf.mk in order to decide
649 which action should be taken for this defconfig.
650
651 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900652 config: CONFIG name to parse.
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900653 dotconfig_lines: lines from the .config file.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900654 autoconf_lines: lines from the include/autoconf.mk file.
655
656 Returns:
657 A tupple of the action for this defconfig and the line
658 matched for the config.
659 """
Masahiro Yamadab6160812015-05-20 11:36:07 +0900660 not_set = '# %s is not set' % config
661
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900662 for line in dotconfig_lines:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900663 line = line.rstrip()
664 if line.startswith(config + '=') or line == not_set:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900665 old_val = line
666 break
Masahiro Yamadab6160812015-05-20 11:36:07 +0900667 else:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900668 return (ACTION_NO_ENTRY, config)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900669
670 for line in autoconf_lines:
671 line = line.rstrip()
672 if line.startswith(config + '='):
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900673 new_val = line
Masahiro Yamadab6160812015-05-20 11:36:07 +0900674 break
Masahiro Yamadab6160812015-05-20 11:36:07 +0900675 else:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900676 new_val = not_set
677
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900678 # If this CONFIG is neither bool nor trisate
679 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
680 # tools/scripts/define2mk.sed changes '1' to 'y'.
681 # This is a problem if the CONFIG is int type.
682 # Check the type in Kconfig and handle it correctly.
683 if new_val[-2:] == '=y':
684 new_val = new_val[:-1] + '1'
685
Masahiro Yamadab48387f2016-06-15 14:33:50 +0900686 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
687 new_val)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900688
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900689 def update_dotconfig(self):
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900690 """Parse files for the config options and update the .config.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900691
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900692 This function parses the generated .config and include/autoconf.mk
693 searching the target options.
Masahiro Yamada7c0d9d22016-05-19 15:51:50 +0900694 Move the config option(s) to the .config as needed.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900695
696 Arguments:
697 defconfig: defconfig name.
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900698
699 Returns:
Masahiro Yamada263d1372016-05-19 15:52:04 +0900700 Return a tuple of (updated flag, log string).
701 The "updated flag" is True if the .config was updated, False
702 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900703 """
704
Masahiro Yamadab6160812015-05-20 11:36:07 +0900705 results = []
Masahiro Yamada263d1372016-05-19 15:52:04 +0900706 updated = False
Masahiro Yamadab6160812015-05-20 11:36:07 +0900707
Masahiro Yamada5393b612016-05-19 15:52:00 +0900708 with open(self.dotconfig) as f:
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900709 dotconfig_lines = f.readlines()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900710
Masahiro Yamada5393b612016-05-19 15:52:00 +0900711 with open(self.autoconf) as f:
Masahiro Yamadab6160812015-05-20 11:36:07 +0900712 autoconf_lines = f.readlines()
713
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900714 for config in self.configs:
715 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger166edec2015-05-19 13:21:17 -0500716 autoconf_lines)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900717 results.append(result)
718
719 log = ''
720
721 for (action, value) in results:
722 if action == ACTION_MOVE:
723 actlog = "Move '%s'" % value
724 log_color = COLOR_LIGHT_GREEN
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900725 elif action == ACTION_NO_ENTRY:
726 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamadab6160812015-05-20 11:36:07 +0900727 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada5643d6e2016-05-19 15:51:56 +0900728 elif action == ACTION_NO_CHANGE:
729 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
730 % value
Masahiro Yamadab6160812015-05-20 11:36:07 +0900731 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamadab6160812015-05-20 11:36:07 +0900732 else:
733 sys.exit("Internal Error. This should not happen.")
734
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900735 log += color_text(self.options.color, log_color, actlog) + '\n'
Masahiro Yamadab6160812015-05-20 11:36:07 +0900736
Masahiro Yamada5393b612016-05-19 15:52:00 +0900737 with open(self.dotconfig, 'a') as f:
Masahiro Yamada953d93b2016-05-19 15:51:49 +0900738 for (action, value) in results:
739 if action == ACTION_MOVE:
740 f.write(value + '\n')
Masahiro Yamada263d1372016-05-19 15:52:04 +0900741 updated = True
Masahiro Yamadab6160812015-05-20 11:36:07 +0900742
Masahiro Yamada07f98522016-05-19 15:52:06 +0900743 self.results = results
Masahiro Yamada5393b612016-05-19 15:52:00 +0900744 os.remove(self.config_autoconf)
745 os.remove(self.autoconf)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900746
Masahiro Yamada263d1372016-05-19 15:52:04 +0900747 return (updated, log)
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900748
Masahiro Yamada07f98522016-05-19 15:52:06 +0900749 def check_defconfig(self):
750 """Check the defconfig after savedefconfig
751
752 Returns:
753 Return additional log if moved CONFIGs were removed again by
754 'make savedefconfig'.
755 """
756
757 log = ''
758
759 with open(self.defconfig) as f:
760 defconfig_lines = f.readlines()
761
762 for (action, value) in self.results:
763 if action != ACTION_MOVE:
764 continue
765 if not value + '\n' in defconfig_lines:
766 log += color_text(self.options.color, COLOR_YELLOW,
767 "'%s' was removed by savedefconfig.\n" %
768 value)
769
770 return log
771
Masahiro Yamadab6160812015-05-20 11:36:07 +0900772class Slot:
773
774 """A slot to store a subprocess.
775
776 Each instance of this class handles one subprocess.
777 This class is useful to control multiple threads
778 for faster processing.
779 """
780
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500781 def __init__(self, configs, options, progress, devnull, make_cmd, reference_src_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900782 """Create a new process slot.
783
784 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +0900785 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900786 options: option flags.
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900787 progress: A progress indicator.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900788 devnull: A file object of '/dev/null'.
789 make_cmd: command name of GNU Make.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500790 reference_src_dir: Determine the true starting config state from this
791 source tree.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900792 """
793 self.options = options
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900794 self.progress = progress
Masahiro Yamadab6160812015-05-20 11:36:07 +0900795 self.build_dir = tempfile.mkdtemp()
796 self.devnull = devnull
797 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500798 self.reference_src_dir = reference_src_dir
Masahiro Yamada69e2bbc2016-05-19 15:52:01 +0900799 self.parser = KconfigParser(configs, options, self.build_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900800 self.state = STATE_IDLE
Masahiro Yamada1271b672016-08-22 22:18:20 +0900801 self.failed_boards = set()
802 self.suspicious_boards = set()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900803
804 def __del__(self):
805 """Delete the working directory
806
807 This function makes sure the temporary directory is cleaned away
808 even if Python suddenly dies due to error. It should be done in here
Joe Hershberger640de872016-06-10 14:53:29 -0500809 because it is guaranteed the destructor is always invoked when the
Masahiro Yamadab6160812015-05-20 11:36:07 +0900810 instance of the class gets unreferenced.
811
812 If the subprocess is still running, wait until it finishes.
813 """
814 if self.state != STATE_IDLE:
815 while self.ps.poll() == None:
816 pass
817 shutil.rmtree(self.build_dir)
818
Masahiro Yamadacefaa582016-05-19 15:51:55 +0900819 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +0900820 """Assign a new subprocess for defconfig and add it to the slot.
821
822 If the slot is vacant, create a new subprocess for processing the
823 given defconfig and add it to the slot. Just returns False if
824 the slot is occupied (i.e. the current subprocess is still running).
825
826 Arguments:
827 defconfig: defconfig name.
828
829 Returns:
830 Return True on success or False on failure
831 """
832 if self.state != STATE_IDLE:
833 return False
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900834
Masahiro Yamadab6160812015-05-20 11:36:07 +0900835 self.defconfig = defconfig
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900836 self.log = ''
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900837 self.current_src_dir = self.reference_src_dir
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900838 self.do_defconfig()
Masahiro Yamadab6160812015-05-20 11:36:07 +0900839 return True
840
841 def poll(self):
842 """Check the status of the subprocess and handle it as needed.
843
844 Returns True if the slot is vacant (i.e. in idle state).
845 If the configuration is successfully finished, assign a new
846 subprocess to build include/autoconf.mk.
847 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada263d1372016-05-19 15:52:04 +0900848 parse the .config and the include/autoconf.mk, moving
849 config options to the .config as needed.
850 If the .config was updated, run "make savedefconfig" to sync
851 it, update the original defconfig, and then set the slot back
852 to the idle state.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900853
854 Returns:
855 Return True if the subprocess is terminated, False otherwise
856 """
857 if self.state == STATE_IDLE:
858 return True
859
860 if self.ps.poll() == None:
861 return False
862
863 if self.ps.poll() != 0:
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900864 self.handle_error()
865 elif self.state == STATE_DEFCONFIG:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900866 if self.reference_src_dir and not self.current_src_dir:
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500867 self.do_savedefconfig()
868 else:
869 self.do_autoconf()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900870 elif self.state == STATE_AUTOCONF:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900871 if self.current_src_dir:
872 self.current_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -0500873 self.do_defconfig()
874 else:
875 self.do_savedefconfig()
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900876 elif self.state == STATE_SAVEDEFCONFIG:
877 self.update_defconfig()
878 else:
879 sys.exit("Internal Error. This should not happen.")
Masahiro Yamadab6160812015-05-20 11:36:07 +0900880
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900881 return True if self.state == STATE_IDLE else False
Joe Hershberger166edec2015-05-19 13:21:17 -0500882
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900883 def handle_error(self):
884 """Handle error cases."""
Masahiro Yamada83c17672016-05-19 15:52:08 +0900885
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900886 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
887 "Failed to process.\n")
888 if self.options.verbose:
889 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
890 self.ps.stderr.read())
891 self.finish(False)
Joe Hershberger166edec2015-05-19 13:21:17 -0500892
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900893 def do_defconfig(self):
894 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900895
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900896 cmd = list(self.make_cmd)
897 cmd.append(self.defconfig)
898 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900899 stderr=subprocess.PIPE,
900 cwd=self.current_src_dir)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900901 self.state = STATE_DEFCONFIG
Masahiro Yamada0f6beda2016-05-19 15:52:07 +0900902
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900903 def do_autoconf(self):
904 """Run 'make include/config/auto.conf'."""
Masahiro Yamadab6160812015-05-20 11:36:07 +0900905
Joe Hershberger11b02702015-05-19 13:21:23 -0500906 self.cross_compile = self.parser.get_cross_compile()
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900907 if self.cross_compile is None:
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900908 self.log += color_text(self.options.color, COLOR_YELLOW,
909 "Compiler is missing. Do nothing.\n")
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900910 self.finish(False)
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900911 return
Masahiro Yamadac4d76eb2016-05-19 15:51:53 +0900912
Masahiro Yamadab6160812015-05-20 11:36:07 +0900913 cmd = list(self.make_cmd)
Joe Hershberger11b02702015-05-19 13:21:23 -0500914 if self.cross_compile:
915 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
Joe Hershberger765442b2015-05-19 13:21:18 -0500916 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Masahiro Yamadab6160812015-05-20 11:36:07 +0900917 cmd.append('include/config/auto.conf')
Joe Hershberger11b02702015-05-19 13:21:23 -0500918 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamada8f5256a2016-06-15 14:33:52 +0900919 stderr=subprocess.PIPE,
920 cwd=self.current_src_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900921 self.state = STATE_AUTOCONF
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900922
923 def do_savedefconfig(self):
924 """Update the .config and run 'make savedefconfig'."""
925
926 (updated, log) = self.parser.update_dotconfig()
927 self.log += log
928
929 if not self.options.force_sync and not updated:
930 self.finish(True)
931 return
932 if updated:
933 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
934 "Syncing by savedefconfig...\n")
935 else:
936 self.log += "Syncing by savedefconfig (forced by option)...\n"
937
938 cmd = list(self.make_cmd)
939 cmd.append('savedefconfig')
940 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
941 stderr=subprocess.PIPE)
942 self.state = STATE_SAVEDEFCONFIG
943
944 def update_defconfig(self):
945 """Update the input defconfig and go back to the idle state."""
946
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +0900947 log = self.parser.check_defconfig()
948 if log:
Masahiro Yamada1271b672016-08-22 22:18:20 +0900949 self.suspicious_boards.add(self.defconfig)
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +0900950 self.log += log
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900951 orig_defconfig = os.path.join('configs', self.defconfig)
952 new_defconfig = os.path.join(self.build_dir, 'defconfig')
953 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
954
955 if updated:
Joe Hershberger93f1c2d2016-06-10 14:53:30 -0500956 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
Masahiro Yamadacb256cb2016-06-08 11:47:37 +0900957 "defconfig was updated.\n")
958
959 if not self.options.dry_run and updated:
960 shutil.move(new_defconfig, orig_defconfig)
961 self.finish(True)
Masahiro Yamadab6160812015-05-20 11:36:07 +0900962
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900963 def finish(self, success):
964 """Display log along with progress and go to the idle state.
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900965
966 Arguments:
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900967 success: Should be True when the defconfig was processed
968 successfully, or False when it fails.
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900969 """
970 # output at least 30 characters to hide the "* defconfigs out of *".
971 log = self.defconfig.ljust(30) + '\n'
972
973 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
974 # Some threads are running in parallel.
975 # Print log atomically to not mix up logs from different threads.
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900976 print >> (sys.stdout if success else sys.stderr), log
977
978 if not success:
979 if self.options.exit_on_error:
980 sys.exit("Exit on error.")
981 # If --exit-on-error flag is not set, skip this board and continue.
982 # Record the failed board.
Masahiro Yamada1271b672016-08-22 22:18:20 +0900983 self.failed_boards.add(self.defconfig)
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900984
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900985 self.progress.inc()
986 self.progress.show()
Masahiro Yamada274a5ee2016-05-19 15:52:03 +0900987 self.state = STATE_IDLE
Masahiro Yamada465b7c02016-05-19 15:52:02 +0900988
Masahiro Yamadab6160812015-05-20 11:36:07 +0900989 def get_failed_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +0900990 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamadab6160812015-05-20 11:36:07 +0900991 """
992 return self.failed_boards
993
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +0900994 def get_suspicious_boards(self):
Masahiro Yamada1271b672016-08-22 22:18:20 +0900995 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +0900996 """
997 return self.suspicious_boards
998
Masahiro Yamadab6160812015-05-20 11:36:07 +0900999class Slots:
1000
1001 """Controller of the array of subprocess slots."""
1002
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001003 def __init__(self, configs, options, progress, reference_src_dir):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001004 """Create a new slots controller.
1005
1006 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001007 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001008 options: option flags.
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001009 progress: A progress indicator.
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001010 reference_src_dir: Determine the true starting config state from this
1011 source tree.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001012 """
1013 self.options = options
1014 self.slots = []
1015 devnull = get_devnull()
1016 make_cmd = get_make_cmd()
1017 for i in range(options.jobs):
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001018 self.slots.append(Slot(configs, options, progress, devnull,
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001019 make_cmd, reference_src_dir))
Masahiro Yamadab6160812015-05-20 11:36:07 +09001020
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001021 def add(self, defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001022 """Add a new subprocess if a vacant slot is found.
1023
1024 Arguments:
1025 defconfig: defconfig name to be put into.
1026
1027 Returns:
1028 Return True on success or False on failure
1029 """
1030 for slot in self.slots:
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001031 if slot.add(defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001032 return True
1033 return False
1034
1035 def available(self):
1036 """Check if there is a vacant slot.
1037
1038 Returns:
1039 Return True if at lease one vacant slot is found, False otherwise.
1040 """
1041 for slot in self.slots:
1042 if slot.poll():
1043 return True
1044 return False
1045
1046 def empty(self):
1047 """Check if all slots are vacant.
1048
1049 Returns:
1050 Return True if all the slots are vacant, False otherwise.
1051 """
1052 ret = True
1053 for slot in self.slots:
1054 if not slot.poll():
1055 ret = False
1056 return ret
1057
1058 def show_failed_boards(self):
1059 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada1271b672016-08-22 22:18:20 +09001060 boards = set()
Masahiro Yamada0153f032016-06-15 14:33:53 +09001061 output_file = 'moveconfig.failed'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001062
1063 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001064 boards |= slot.get_failed_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001065
Masahiro Yamada0153f032016-06-15 14:33:53 +09001066 if boards:
1067 boards = '\n'.join(boards) + '\n'
1068 msg = "The following boards were not processed due to error:\n"
1069 msg += boards
1070 msg += "(the list has been saved in %s)\n" % output_file
1071 print >> sys.stderr, color_text(self.options.color, COLOR_LIGHT_RED,
1072 msg)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001073
Masahiro Yamada0153f032016-06-15 14:33:53 +09001074 with open(output_file, 'w') as f:
1075 f.write(boards)
Joe Hershbergerdade12e2015-05-19 13:21:22 -05001076
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001077 def show_suspicious_boards(self):
1078 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada1271b672016-08-22 22:18:20 +09001079 boards = set()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001080 output_file = 'moveconfig.suspicious'
1081
1082 for slot in self.slots:
Masahiro Yamada1271b672016-08-22 22:18:20 +09001083 boards |= slot.get_suspicious_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001084
1085 if boards:
1086 boards = '\n'.join(boards) + '\n'
1087 msg = "The following boards might have been converted incorrectly.\n"
1088 msg += "It is highly recommended to check them manually:\n"
1089 msg += boards
1090 msg += "(the list has been saved in %s)\n" % output_file
1091 print >> sys.stderr, color_text(self.options.color, COLOR_YELLOW,
1092 msg)
1093
1094 with open(output_file, 'w') as f:
1095 f.write(boards)
1096
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001097class ReferenceSource:
1098
1099 """Reference source against which original configs should be parsed."""
1100
1101 def __init__(self, commit):
1102 """Create a reference source directory based on a specified commit.
1103
1104 Arguments:
1105 commit: commit to git-clone
1106 """
1107 self.src_dir = tempfile.mkdtemp()
1108 print "Cloning git repo to a separate work directory..."
1109 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1110 cwd=self.src_dir)
1111 print "Checkout '%s' to build the original autoconf.mk." % \
1112 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip()
1113 subprocess.check_output(['git', 'checkout', commit],
1114 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001115
1116 def __del__(self):
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001117 """Delete the reference source directory
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001118
1119 This function makes sure the temporary directory is cleaned away
1120 even if Python suddenly dies due to error. It should be done in here
1121 because it is guaranteed the destructor is always invoked when the
1122 instance of the class gets unreferenced.
1123 """
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001124 shutil.rmtree(self.src_dir)
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001125
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001126 def get_dir(self):
1127 """Return the absolute path to the reference source directory."""
1128
1129 return self.src_dir
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001130
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001131def move_config(configs, options):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001132 """Move config options to defconfig files.
1133
1134 Arguments:
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001135 configs: A list of CONFIGs to move.
Masahiro Yamadab6160812015-05-20 11:36:07 +09001136 options: option flags
1137 """
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001138 if len(configs) == 0:
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001139 if options.force_sync:
1140 print 'No CONFIG is specified. You are probably syncing defconfigs.',
1141 else:
1142 print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
1143 else:
1144 print 'Move ' + ', '.join(configs),
1145 print '(jobs: %d)\n' % options.jobs
Masahiro Yamadab6160812015-05-20 11:36:07 +09001146
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001147 if options.git_ref:
Masahiro Yamada2e74fee2016-06-15 14:33:51 +09001148 reference_src = ReferenceSource(options.git_ref)
1149 reference_src_dir = reference_src.get_dir()
1150 else:
Masahiro Yamada8f5256a2016-06-15 14:33:52 +09001151 reference_src_dir = None
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001152
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001153 if options.defconfigs:
1154 defconfigs = [line.strip() for line in open(options.defconfigs)]
1155 for i, defconfig in enumerate(defconfigs):
1156 if not defconfig.endswith('_defconfig'):
1157 defconfigs[i] = defconfig + '_defconfig'
1158 if not os.path.exists(os.path.join('configs', defconfigs[i])):
1159 sys.exit('%s - defconfig does not exist. Stopping.' %
1160 defconfigs[i])
1161 else:
Masahiro Yamada58175e32016-07-25 19:15:28 +09001162 defconfigs = get_all_defconfigs()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001163
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001164 progress = Progress(len(defconfigs))
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001165 slots = Slots(configs, options, progress, reference_src_dir)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001166
1167 # Main loop to process defconfig files:
1168 # Add a new subprocess into a vacant slot.
1169 # Sleep if there is no available slot.
Masahiro Yamadacefaa582016-05-19 15:51:55 +09001170 for defconfig in defconfigs:
1171 while not slots.add(defconfig):
Masahiro Yamadab6160812015-05-20 11:36:07 +09001172 while not slots.available():
1173 # No available slot: sleep for a while
1174 time.sleep(SLEEP_TIME)
1175
1176 # wait until all the subprocesses finish
1177 while not slots.empty():
1178 time.sleep(SLEEP_TIME)
1179
Joe Hershberger3fa1ab72015-05-19 13:21:25 -05001180 print ''
Masahiro Yamadab6160812015-05-20 11:36:07 +09001181 slots.show_failed_boards()
Masahiro Yamada3c9bfea2016-06-15 14:33:54 +09001182 slots.show_suspicious_boards()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001183
Masahiro Yamadab6160812015-05-20 11:36:07 +09001184def main():
1185 try:
1186 cpu_count = multiprocessing.cpu_count()
1187 except NotImplementedError:
1188 cpu_count = 1
1189
1190 parser = optparse.OptionParser()
1191 # Add options here
1192 parser.add_option('-c', '--color', action='store_true', default=False,
1193 help='display the log in color')
Joe Hershbergerc6e043a2015-05-19 13:21:19 -05001194 parser.add_option('-d', '--defconfigs', type='string',
1195 help='a file containing a list of defconfigs to move')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001196 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1197 help='perform a trial run (show log with no changes)')
1198 parser.add_option('-e', '--exit-on-error', action='store_true',
1199 default=False,
1200 help='exit immediately on any error')
Masahiro Yamada83c17672016-05-19 15:52:08 +09001201 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1202 help='force sync by savedefconfig')
Joe Hershberger23475932015-05-19 13:21:20 -05001203 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1204 action='store_true', default=False,
1205 help='only cleanup the headers')
Masahiro Yamadab6160812015-05-20 11:36:07 +09001206 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1207 help='the number of jobs to run simultaneously')
Joe Hershbergerb1a570f2016-06-10 14:53:32 -05001208 parser.add_option('-r', '--git-ref', type='string',
1209 help='the git ref to clone for building the autoconf.mk')
Joe Hershberger808b63f2015-05-19 13:21:24 -05001210 parser.add_option('-v', '--verbose', action='store_true', default=False,
1211 help='show any build errors as boards are built')
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001212 parser.usage += ' CONFIG ...'
Masahiro Yamadab6160812015-05-20 11:36:07 +09001213
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001214 (options, configs) = parser.parse_args()
Masahiro Yamadab6160812015-05-20 11:36:07 +09001215
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001216 if len(configs) == 0 and not options.force_sync:
Masahiro Yamadab6160812015-05-20 11:36:07 +09001217 parser.print_usage()
1218 sys.exit(1)
1219
Masahiro Yamadab903c4e2016-05-19 15:51:58 +09001220 # prefix the option name with CONFIG_ if missing
1221 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1222 for config in configs ]
Masahiro Yamadab6160812015-05-20 11:36:07 +09001223
Joe Hershberger23475932015-05-19 13:21:20 -05001224 check_top_directory()
1225
1226 if not options.cleanup_headers_only:
Masahiro Yamadad0a9d2a2016-07-25 19:15:23 +09001227 check_clean_directory()
1228 update_cross_compile(options.color)
Masahiro Yamadab80a6672016-05-19 15:51:57 +09001229 move_config(configs, options)
Joe Hershberger23475932015-05-19 13:21:20 -05001230
Masahiro Yamada9566abd2016-05-19 15:52:09 +09001231 if configs:
Masahiro Yamadaa1a4b092016-07-25 19:15:26 +09001232 cleanup_headers(configs, options)
Masahiro Yamadadce28de2016-07-25 19:15:29 +09001233 cleanup_extra_options(configs, options)
Masahiro Yamadab6160812015-05-20 11:36:07 +09001234
1235if __name__ == '__main__':
1236 main()