blob: eb7ada28382947628a7c88cc12a81d75a72bf8ab [file] [log] [blame]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001#!/usr/bin/env python2
2#
3# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
4#
5# SPDX-License-Identifier: GPL-2.0+
6#
7
8"""
9Move config options from headers to defconfig files.
10
11Since Kconfig was introduced to U-Boot, we have worked on moving
12config options from headers to Kconfig (defconfig).
13
14This tool intends to help this tremendous work.
15
16
17Usage
18-----
19
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090020First, you must edit the Kconfig to add the menu entries for the configs
Joe Hershberger96464ba2015-05-19 13:21:17 -050021you are moving.
22
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090023And then run this tool giving CONFIG names you want to move.
24For example, if you want to move CONFIG_CMD_USB and CONFIG_SYS_TEXT_BASE,
25simply type as follows:
Masahiro Yamada5a27c732015-05-20 11:36:07 +090026
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090027 $ tools/moveconfig.py CONFIG_CMD_USB CONFIG_SYS_TEXT_BASE
Masahiro Yamada5a27c732015-05-20 11:36:07 +090028
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090029The tool walks through all the defconfig files and move the given CONFIGs.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090030
31The log is also displayed on the terminal.
32
Masahiro Yamada1d085562016-05-19 15:52:02 +090033The log is printed for each defconfig as follows:
Masahiro Yamada5a27c732015-05-20 11:36:07 +090034
Masahiro Yamada1d085562016-05-19 15:52:02 +090035<defconfig_name>
36 <action1>
37 <action2>
38 <action3>
39 ...
Masahiro Yamada5a27c732015-05-20 11:36:07 +090040
Masahiro Yamada1d085562016-05-19 15:52:02 +090041<defconfig_name> is the name of the defconfig.
42
43<action*> shows what the tool did for that defconfig.
Masahiro Yamadac21fc7e2016-08-21 16:12:36 +090044It looks like one of the following:
Masahiro Yamada5a27c732015-05-20 11:36:07 +090045
46 - Move 'CONFIG_... '
47 This config option was moved to the defconfig
48
Masahiro Yamadacc008292016-05-19 15:51:56 +090049 - CONFIG_... is not defined in Kconfig. Do nothing.
Masahiro Yamada916224c2016-08-22 22:18:21 +090050 The entry for this CONFIG was not found in Kconfig. The option is not
51 defined in the config header, either. So, this case can be just skipped.
52
53 - CONFIG_... is not defined in Kconfig (suspicious). Do nothing.
54 This option is defined in the config header, but its entry was not found
55 in Kconfig.
Masahiro Yamadacc008292016-05-19 15:51:56 +090056 There are two common cases:
57 - You forgot to create an entry for the CONFIG before running
58 this tool, or made a typo in a CONFIG passed to this tool.
59 - The entry was hidden due to unmet 'depends on'.
Masahiro Yamada916224c2016-08-22 22:18:21 +090060 The tool does not know if the result is reasonable, so please check it
61 manually.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090062
Masahiro Yamadacc008292016-05-19 15:51:56 +090063 - 'CONFIG_...' is the same as the define in Kconfig. Do nothing.
64 The define in the config header matched the one in Kconfig.
65 We do not need to touch it.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090066
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +090067 - Compiler is missing. Do nothing.
68 The compiler specified for this architecture was not found
69 in your PATH environment.
70 (If -e option is passed, the tool exits immediately.)
71
72 - Failed to process.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090073 An error occurred during processing this defconfig. Skipped.
74 (If -e option is passed, the tool exits immediately on error.)
75
76Finally, you will be asked, Clean up headers? [y/n]:
77
78If you say 'y' here, the unnecessary config defines are removed
79from the config headers (include/configs/*.h).
80It just uses the regex method, so you should not rely on it.
81Just in case, please do 'git diff' to see what happened.
82
83
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090084How does it work?
85-----------------
Masahiro Yamada5a27c732015-05-20 11:36:07 +090086
87This tool runs configuration and builds include/autoconf.mk for every
88defconfig. The config options defined in Kconfig appear in the .config
89file (unless they are hidden because of unmet dependency.)
90On the other hand, the config options defined by board headers are seen
91in include/autoconf.mk. The tool looks for the specified options in both
Masahiro Yamadab6ef3932016-05-19 15:51:58 +090092of them to decide the appropriate action for the options. If the given
93config option is found in the .config, but its value does not match the
94one from the board header, the config option in the .config is replaced
95with the define in the board header. Then, the .config is synced by
96"make savedefconfig" and the defconfig is updated with it.
Masahiro Yamada5a27c732015-05-20 11:36:07 +090097
98For faster processing, this tool handles multi-threading. It creates
99separate build directories where the out-of-tree build is run. The
100temporary build directories are automatically created and deleted as
101needed. The number of threads are chosen based on the number of the CPU
102cores of your system although you can change it via -j (--jobs) option.
103
104
105Toolchains
106----------
107
108Appropriate toolchain are necessary to generate include/autoconf.mk
109for all the architectures supported by U-Boot. Most of them are available
110at the kernel.org site, some are not provided by kernel.org.
111
112The default per-arch CROSS_COMPILE used by this tool is specified by
113the list below, CROSS_COMPILE. You may wish to update the list to
114use your own. Instead of modifying the list directly, you can give
115them via environments.
116
117
Simon Glassddf91c62017-06-01 19:39:00 -0600118Tips and trips
119--------------
120
121To sync only X86 defconfigs:
122
123 ./tools/moveconfig.py -s -d <(grep -l X86 configs/*)
124
125or:
126
127 grep -l X86 configs/* | ./tools/moveconfig.py -s -d -
128
129To process CONFIG_CMD_FPGAD only for a subset of configs based on path match:
130
131 ls configs/{hrcon*,iocon*,strider*} | \
132 ./tools/moveconfig.py -Cy CONFIG_CMD_FPGAD -d -
133
134
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900135Available options
136-----------------
137
138 -c, --color
139 Surround each portion of the log with escape sequences to display it
140 in color on the terminal.
141
Simon Glass9ede2122016-09-12 23:18:21 -0600142 -C, --commit
143 Create a git commit with the changes when the operation is complete. A
144 standard commit message is used which may need to be edited.
145
Joe Hershberger91040e82015-05-19 13:21:19 -0500146 -d, --defconfigs
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900147 Specify a file containing a list of defconfigs to move. The defconfig
Simon Glassddf91c62017-06-01 19:39:00 -0600148 files can be given with shell-style wildcards. Use '-' to read from stdin.
Joe Hershberger91040e82015-05-19 13:21:19 -0500149
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900150 -n, --dry-run
Masahiro Yamadab6ef3932016-05-19 15:51:58 +0900151 Perform a trial run that does not make any changes. It is useful to
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900152 see what is going to happen before one actually runs it.
153
154 -e, --exit-on-error
155 Exit immediately if Make exits with a non-zero status while processing
156 a defconfig file.
157
Masahiro Yamada8513dc02016-05-19 15:52:08 +0900158 -s, --force-sync
159 Do "make savedefconfig" forcibly for all the defconfig files.
160 If not specified, "make savedefconfig" only occurs for cases
161 where at least one CONFIG was moved.
162
Masahiro Yamada07913d12016-08-22 22:18:22 +0900163 -S, --spl
164 Look for moved config options in spl/include/autoconf.mk instead of
165 include/autoconf.mk. This is useful for moving options for SPL build
166 because SPL related options (mostly prefixed with CONFIG_SPL_) are
167 sometimes blocked by CONFIG_SPL_BUILD ifdef conditionals.
168
Joe Hershberger2144f882015-05-19 13:21:20 -0500169 -H, --headers-only
170 Only cleanup the headers; skip the defconfig processing
171
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900172 -j, --jobs
173 Specify the number of threads to run simultaneously. If not specified,
174 the number of threads is the same as the number of CPU cores.
175
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500176 -r, --git-ref
177 Specify the git ref to clone for building the autoconf.mk. If unspecified
178 use the CWD. This is useful for when changes to the Kconfig affect the
179 default values and you want to capture the state of the defconfig from
180 before that change was in effect. If in doubt, specify a ref pre-Kconfig
181 changes (use HEAD if Kconfig changes are not committed). Worst case it will
182 take a bit longer to run, but will always do the right thing.
183
Joe Hershberger95bf9c72015-05-19 13:21:24 -0500184 -v, --verbose
185 Show any build errors as boards are built
186
Simon Glass6b403df2016-09-12 23:18:20 -0600187 -y, --yes
188 Instead of prompting, automatically go ahead with all operations. This
Simon Glassddf91c62017-06-01 19:39:00 -0600189 includes cleaning up headers, CONFIG_SYS_EXTRA_OPTIONS, the config whitelist
190 and the README.
Simon Glass6b403df2016-09-12 23:18:20 -0600191
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900192To see the complete list of supported options, run
193
194 $ tools/moveconfig.py -h
195
196"""
197
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900198import copy
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900199import difflib
Masahiro Yamadac8e1b102016-05-19 15:52:07 +0900200import filecmp
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900201import fnmatch
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900202import glob
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900203import multiprocessing
204import optparse
205import os
206import re
207import shutil
208import subprocess
209import sys
210import tempfile
211import time
212
213SHOW_GNU_MAKE = 'scripts/show-gnu-make'
214SLEEP_TIME=0.03
215
216# Here is the list of cross-tools I use.
217# Most of them are available at kernel.org
Masahiro Yamadac21fc7e2016-08-21 16:12:36 +0900218# (https://www.kernel.org/pub/tools/crosstool/files/bin/), except the following:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900219# arc: https://github.com/foss-for-synopsys-dwc-arc-processors/toolchain/releases
Bin Meng4440ece2015-09-25 01:22:39 -0700220# nds32: http://osdk.andestech.com/packages/nds32le-linux-glibc-v1.tgz
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900221# nios2: https://sourcery.mentor.com/GNUToolchain/subscription42545
222# sh: http://sourcery.mentor.com/public/gnu_toolchain/sh-linux-gnu
223CROSS_COMPILE = {
224 'arc': 'arc-linux-',
225 'aarch64': 'aarch64-linux-',
226 'arm': 'arm-unknown-linux-gnueabi-',
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900227 'm68k': 'm68k-linux-',
228 'microblaze': 'microblaze-linux-',
229 'mips': 'mips-linux-',
230 'nds32': 'nds32le-linux-',
231 'nios2': 'nios2-linux-gnu-',
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900232 'powerpc': 'powerpc-linux-',
233 'sh': 'sh-linux-gnu-',
Masahiro Yamada88e13462016-08-21 16:03:08 +0900234 'x86': 'i386-linux-',
235 'xtensa': 'xtensa-linux-'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900236}
237
238STATE_IDLE = 0
239STATE_DEFCONFIG = 1
240STATE_AUTOCONF = 2
Joe Hershberger96464ba2015-05-19 13:21:17 -0500241STATE_SAVEDEFCONFIG = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900242
243ACTION_MOVE = 0
Masahiro Yamadacc008292016-05-19 15:51:56 +0900244ACTION_NO_ENTRY = 1
Masahiro Yamada916224c2016-08-22 22:18:21 +0900245ACTION_NO_ENTRY_WARN = 2
246ACTION_NO_CHANGE = 3
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900247
248COLOR_BLACK = '0;30'
249COLOR_RED = '0;31'
250COLOR_GREEN = '0;32'
251COLOR_BROWN = '0;33'
252COLOR_BLUE = '0;34'
253COLOR_PURPLE = '0;35'
254COLOR_CYAN = '0;36'
255COLOR_LIGHT_GRAY = '0;37'
256COLOR_DARK_GRAY = '1;30'
257COLOR_LIGHT_RED = '1;31'
258COLOR_LIGHT_GREEN = '1;32'
259COLOR_YELLOW = '1;33'
260COLOR_LIGHT_BLUE = '1;34'
261COLOR_LIGHT_PURPLE = '1;35'
262COLOR_LIGHT_CYAN = '1;36'
263COLOR_WHITE = '1;37'
264
Simon Glassf3b8e642017-06-01 19:39:01 -0600265AUTO_CONF_PATH = 'include/config/auto.conf'
266
267
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900268### helper functions ###
269def get_devnull():
270 """Get the file object of '/dev/null' device."""
271 try:
272 devnull = subprocess.DEVNULL # py3k
273 except AttributeError:
274 devnull = open(os.devnull, 'wb')
275 return devnull
276
277def check_top_directory():
278 """Exit if we are not at the top of source directory."""
279 for f in ('README', 'Licenses'):
280 if not os.path.exists(f):
281 sys.exit('Please run at the top of source directory.')
282
Masahiro Yamadabd63e5b2016-05-19 15:51:54 +0900283def check_clean_directory():
284 """Exit if the source tree is not clean."""
285 for f in ('.config', 'include/config'):
286 if os.path.exists(f):
287 sys.exit("source tree is not clean, please run 'make mrproper'")
288
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900289def get_make_cmd():
290 """Get the command name of GNU Make.
291
292 U-Boot needs GNU Make for building, but the command name is not
293 necessarily "make". (for example, "gmake" on FreeBSD).
294 Returns the most appropriate command name on your system.
295 """
296 process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
297 ret = process.communicate()
298 if process.returncode:
299 sys.exit('GNU Make not found')
300 return ret[0].rstrip()
301
Simon Glass25f978c2017-06-01 19:38:58 -0600302def get_matched_defconfig(line):
303 """Get the defconfig files that match a pattern
304
305 Args:
306 line: Path or filename to match, e.g. 'configs/snow_defconfig' or
307 'k2*_defconfig'. If no directory is provided, 'configs/' is
308 prepended
309
310 Returns:
311 a list of matching defconfig files
312 """
313 dirname = os.path.dirname(line)
314 if dirname:
315 pattern = line
316 else:
317 pattern = os.path.join('configs', line)
318 return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
319
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900320def get_matched_defconfigs(defconfigs_file):
Simon Glassee4e61b2017-06-01 19:38:59 -0600321 """Get all the defconfig files that match the patterns in a file.
322
323 Args:
324 defconfigs_file: File containing a list of defconfigs to process, or
325 '-' to read the list from stdin
326
327 Returns:
328 A list of paths to defconfig files, with no duplicates
329 """
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900330 defconfigs = []
Simon Glassee4e61b2017-06-01 19:38:59 -0600331 if defconfigs_file == '-':
332 fd = sys.stdin
333 defconfigs_file = 'stdin'
334 else:
335 fd = open(defconfigs_file)
336 for i, line in enumerate(fd):
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900337 line = line.strip()
338 if not line:
339 continue # skip blank lines silently
Simon Glass25f978c2017-06-01 19:38:58 -0600340 matched = get_matched_defconfig(line)
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +0900341 if not matched:
342 print >> sys.stderr, "warning: %s:%d: no defconfig matched '%s'" % \
343 (defconfigs_file, i + 1, line)
344
345 defconfigs += matched
346
347 # use set() to drop multiple matching
348 return [ defconfig[len('configs') + 1:] for defconfig in set(defconfigs) ]
349
Masahiro Yamada684c3062016-07-25 19:15:28 +0900350def get_all_defconfigs():
351 """Get all the defconfig files under the configs/ directory."""
352 defconfigs = []
353 for (dirpath, dirnames, filenames) in os.walk('configs'):
354 dirpath = dirpath[len('configs') + 1:]
355 for filename in fnmatch.filter(filenames, '*_defconfig'):
356 defconfigs.append(os.path.join(dirpath, filename))
357
358 return defconfigs
359
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900360def color_text(color_enabled, color, string):
361 """Return colored string."""
362 if color_enabled:
Masahiro Yamada1d085562016-05-19 15:52:02 +0900363 # LF should not be surrounded by the escape sequence.
364 # Otherwise, additional whitespace or line-feed might be printed.
365 return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
366 for s in string.split('\n') ])
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900367 else:
368 return string
369
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900370def show_diff(a, b, file_path, color_enabled):
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900371 """Show unidified diff.
372
373 Arguments:
374 a: A list of lines (before)
375 b: A list of lines (after)
376 file_path: Path to the file
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900377 color_enabled: Display the diff in color
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900378 """
379
380 diff = difflib.unified_diff(a, b,
381 fromfile=os.path.join('a', file_path),
382 tofile=os.path.join('b', file_path))
383
384 for line in diff:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900385 if line[0] == '-' and line[1] != '-':
386 print color_text(color_enabled, COLOR_RED, line),
387 elif line[0] == '+' and line[1] != '+':
388 print color_text(color_enabled, COLOR_GREEN, line),
389 else:
390 print line,
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900391
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900392def update_cross_compile(color_enabled):
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400393 """Update per-arch CROSS_COMPILE via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900394
395 The default CROSS_COMPILE values are available
396 in the CROSS_COMPILE list above.
397
Robert P. J. Day1cc0a9f2016-05-04 04:47:31 -0400398 You can override them via environment variables
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900399 CROSS_COMPILE_{ARCH}.
400
401 For example, if you want to override toolchain prefixes
402 for ARM and PowerPC, you can do as follows in your shell:
403
404 export CROSS_COMPILE_ARM=...
405 export CROSS_COMPILE_POWERPC=...
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900406
407 Then, this function checks if specified compilers really exist in your
408 PATH environment.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900409 """
410 archs = []
411
412 for arch in os.listdir('arch'):
413 if os.path.exists(os.path.join('arch', arch, 'Makefile')):
414 archs.append(arch)
415
416 # arm64 is a special case
417 archs.append('aarch64')
418
419 for arch in archs:
420 env = 'CROSS_COMPILE_' + arch.upper()
421 cross_compile = os.environ.get(env)
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900422 if not cross_compile:
423 cross_compile = CROSS_COMPILE.get(arch, '')
424
425 for path in os.environ["PATH"].split(os.pathsep):
426 gcc_path = os.path.join(path, cross_compile + 'gcc')
427 if os.path.isfile(gcc_path) and os.access(gcc_path, os.X_OK):
428 break
429 else:
430 print >> sys.stderr, color_text(color_enabled, COLOR_YELLOW,
431 'warning: %sgcc: not found in PATH. %s architecture boards will be skipped'
432 % (cross_compile, arch))
433 cross_compile = None
434
435 CROSS_COMPILE[arch] = cross_compile
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900436
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900437def extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
438 extend_post):
439 """Extend matched lines if desired patterns are found before/after already
440 matched lines.
441
442 Arguments:
443 lines: A list of lines handled.
444 matched: A list of line numbers that have been already matched.
445 (will be updated by this function)
446 pre_patterns: A list of regular expression that should be matched as
447 preamble.
448 post_patterns: A list of regular expression that should be matched as
449 postamble.
450 extend_pre: Add the line number of matched preamble to the matched list.
451 extend_post: Add the line number of matched postamble to the matched list.
452 """
453 extended_matched = []
454
455 j = matched[0]
456
457 for i in matched:
458 if i == 0 or i < j:
459 continue
460 j = i
461 while j in matched:
462 j += 1
463 if j >= len(lines):
464 break
465
466 for p in pre_patterns:
467 if p.search(lines[i - 1]):
468 break
469 else:
470 # not matched
471 continue
472
473 for p in post_patterns:
474 if p.search(lines[j]):
475 break
476 else:
477 # not matched
478 continue
479
480 if extend_pre:
481 extended_matched.append(i - 1)
482 if extend_post:
483 extended_matched.append(j)
484
485 matched += extended_matched
486 matched.sort()
487
Chris Packham85edfc12017-05-02 21:30:46 +1200488def confirm(options, prompt):
489 if not options.yes:
490 while True:
491 choice = raw_input('{} [y/n]: '.format(prompt))
492 choice = choice.lower()
493 print choice
494 if choice == 'y' or choice == 'n':
495 break
496
497 if choice == 'n':
498 return False
499
500 return True
501
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900502def cleanup_one_header(header_path, patterns, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900503 """Clean regex-matched lines away from a file.
504
505 Arguments:
506 header_path: path to the cleaned file.
507 patterns: list of regex patterns. Any lines matching to these
508 patterns are deleted.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900509 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900510 """
511 with open(header_path) as f:
512 lines = f.readlines()
513
514 matched = []
515 for i, line in enumerate(lines):
Masahiro Yamadaa3a779f2016-07-25 19:15:27 +0900516 if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
517 matched.append(i)
518 continue
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900519 for pattern in patterns:
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900520 if pattern.search(line):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900521 matched.append(i)
522 break
523
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900524 if not matched:
525 return
526
527 # remove empty #ifdef ... #endif, successive blank lines
528 pattern_if = re.compile(r'#\s*if(def|ndef)?\W') # #if, #ifdef, #ifndef
529 pattern_elif = re.compile(r'#\s*el(if|se)\W') # #elif, #else
530 pattern_endif = re.compile(r'#\s*endif\W') # #endif
531 pattern_blank = re.compile(r'^\s*$') # empty line
532
533 while True:
534 old_matched = copy.copy(matched)
535 extend_matched_lines(lines, matched, [pattern_if],
536 [pattern_endif], True, True)
537 extend_matched_lines(lines, matched, [pattern_elif],
538 [pattern_elif, pattern_endif], True, False)
539 extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
540 [pattern_blank], False, True)
541 extend_matched_lines(lines, matched, [pattern_blank],
542 [pattern_elif, pattern_endif], True, False)
543 extend_matched_lines(lines, matched, [pattern_blank],
544 [pattern_blank], True, False)
545 if matched == old_matched:
546 break
547
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900548 tolines = copy.copy(lines)
549
550 for i in reversed(matched):
551 tolines.pop(i)
552
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900553 show_diff(lines, tolines, header_path, options.color)
Masahiro Yamada8ba1f5d2016-07-25 19:15:24 +0900554
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900555 if options.dry_run:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900556 return
557
558 with open(header_path, 'w') as f:
Masahiro Yamadaf2f69812016-07-25 19:15:25 +0900559 for line in tolines:
560 f.write(line)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900561
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900562def cleanup_headers(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900563 """Delete config defines from board headers.
564
565 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900566 configs: A list of CONFIGs to remove.
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900567 options: option flags.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900568 """
Chris Packham85edfc12017-05-02 21:30:46 +1200569 if not confirm(options, 'Clean up headers?'):
570 return
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900571
572 patterns = []
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900573 for config in configs:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900574 patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
575 patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
576
Joe Hershberger60727f52015-05-19 13:21:21 -0500577 for dir in 'include', 'arch', 'board':
578 for (dirpath, dirnames, filenames) in os.walk(dir):
Masahiro Yamadadc6de502016-07-25 19:15:22 +0900579 if dirpath == os.path.join('include', 'generated'):
580 continue
Joe Hershberger60727f52015-05-19 13:21:21 -0500581 for filename in filenames:
582 if not fnmatch.fnmatch(filename, '*~'):
583 cleanup_one_header(os.path.join(dirpath, filename),
Masahiro Yamadae9ea1222016-07-25 19:15:26 +0900584 patterns, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900585
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900586def cleanup_one_extra_option(defconfig_path, configs, options):
587 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
588
589 Arguments:
590 defconfig_path: path to the cleaned defconfig file.
591 configs: A list of CONFIGs to remove.
592 options: option flags.
593 """
594
595 start = 'CONFIG_SYS_EXTRA_OPTIONS="'
596 end = '"\n'
597
598 with open(defconfig_path) as f:
599 lines = f.readlines()
600
601 for i, line in enumerate(lines):
602 if line.startswith(start) and line.endswith(end):
603 break
604 else:
605 # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
606 return
607
608 old_tokens = line[len(start):-len(end)].split(',')
609 new_tokens = []
610
611 for token in old_tokens:
612 pos = token.find('=')
613 if not (token[:pos] if pos >= 0 else token) in configs:
614 new_tokens.append(token)
615
616 if new_tokens == old_tokens:
617 return
618
619 tolines = copy.copy(lines)
620
621 if new_tokens:
622 tolines[i] = start + ','.join(new_tokens) + end
623 else:
624 tolines.pop(i)
625
626 show_diff(lines, tolines, defconfig_path, options.color)
627
628 if options.dry_run:
629 return
630
631 with open(defconfig_path, 'w') as f:
632 for line in tolines:
633 f.write(line)
634
635def cleanup_extra_options(configs, options):
636 """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
637
638 Arguments:
639 configs: A list of CONFIGs to remove.
640 options: option flags.
641 """
Chris Packham85edfc12017-05-02 21:30:46 +1200642 if not confirm(options, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
643 return
Masahiro Yamada9ab02962016-07-25 19:15:29 +0900644
645 configs = [ config[len('CONFIG_'):] for config in configs ]
646
647 defconfigs = get_all_defconfigs()
648
649 for defconfig in defconfigs:
650 cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
651 options)
652
Chris Packhamca438342017-05-02 21:30:47 +1200653def cleanup_whitelist(configs, options):
654 """Delete config whitelist entries
655
656 Arguments:
657 configs: A list of CONFIGs to remove.
658 options: option flags.
659 """
660 if not confirm(options, 'Clean up whitelist entries?'):
661 return
662
663 with open(os.path.join('scripts', 'config_whitelist.txt')) as f:
664 lines = f.readlines()
665
666 lines = [x for x in lines if x.strip() not in configs]
667
668 with open(os.path.join('scripts', 'config_whitelist.txt'), 'w') as f:
669 f.write(''.join(lines))
670
Chris Packhamf90df592017-05-02 21:30:48 +1200671def find_matching(patterns, line):
672 for pat in patterns:
673 if pat.search(line):
674 return True
675 return False
676
677def cleanup_readme(configs, options):
678 """Delete config description in README
679
680 Arguments:
681 configs: A list of CONFIGs to remove.
682 options: option flags.
683 """
684 if not confirm(options, 'Clean up README?'):
685 return
686
687 patterns = []
688 for config in configs:
689 patterns.append(re.compile(r'^\s+%s' % config))
690
691 with open('README') as f:
692 lines = f.readlines()
693
694 found = False
695 newlines = []
696 for line in lines:
697 if not found:
698 found = find_matching(patterns, line)
699 if found:
700 continue
701
702 if found and re.search(r'^\s+CONFIG', line):
703 found = False
704
705 if not found:
706 newlines.append(line)
707
708 with open('README', 'w') as f:
709 f.write(''.join(newlines))
710
Chris Packhamca438342017-05-02 21:30:47 +1200711
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900712### classes ###
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900713class Progress:
714
715 """Progress Indicator"""
716
717 def __init__(self, total):
718 """Create a new progress indicator.
719
720 Arguments:
721 total: A number of defconfig files to process.
722 """
723 self.current = 0
724 self.total = total
725
726 def inc(self):
727 """Increment the number of processed defconfig files."""
728
729 self.current += 1
730
731 def show(self):
732 """Display the progress."""
733 print ' %d defconfigs out of %d\r' % (self.current, self.total),
734 sys.stdout.flush()
735
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900736class KconfigParser:
737
738 """A parser of .config and include/autoconf.mk."""
739
740 re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
741 re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
742
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900743 def __init__(self, configs, options, build_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900744 """Create a new parser.
745
746 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900747 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900748 options: option flags.
749 build_dir: Build directory.
750 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900751 self.configs = configs
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900752 self.options = options
Masahiro Yamada1f169922016-05-19 15:52:00 +0900753 self.dotconfig = os.path.join(build_dir, '.config')
754 self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
Masahiro Yamada07913d12016-08-22 22:18:22 +0900755 self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
756 'autoconf.mk')
Simon Glassf3b8e642017-06-01 19:39:01 -0600757 self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900758 self.defconfig = os.path.join(build_dir, 'defconfig')
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900759
760 def get_cross_compile(self):
761 """Parse .config file and return CROSS_COMPILE.
762
763 Returns:
764 A string storing the compiler prefix for the architecture.
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900765 Return a NULL string for architectures that do not require
766 compiler prefix (Sandbox and native build is the case).
767 Return None if the specified compiler is missing in your PATH.
768 Caller should distinguish '' and None.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900769 """
770 arch = ''
771 cpu = ''
Masahiro Yamada1f169922016-05-19 15:52:00 +0900772 for line in open(self.dotconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900773 m = self.re_arch.match(line)
774 if m:
775 arch = m.group(1)
776 continue
777 m = self.re_cpu.match(line)
778 if m:
779 cpu = m.group(1)
780
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900781 if not arch:
782 return None
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900783
784 # fix-up for aarch64
785 if arch == 'arm' and cpu == 'armv8':
786 arch = 'aarch64'
787
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +0900788 return CROSS_COMPILE.get(arch, None)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900789
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900790 def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900791 """Parse .config, defconfig, include/autoconf.mk for one config.
792
793 This function looks for the config options in the lines from
794 defconfig, .config, and include/autoconf.mk in order to decide
795 which action should be taken for this defconfig.
796
797 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900798 config: CONFIG name to parse.
Masahiro Yamadacc008292016-05-19 15:51:56 +0900799 dotconfig_lines: lines from the .config file.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900800 autoconf_lines: lines from the include/autoconf.mk file.
801
802 Returns:
803 A tupple of the action for this defconfig and the line
804 matched for the config.
805 """
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900806 not_set = '# %s is not set' % config
807
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900808 for line in autoconf_lines:
809 line = line.rstrip()
810 if line.startswith(config + '='):
Masahiro Yamadacc008292016-05-19 15:51:56 +0900811 new_val = line
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900812 break
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900813 else:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900814 new_val = not_set
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900815
Masahiro Yamada916224c2016-08-22 22:18:21 +0900816 for line in dotconfig_lines:
817 line = line.rstrip()
818 if line.startswith(config + '=') or line == not_set:
819 old_val = line
820 break
821 else:
822 if new_val == not_set:
823 return (ACTION_NO_ENTRY, config)
824 else:
825 return (ACTION_NO_ENTRY_WARN, config)
826
Masahiro Yamadacc008292016-05-19 15:51:56 +0900827 # If this CONFIG is neither bool nor trisate
828 if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
829 # tools/scripts/define2mk.sed changes '1' to 'y'.
830 # This is a problem if the CONFIG is int type.
831 # Check the type in Kconfig and handle it correctly.
832 if new_val[-2:] == '=y':
833 new_val = new_val[:-1] + '1'
834
Masahiro Yamada50301592016-06-15 14:33:50 +0900835 return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
836 new_val)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900837
Masahiro Yamada1d085562016-05-19 15:52:02 +0900838 def update_dotconfig(self):
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900839 """Parse files for the config options and update the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900840
Masahiro Yamadacc008292016-05-19 15:51:56 +0900841 This function parses the generated .config and include/autoconf.mk
842 searching the target options.
Masahiro Yamada6ff36d22016-05-19 15:51:50 +0900843 Move the config option(s) to the .config as needed.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900844
845 Arguments:
846 defconfig: defconfig name.
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900847
848 Returns:
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900849 Return a tuple of (updated flag, log string).
850 The "updated flag" is True if the .config was updated, False
851 otherwise. The "log string" shows what happend to the .config.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900852 """
853
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900854 results = []
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900855 updated = False
Masahiro Yamada916224c2016-08-22 22:18:21 +0900856 suspicious = False
Masahiro Yamada07913d12016-08-22 22:18:22 +0900857 rm_files = [self.config_autoconf, self.autoconf]
858
859 if self.options.spl:
860 if os.path.exists(self.spl_autoconf):
861 autoconf_path = self.spl_autoconf
862 rm_files.append(self.spl_autoconf)
863 else:
864 for f in rm_files:
865 os.remove(f)
866 return (updated, suspicious,
867 color_text(self.options.color, COLOR_BROWN,
868 "SPL is not enabled. Skipped.") + '\n')
869 else:
870 autoconf_path = self.autoconf
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900871
Masahiro Yamada1f169922016-05-19 15:52:00 +0900872 with open(self.dotconfig) as f:
Masahiro Yamadacc008292016-05-19 15:51:56 +0900873 dotconfig_lines = f.readlines()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900874
Masahiro Yamada07913d12016-08-22 22:18:22 +0900875 with open(autoconf_path) as f:
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900876 autoconf_lines = f.readlines()
877
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900878 for config in self.configs:
879 result = self.parse_one_config(config, dotconfig_lines,
Joe Hershberger96464ba2015-05-19 13:21:17 -0500880 autoconf_lines)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900881 results.append(result)
882
883 log = ''
884
885 for (action, value) in results:
886 if action == ACTION_MOVE:
887 actlog = "Move '%s'" % value
888 log_color = COLOR_LIGHT_GREEN
Masahiro Yamadacc008292016-05-19 15:51:56 +0900889 elif action == ACTION_NO_ENTRY:
890 actlog = "%s is not defined in Kconfig. Do nothing." % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900891 log_color = COLOR_LIGHT_BLUE
Masahiro Yamada916224c2016-08-22 22:18:21 +0900892 elif action == ACTION_NO_ENTRY_WARN:
893 actlog = "%s is not defined in Kconfig (suspicious). Do nothing." % value
894 log_color = COLOR_YELLOW
895 suspicious = True
Masahiro Yamadacc008292016-05-19 15:51:56 +0900896 elif action == ACTION_NO_CHANGE:
897 actlog = "'%s' is the same as the define in Kconfig. Do nothing." \
898 % value
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900899 log_color = COLOR_LIGHT_PURPLE
Masahiro Yamada07913d12016-08-22 22:18:22 +0900900 elif action == ACTION_SPL_NOT_EXIST:
901 actlog = "SPL is not enabled for this defconfig. Skip."
902 log_color = COLOR_PURPLE
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900903 else:
904 sys.exit("Internal Error. This should not happen.")
905
Masahiro Yamada1d085562016-05-19 15:52:02 +0900906 log += color_text(self.options.color, log_color, actlog) + '\n'
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900907
Masahiro Yamada1f169922016-05-19 15:52:00 +0900908 with open(self.dotconfig, 'a') as f:
Masahiro Yamadae423d172016-05-19 15:51:49 +0900909 for (action, value) in results:
910 if action == ACTION_MOVE:
911 f.write(value + '\n')
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +0900912 updated = True
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900913
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900914 self.results = results
Masahiro Yamada07913d12016-08-22 22:18:22 +0900915 for f in rm_files:
916 os.remove(f)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900917
Masahiro Yamada916224c2016-08-22 22:18:21 +0900918 return (updated, suspicious, log)
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900919
Masahiro Yamada5da4f852016-05-19 15:52:06 +0900920 def check_defconfig(self):
921 """Check the defconfig after savedefconfig
922
923 Returns:
924 Return additional log if moved CONFIGs were removed again by
925 'make savedefconfig'.
926 """
927
928 log = ''
929
930 with open(self.defconfig) as f:
931 defconfig_lines = f.readlines()
932
933 for (action, value) in self.results:
934 if action != ACTION_MOVE:
935 continue
936 if not value + '\n' in defconfig_lines:
937 log += color_text(self.options.color, COLOR_YELLOW,
938 "'%s' was removed by savedefconfig.\n" %
939 value)
940
941 return log
942
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900943class Slot:
944
945 """A slot to store a subprocess.
946
947 Each instance of this class handles one subprocess.
948 This class is useful to control multiple threads
949 for faster processing.
950 """
951
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500952 def __init__(self, configs, options, progress, devnull, make_cmd, reference_src_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900953 """Create a new process slot.
954
955 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +0900956 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900957 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900958 progress: A progress indicator.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900959 devnull: A file object of '/dev/null'.
960 make_cmd: command name of GNU Make.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500961 reference_src_dir: Determine the true starting config state from this
962 source tree.
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900963 """
964 self.options = options
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900965 self.progress = progress
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900966 self.build_dir = tempfile.mkdtemp()
967 self.devnull = devnull
968 self.make_cmd = (make_cmd, 'O=' + self.build_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -0500969 self.reference_src_dir = reference_src_dir
Masahiro Yamada522e8dc2016-05-19 15:52:01 +0900970 self.parser = KconfigParser(configs, options, self.build_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900971 self.state = STATE_IDLE
Masahiro Yamada09c6c062016-08-22 22:18:20 +0900972 self.failed_boards = set()
973 self.suspicious_boards = set()
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900974
975 def __del__(self):
976 """Delete the working directory
977
978 This function makes sure the temporary directory is cleaned away
979 even if Python suddenly dies due to error. It should be done in here
Joe Hershbergerf2dae752016-06-10 14:53:29 -0500980 because it is guaranteed the destructor is always invoked when the
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900981 instance of the class gets unreferenced.
982
983 If the subprocess is still running, wait until it finishes.
984 """
985 if self.state != STATE_IDLE:
986 while self.ps.poll() == None:
987 pass
988 shutil.rmtree(self.build_dir)
989
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +0900990 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +0900991 """Assign a new subprocess for defconfig and add it to the slot.
992
993 If the slot is vacant, create a new subprocess for processing the
994 given defconfig and add it to the slot. Just returns False if
995 the slot is occupied (i.e. the current subprocess is still running).
996
997 Arguments:
998 defconfig: defconfig name.
999
1000 Returns:
1001 Return True on success or False on failure
1002 """
1003 if self.state != STATE_IDLE:
1004 return False
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001005
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001006 self.defconfig = defconfig
Masahiro Yamada1d085562016-05-19 15:52:02 +09001007 self.log = ''
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001008 self.current_src_dir = self.reference_src_dir
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001009 self.do_defconfig()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001010 return True
1011
1012 def poll(self):
1013 """Check the status of the subprocess and handle it as needed.
1014
1015 Returns True if the slot is vacant (i.e. in idle state).
1016 If the configuration is successfully finished, assign a new
1017 subprocess to build include/autoconf.mk.
1018 If include/autoconf.mk is generated, invoke the parser to
Masahiro Yamada7fb0bac2016-05-19 15:52:04 +09001019 parse the .config and the include/autoconf.mk, moving
1020 config options to the .config as needed.
1021 If the .config was updated, run "make savedefconfig" to sync
1022 it, update the original defconfig, and then set the slot back
1023 to the idle state.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001024
1025 Returns:
1026 Return True if the subprocess is terminated, False otherwise
1027 """
1028 if self.state == STATE_IDLE:
1029 return True
1030
1031 if self.ps.poll() == None:
1032 return False
1033
1034 if self.ps.poll() != 0:
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001035 self.handle_error()
1036 elif self.state == STATE_DEFCONFIG:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001037 if self.reference_src_dir and not self.current_src_dir:
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001038 self.do_savedefconfig()
1039 else:
1040 self.do_autoconf()
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001041 elif self.state == STATE_AUTOCONF:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001042 if self.current_src_dir:
1043 self.current_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001044 self.do_defconfig()
1045 else:
1046 self.do_savedefconfig()
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001047 elif self.state == STATE_SAVEDEFCONFIG:
1048 self.update_defconfig()
1049 else:
1050 sys.exit("Internal Error. This should not happen.")
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001051
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001052 return True if self.state == STATE_IDLE else False
Joe Hershberger96464ba2015-05-19 13:21:17 -05001053
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001054 def handle_error(self):
1055 """Handle error cases."""
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001056
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001057 self.log += color_text(self.options.color, COLOR_LIGHT_RED,
1058 "Failed to process.\n")
1059 if self.options.verbose:
1060 self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
1061 self.ps.stderr.read())
1062 self.finish(False)
Joe Hershberger96464ba2015-05-19 13:21:17 -05001063
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001064 def do_defconfig(self):
1065 """Run 'make <board>_defconfig' to create the .config file."""
Masahiro Yamadac8e1b102016-05-19 15:52:07 +09001066
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001067 cmd = list(self.make_cmd)
1068 cmd.append(self.defconfig)
1069 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001070 stderr=subprocess.PIPE,
1071 cwd=self.current_src_dir)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001072 self.state = STATE_DEFCONFIG
Masahiro Yamadac8e1b102016-05-19 15:52:07 +09001073
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001074 def do_autoconf(self):
Simon Glassf3b8e642017-06-01 19:39:01 -06001075 """Run 'make AUTO_CONF_PATH'."""
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001076
Joe Hershberger25400092015-05-19 13:21:23 -05001077 self.cross_compile = self.parser.get_cross_compile()
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +09001078 if self.cross_compile is None:
Masahiro Yamada1d085562016-05-19 15:52:02 +09001079 self.log += color_text(self.options.color, COLOR_YELLOW,
1080 "Compiler is missing. Do nothing.\n")
Masahiro Yamada4efef992016-05-19 15:52:03 +09001081 self.finish(False)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001082 return
Masahiro Yamada90ed6cb2016-05-19 15:51:53 +09001083
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001084 cmd = list(self.make_cmd)
Joe Hershberger25400092015-05-19 13:21:23 -05001085 if self.cross_compile:
1086 cmd.append('CROSS_COMPILE=%s' % self.cross_compile)
Joe Hershberger7740f652015-05-19 13:21:18 -05001087 cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
Simon Glassf3b8e642017-06-01 19:39:01 -06001088 cmd.append(AUTO_CONF_PATH)
Joe Hershberger25400092015-05-19 13:21:23 -05001089 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001090 stderr=subprocess.PIPE,
1091 cwd=self.current_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001092 self.state = STATE_AUTOCONF
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001093
1094 def do_savedefconfig(self):
1095 """Update the .config and run 'make savedefconfig'."""
1096
Masahiro Yamada916224c2016-08-22 22:18:21 +09001097 (updated, suspicious, log) = self.parser.update_dotconfig()
1098 if suspicious:
1099 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001100 self.log += log
1101
1102 if not self.options.force_sync and not updated:
1103 self.finish(True)
1104 return
1105 if updated:
1106 self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
1107 "Syncing by savedefconfig...\n")
1108 else:
1109 self.log += "Syncing by savedefconfig (forced by option)...\n"
1110
1111 cmd = list(self.make_cmd)
1112 cmd.append('savedefconfig')
1113 self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1114 stderr=subprocess.PIPE)
1115 self.state = STATE_SAVEDEFCONFIG
1116
1117 def update_defconfig(self):
1118 """Update the input defconfig and go back to the idle state."""
1119
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001120 log = self.parser.check_defconfig()
1121 if log:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001122 self.suspicious_boards.add(self.defconfig)
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001123 self.log += log
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001124 orig_defconfig = os.path.join('configs', self.defconfig)
1125 new_defconfig = os.path.join(self.build_dir, 'defconfig')
1126 updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1127
1128 if updated:
Joe Hershberger06cc1d32016-06-10 14:53:30 -05001129 self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
Masahiro Yamadae307fa92016-06-08 11:47:37 +09001130 "defconfig was updated.\n")
1131
1132 if not self.options.dry_run and updated:
1133 shutil.move(new_defconfig, orig_defconfig)
1134 self.finish(True)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001135
Masahiro Yamada4efef992016-05-19 15:52:03 +09001136 def finish(self, success):
1137 """Display log along with progress and go to the idle state.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001138
1139 Arguments:
Masahiro Yamada4efef992016-05-19 15:52:03 +09001140 success: Should be True when the defconfig was processed
1141 successfully, or False when it fails.
Masahiro Yamada1d085562016-05-19 15:52:02 +09001142 """
1143 # output at least 30 characters to hide the "* defconfigs out of *".
1144 log = self.defconfig.ljust(30) + '\n'
1145
1146 log += '\n'.join([ ' ' + s for s in self.log.split('\n') ])
1147 # Some threads are running in parallel.
1148 # Print log atomically to not mix up logs from different threads.
Masahiro Yamada4efef992016-05-19 15:52:03 +09001149 print >> (sys.stdout if success else sys.stderr), log
1150
1151 if not success:
1152 if self.options.exit_on_error:
1153 sys.exit("Exit on error.")
1154 # If --exit-on-error flag is not set, skip this board and continue.
1155 # Record the failed board.
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001156 self.failed_boards.add(self.defconfig)
Masahiro Yamada4efef992016-05-19 15:52:03 +09001157
Masahiro Yamada1d085562016-05-19 15:52:02 +09001158 self.progress.inc()
1159 self.progress.show()
Masahiro Yamada4efef992016-05-19 15:52:03 +09001160 self.state = STATE_IDLE
Masahiro Yamada1d085562016-05-19 15:52:02 +09001161
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001162 def get_failed_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001163 """Returns a set of failed boards (defconfigs) in this slot.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001164 """
1165 return self.failed_boards
1166
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001167 def get_suspicious_boards(self):
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001168 """Returns a set of boards (defconfigs) with possible misconversion.
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001169 """
Masahiro Yamada916224c2016-08-22 22:18:21 +09001170 return self.suspicious_boards - self.failed_boards
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001171
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001172class Slots:
1173
1174 """Controller of the array of subprocess slots."""
1175
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001176 def __init__(self, configs, options, progress, reference_src_dir):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001177 """Create a new slots controller.
1178
1179 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001180 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001181 options: option flags.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001182 progress: A progress indicator.
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001183 reference_src_dir: Determine the true starting config state from this
1184 source tree.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001185 """
1186 self.options = options
1187 self.slots = []
1188 devnull = get_devnull()
1189 make_cmd = get_make_cmd()
1190 for i in range(options.jobs):
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001191 self.slots.append(Slot(configs, options, progress, devnull,
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001192 make_cmd, reference_src_dir))
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001193
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001194 def add(self, defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001195 """Add a new subprocess if a vacant slot is found.
1196
1197 Arguments:
1198 defconfig: defconfig name to be put into.
1199
1200 Returns:
1201 Return True on success or False on failure
1202 """
1203 for slot in self.slots:
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001204 if slot.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001205 return True
1206 return False
1207
1208 def available(self):
1209 """Check if there is a vacant slot.
1210
1211 Returns:
1212 Return True if at lease one vacant slot is found, False otherwise.
1213 """
1214 for slot in self.slots:
1215 if slot.poll():
1216 return True
1217 return False
1218
1219 def empty(self):
1220 """Check if all slots are vacant.
1221
1222 Returns:
1223 Return True if all the slots are vacant, False otherwise.
1224 """
1225 ret = True
1226 for slot in self.slots:
1227 if not slot.poll():
1228 ret = False
1229 return ret
1230
1231 def show_failed_boards(self):
1232 """Display all of the failed boards (defconfigs)."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001233 boards = set()
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001234 output_file = 'moveconfig.failed'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001235
1236 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001237 boards |= slot.get_failed_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001238
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001239 if boards:
1240 boards = '\n'.join(boards) + '\n'
1241 msg = "The following boards were not processed due to error:\n"
1242 msg += boards
1243 msg += "(the list has been saved in %s)\n" % output_file
1244 print >> sys.stderr, color_text(self.options.color, COLOR_LIGHT_RED,
1245 msg)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001246
Masahiro Yamada96dccd92016-06-15 14:33:53 +09001247 with open(output_file, 'w') as f:
1248 f.write(boards)
Joe Hershberger2559cd82015-05-19 13:21:22 -05001249
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001250 def show_suspicious_boards(self):
1251 """Display all boards (defconfigs) with possible misconversion."""
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001252 boards = set()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001253 output_file = 'moveconfig.suspicious'
1254
1255 for slot in self.slots:
Masahiro Yamada09c6c062016-08-22 22:18:20 +09001256 boards |= slot.get_suspicious_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001257
1258 if boards:
1259 boards = '\n'.join(boards) + '\n'
1260 msg = "The following boards might have been converted incorrectly.\n"
1261 msg += "It is highly recommended to check them manually:\n"
1262 msg += boards
1263 msg += "(the list has been saved in %s)\n" % output_file
1264 print >> sys.stderr, color_text(self.options.color, COLOR_YELLOW,
1265 msg)
1266
1267 with open(output_file, 'w') as f:
1268 f.write(boards)
1269
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001270class ReferenceSource:
1271
1272 """Reference source against which original configs should be parsed."""
1273
1274 def __init__(self, commit):
1275 """Create a reference source directory based on a specified commit.
1276
1277 Arguments:
1278 commit: commit to git-clone
1279 """
1280 self.src_dir = tempfile.mkdtemp()
1281 print "Cloning git repo to a separate work directory..."
1282 subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1283 cwd=self.src_dir)
1284 print "Checkout '%s' to build the original autoconf.mk." % \
1285 subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip()
1286 subprocess.check_output(['git', 'checkout', commit],
1287 stderr=subprocess.STDOUT, cwd=self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001288
1289 def __del__(self):
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001290 """Delete the reference source directory
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001291
1292 This function makes sure the temporary directory is cleaned away
1293 even if Python suddenly dies due to error. It should be done in here
1294 because it is guaranteed the destructor is always invoked when the
1295 instance of the class gets unreferenced.
1296 """
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001297 shutil.rmtree(self.src_dir)
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001298
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001299 def get_dir(self):
1300 """Return the absolute path to the reference source directory."""
1301
1302 return self.src_dir
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001303
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001304def move_config(configs, options):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001305 """Move config options to defconfig files.
1306
1307 Arguments:
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001308 configs: A list of CONFIGs to move.
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001309 options: option flags
1310 """
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001311 if len(configs) == 0:
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001312 if options.force_sync:
1313 print 'No CONFIG is specified. You are probably syncing defconfigs.',
1314 else:
1315 print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
1316 else:
1317 print 'Move ' + ', '.join(configs),
1318 print '(jobs: %d)\n' % options.jobs
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001319
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001320 if options.git_ref:
Masahiro Yamada5cc42a52016-06-15 14:33:51 +09001321 reference_src = ReferenceSource(options.git_ref)
1322 reference_src_dir = reference_src.get_dir()
1323 else:
Masahiro Yamadaf432c332016-06-15 14:33:52 +09001324 reference_src_dir = None
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001325
Joe Hershberger91040e82015-05-19 13:21:19 -05001326 if options.defconfigs:
Masahiro Yamada0dbc9b52016-10-19 14:39:54 +09001327 defconfigs = get_matched_defconfigs(options.defconfigs)
Joe Hershberger91040e82015-05-19 13:21:19 -05001328 else:
Masahiro Yamada684c3062016-07-25 19:15:28 +09001329 defconfigs = get_all_defconfigs()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001330
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001331 progress = Progress(len(defconfigs))
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001332 slots = Slots(configs, options, progress, reference_src_dir)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001333
1334 # Main loop to process defconfig files:
1335 # Add a new subprocess into a vacant slot.
1336 # Sleep if there is no available slot.
Masahiro Yamadac5e60fd2016-05-19 15:51:55 +09001337 for defconfig in defconfigs:
1338 while not slots.add(defconfig):
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001339 while not slots.available():
1340 # No available slot: sleep for a while
1341 time.sleep(SLEEP_TIME)
1342
1343 # wait until all the subprocesses finish
1344 while not slots.empty():
1345 time.sleep(SLEEP_TIME)
1346
Joe Hershberger2e2ce6c2015-05-19 13:21:25 -05001347 print ''
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001348 slots.show_failed_boards()
Masahiro Yamadafc2661e2016-06-15 14:33:54 +09001349 slots.show_suspicious_boards()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001350
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001351def main():
1352 try:
1353 cpu_count = multiprocessing.cpu_count()
1354 except NotImplementedError:
1355 cpu_count = 1
1356
1357 parser = optparse.OptionParser()
1358 # Add options here
1359 parser.add_option('-c', '--color', action='store_true', default=False,
1360 help='display the log in color')
Simon Glass9ede2122016-09-12 23:18:21 -06001361 parser.add_option('-C', '--commit', action='store_true', default=False,
1362 help='Create a git commit for the operation')
Joe Hershberger91040e82015-05-19 13:21:19 -05001363 parser.add_option('-d', '--defconfigs', type='string',
Simon Glassee4e61b2017-06-01 19:38:59 -06001364 help='a file containing a list of defconfigs to move, '
1365 "one per line (for example 'snow_defconfig') "
1366 "or '-' to read from stdin")
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001367 parser.add_option('-n', '--dry-run', action='store_true', default=False,
1368 help='perform a trial run (show log with no changes)')
1369 parser.add_option('-e', '--exit-on-error', action='store_true',
1370 default=False,
1371 help='exit immediately on any error')
Masahiro Yamada8513dc02016-05-19 15:52:08 +09001372 parser.add_option('-s', '--force-sync', action='store_true', default=False,
1373 help='force sync by savedefconfig')
Masahiro Yamada07913d12016-08-22 22:18:22 +09001374 parser.add_option('-S', '--spl', action='store_true', default=False,
1375 help='parse config options defined for SPL build')
Joe Hershberger2144f882015-05-19 13:21:20 -05001376 parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1377 action='store_true', default=False,
1378 help='only cleanup the headers')
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001379 parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1380 help='the number of jobs to run simultaneously')
Joe Hershberger6b96c1a2016-06-10 14:53:32 -05001381 parser.add_option('-r', '--git-ref', type='string',
1382 help='the git ref to clone for building the autoconf.mk')
Simon Glass6b403df2016-09-12 23:18:20 -06001383 parser.add_option('-y', '--yes', action='store_true', default=False,
1384 help="respond 'yes' to any prompts")
Joe Hershberger95bf9c72015-05-19 13:21:24 -05001385 parser.add_option('-v', '--verbose', action='store_true', default=False,
1386 help='show any build errors as boards are built')
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001387 parser.usage += ' CONFIG ...'
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001388
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001389 (options, configs) = parser.parse_args()
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001390
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001391 if len(configs) == 0 and not options.force_sync:
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001392 parser.print_usage()
1393 sys.exit(1)
1394
Masahiro Yamadab6ef3932016-05-19 15:51:58 +09001395 # prefix the option name with CONFIG_ if missing
1396 configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1397 for config in configs ]
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001398
Joe Hershberger2144f882015-05-19 13:21:20 -05001399 check_top_directory()
1400
1401 if not options.cleanup_headers_only:
Masahiro Yamadaf7536f72016-07-25 19:15:23 +09001402 check_clean_directory()
1403 update_cross_compile(options.color)
Masahiro Yamadab134bc12016-05-19 15:51:57 +09001404 move_config(configs, options)
Joe Hershberger2144f882015-05-19 13:21:20 -05001405
Masahiro Yamada6a9f79f2016-05-19 15:52:09 +09001406 if configs:
Masahiro Yamadae9ea1222016-07-25 19:15:26 +09001407 cleanup_headers(configs, options)
Masahiro Yamada9ab02962016-07-25 19:15:29 +09001408 cleanup_extra_options(configs, options)
Chris Packhamca438342017-05-02 21:30:47 +12001409 cleanup_whitelist(configs, options)
Chris Packhamf90df592017-05-02 21:30:48 +12001410 cleanup_readme(configs, options)
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001411
Simon Glass9ede2122016-09-12 23:18:21 -06001412 if options.commit:
1413 subprocess.call(['git', 'add', '-u'])
1414 if configs:
1415 msg = 'Convert %s %sto Kconfig' % (configs[0],
1416 'et al ' if len(configs) > 1 else '')
1417 msg += ('\n\nThis converts the following to Kconfig:\n %s\n' %
1418 '\n '.join(configs))
1419 else:
1420 msg = 'configs: Resync with savedefconfig'
1421 msg += '\n\nRsync all defconfig files using moveconfig.py'
1422 subprocess.call(['git', 'commit', '-s', '-m', msg])
1423
Masahiro Yamada5a27c732015-05-20 11:36:07 +09001424if __name__ == '__main__':
1425 main()