xref: /OK3568_Linux_fs/u-boot/tools/moveconfig.py (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun#!/usr/bin/env python2
2*4882a593Smuzhiyun#
3*4882a593Smuzhiyun# Author: Masahiro Yamada <yamada.masahiro@socionext.com>
4*4882a593Smuzhiyun#
5*4882a593Smuzhiyun# SPDX-License-Identifier:	GPL-2.0+
6*4882a593Smuzhiyun#
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun"""
9*4882a593SmuzhiyunMove config options from headers to defconfig files.
10*4882a593Smuzhiyun
11*4882a593SmuzhiyunSince Kconfig was introduced to U-Boot, we have worked on moving
12*4882a593Smuzhiyunconfig options from headers to Kconfig (defconfig).
13*4882a593Smuzhiyun
14*4882a593SmuzhiyunThis tool intends to help this tremendous work.
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun
17*4882a593SmuzhiyunUsage
18*4882a593Smuzhiyun-----
19*4882a593Smuzhiyun
20*4882a593SmuzhiyunFirst, you must edit the Kconfig to add the menu entries for the configs
21*4882a593Smuzhiyunyou are moving.
22*4882a593Smuzhiyun
23*4882a593SmuzhiyunAnd then run this tool giving CONFIG names you want to move.
24*4882a593SmuzhiyunFor example, if you want to move CONFIG_CMD_USB and CONFIG_SYS_TEXT_BASE,
25*4882a593Smuzhiyunsimply type as follows:
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun  $ tools/moveconfig.py CONFIG_CMD_USB CONFIG_SYS_TEXT_BASE
28*4882a593Smuzhiyun
29*4882a593SmuzhiyunThe tool walks through all the defconfig files and move the given CONFIGs.
30*4882a593Smuzhiyun
31*4882a593SmuzhiyunThe log is also displayed on the terminal.
32*4882a593Smuzhiyun
33*4882a593SmuzhiyunThe log is printed for each defconfig as follows:
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun<defconfig_name>
36*4882a593Smuzhiyun    <action1>
37*4882a593Smuzhiyun    <action2>
38*4882a593Smuzhiyun    <action3>
39*4882a593Smuzhiyun    ...
40*4882a593Smuzhiyun
41*4882a593Smuzhiyun<defconfig_name> is the name of the defconfig.
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun<action*> shows what the tool did for that defconfig.
44*4882a593SmuzhiyunIt looks like one of the following:
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun - Move 'CONFIG_... '
47*4882a593Smuzhiyun   This config option was moved to the defconfig
48*4882a593Smuzhiyun
49*4882a593Smuzhiyun - CONFIG_... is not defined in Kconfig.  Do nothing.
50*4882a593Smuzhiyun   The entry for this CONFIG was not found in Kconfig.  The option is not
51*4882a593Smuzhiyun   defined in the config header, either.  So, this case can be just skipped.
52*4882a593Smuzhiyun
53*4882a593Smuzhiyun - CONFIG_... is not defined in Kconfig (suspicious).  Do nothing.
54*4882a593Smuzhiyun   This option is defined in the config header, but its entry was not found
55*4882a593Smuzhiyun   in Kconfig.
56*4882a593Smuzhiyun   There are two common cases:
57*4882a593Smuzhiyun     - You forgot to create an entry for the CONFIG before running
58*4882a593Smuzhiyun       this tool, or made a typo in a CONFIG passed to this tool.
59*4882a593Smuzhiyun     - The entry was hidden due to unmet 'depends on'.
60*4882a593Smuzhiyun   The tool does not know if the result is reasonable, so please check it
61*4882a593Smuzhiyun   manually.
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun - 'CONFIG_...' is the same as the define in Kconfig.  Do nothing.
64*4882a593Smuzhiyun   The define in the config header matched the one in Kconfig.
65*4882a593Smuzhiyun   We do not need to touch it.
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun - Compiler is missing.  Do nothing.
68*4882a593Smuzhiyun   The compiler specified for this architecture was not found
69*4882a593Smuzhiyun   in your PATH environment.
70*4882a593Smuzhiyun   (If -e option is passed, the tool exits immediately.)
71*4882a593Smuzhiyun
72*4882a593Smuzhiyun - Failed to process.
73*4882a593Smuzhiyun   An error occurred during processing this defconfig.  Skipped.
74*4882a593Smuzhiyun   (If -e option is passed, the tool exits immediately on error.)
75*4882a593Smuzhiyun
76*4882a593SmuzhiyunFinally, you will be asked, Clean up headers? [y/n]:
77*4882a593Smuzhiyun
78*4882a593SmuzhiyunIf you say 'y' here, the unnecessary config defines are removed
79*4882a593Smuzhiyunfrom the config headers (include/configs/*.h).
80*4882a593SmuzhiyunIt just uses the regex method, so you should not rely on it.
81*4882a593SmuzhiyunJust in case, please do 'git diff' to see what happened.
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun
84*4882a593SmuzhiyunHow does it work?
85*4882a593Smuzhiyun-----------------
86*4882a593Smuzhiyun
87*4882a593SmuzhiyunThis tool runs configuration and builds include/autoconf.mk for every
88*4882a593Smuzhiyundefconfig.  The config options defined in Kconfig appear in the .config
89*4882a593Smuzhiyunfile (unless they are hidden because of unmet dependency.)
90*4882a593SmuzhiyunOn the other hand, the config options defined by board headers are seen
91*4882a593Smuzhiyunin include/autoconf.mk.  The tool looks for the specified options in both
92*4882a593Smuzhiyunof them to decide the appropriate action for the options.  If the given
93*4882a593Smuzhiyunconfig option is found in the .config, but its value does not match the
94*4882a593Smuzhiyunone from the board header, the config option in the .config is replaced
95*4882a593Smuzhiyunwith the define in the board header.  Then, the .config is synced by
96*4882a593Smuzhiyun"make savedefconfig" and the defconfig is updated with it.
97*4882a593Smuzhiyun
98*4882a593SmuzhiyunFor faster processing, this tool handles multi-threading.  It creates
99*4882a593Smuzhiyunseparate build directories where the out-of-tree build is run.  The
100*4882a593Smuzhiyuntemporary build directories are automatically created and deleted as
101*4882a593Smuzhiyunneeded.  The number of threads are chosen based on the number of the CPU
102*4882a593Smuzhiyuncores of your system although you can change it via -j (--jobs) option.
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun
105*4882a593SmuzhiyunToolchains
106*4882a593Smuzhiyun----------
107*4882a593Smuzhiyun
108*4882a593SmuzhiyunAppropriate toolchain are necessary to generate include/autoconf.mk
109*4882a593Smuzhiyunfor all the architectures supported by U-Boot.  Most of them are available
110*4882a593Smuzhiyunat the kernel.org site, some are not provided by kernel.org. This tool uses
111*4882a593Smuzhiyunthe same tools as buildman, so see that tool for setup (e.g. --fetch-arch).
112*4882a593Smuzhiyun
113*4882a593Smuzhiyun
114*4882a593SmuzhiyunTips and trips
115*4882a593Smuzhiyun--------------
116*4882a593Smuzhiyun
117*4882a593SmuzhiyunTo sync only X86 defconfigs:
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun   ./tools/moveconfig.py -s -d <(grep -l X86 configs/*)
120*4882a593Smuzhiyun
121*4882a593Smuzhiyunor:
122*4882a593Smuzhiyun
123*4882a593Smuzhiyun   grep -l X86 configs/* | ./tools/moveconfig.py -s -d -
124*4882a593Smuzhiyun
125*4882a593SmuzhiyunTo process CONFIG_CMD_FPGAD only for a subset of configs based on path match:
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun   ls configs/{hrcon*,iocon*,strider*} | \
128*4882a593Smuzhiyun       ./tools/moveconfig.py -Cy CONFIG_CMD_FPGAD -d -
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun
131*4882a593SmuzhiyunFinding implied CONFIGs
132*4882a593Smuzhiyun-----------------------
133*4882a593Smuzhiyun
134*4882a593SmuzhiyunSome CONFIG options can be implied by others and this can help to reduce
135*4882a593Smuzhiyunthe size of the defconfig files. For example, CONFIG_X86 implies
136*4882a593SmuzhiyunCONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
137*4882a593Smuzhiyunall x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
138*4882a593Smuzhiyuneach of the x86 defconfig files.
139*4882a593Smuzhiyun
140*4882a593SmuzhiyunThis tool can help find such configs. To use it, first build a database:
141*4882a593Smuzhiyun
142*4882a593Smuzhiyun    ./tools/moveconfig.py -b
143*4882a593Smuzhiyun
144*4882a593SmuzhiyunThen try to query it:
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun    ./tools/moveconfig.py -i CONFIG_CMD_IRQ
147*4882a593Smuzhiyun    CONFIG_CMD_IRQ found in 311/2384 defconfigs
148*4882a593Smuzhiyun    44 : CONFIG_SYS_FSL_ERRATUM_IFC_A002769
149*4882a593Smuzhiyun    41 : CONFIG_SYS_FSL_ERRATUM_A007075
150*4882a593Smuzhiyun    31 : CONFIG_SYS_FSL_DDR_VER_44
151*4882a593Smuzhiyun    28 : CONFIG_ARCH_P1010
152*4882a593Smuzhiyun    28 : CONFIG_SYS_FSL_ERRATUM_P1010_A003549
153*4882a593Smuzhiyun    28 : CONFIG_SYS_FSL_ERRATUM_SEC_A003571
154*4882a593Smuzhiyun    28 : CONFIG_SYS_FSL_ERRATUM_IFC_A003399
155*4882a593Smuzhiyun    25 : CONFIG_SYS_FSL_ERRATUM_A008044
156*4882a593Smuzhiyun    22 : CONFIG_ARCH_P1020
157*4882a593Smuzhiyun    21 : CONFIG_SYS_FSL_DDR_VER_46
158*4882a593Smuzhiyun    20 : CONFIG_MAX_PIRQ_LINKS
159*4882a593Smuzhiyun    20 : CONFIG_HPET_ADDRESS
160*4882a593Smuzhiyun    20 : CONFIG_X86
161*4882a593Smuzhiyun    20 : CONFIG_PCIE_ECAM_SIZE
162*4882a593Smuzhiyun    20 : CONFIG_IRQ_SLOT_COUNT
163*4882a593Smuzhiyun    20 : CONFIG_I8259_PIC
164*4882a593Smuzhiyun    20 : CONFIG_CPU_ADDR_BITS
165*4882a593Smuzhiyun    20 : CONFIG_RAMBASE
166*4882a593Smuzhiyun    20 : CONFIG_SYS_FSL_ERRATUM_A005871
167*4882a593Smuzhiyun    20 : CONFIG_PCIE_ECAM_BASE
168*4882a593Smuzhiyun    20 : CONFIG_X86_TSC_TIMER
169*4882a593Smuzhiyun    20 : CONFIG_I8254_TIMER
170*4882a593Smuzhiyun    20 : CONFIG_CMD_GETTIME
171*4882a593Smuzhiyun    19 : CONFIG_SYS_FSL_ERRATUM_A005812
172*4882a593Smuzhiyun    18 : CONFIG_X86_RUN_32BIT
173*4882a593Smuzhiyun    17 : CONFIG_CMD_CHIP_CONFIG
174*4882a593Smuzhiyun    ...
175*4882a593Smuzhiyun
176*4882a593SmuzhiyunThis shows a list of config options which might imply CONFIG_CMD_EEPROM along
177*4882a593Smuzhiyunwith how many defconfigs they cover. From this you can see that CONFIG_X86
178*4882a593Smuzhiyunimplies CONFIG_CMD_EEPROM. Therefore, instead of adding CONFIG_CMD_EEPROM to
179*4882a593Smuzhiyunthe defconfig of every x86 board, you could add a single imply line to the
180*4882a593SmuzhiyunKconfig file:
181*4882a593Smuzhiyun
182*4882a593Smuzhiyun    config X86
183*4882a593Smuzhiyun        bool "x86 architecture"
184*4882a593Smuzhiyun        ...
185*4882a593Smuzhiyun        imply CMD_EEPROM
186*4882a593Smuzhiyun
187*4882a593SmuzhiyunThat will cover 20 defconfigs. Many of the options listed are not suitable as
188*4882a593Smuzhiyunthey are not related. E.g. it would be odd for CONFIG_CMD_GETTIME to imply
189*4882a593SmuzhiyunCMD_EEPROM.
190*4882a593Smuzhiyun
191*4882a593SmuzhiyunUsing this search you can reduce the size of moveconfig patches.
192*4882a593Smuzhiyun
193*4882a593SmuzhiyunYou can automatically add 'imply' statements in the Kconfig with the -a
194*4882a593Smuzhiyunoption:
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun    ./tools/moveconfig.py -s -i CONFIG_SCSI \
197*4882a593Smuzhiyun            -a CONFIG_ARCH_LS1021A,CONFIG_ARCH_LS1043A
198*4882a593Smuzhiyun
199*4882a593SmuzhiyunThis will add 'imply SCSI' to the two CONFIG options mentioned, assuming that
200*4882a593Smuzhiyunthe database indicates that they do actually imply CONFIG_SCSI and do not
201*4882a593Smuzhiyunalready have an 'imply SCSI'.
202*4882a593Smuzhiyun
203*4882a593SmuzhiyunThe output shows where the imply is added:
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun   18 : CONFIG_ARCH_LS1021A       arch/arm/cpu/armv7/ls102xa/Kconfig:1
206*4882a593Smuzhiyun   13 : CONFIG_ARCH_LS1043A       arch/arm/cpu/armv8/fsl-layerscape/Kconfig:11
207*4882a593Smuzhiyun   12 : CONFIG_ARCH_LS1046A       arch/arm/cpu/armv8/fsl-layerscape/Kconfig:31
208*4882a593Smuzhiyun
209*4882a593SmuzhiyunThe first number is the number of boards which can avoid having a special
210*4882a593SmuzhiyunCONFIG_SCSI option in their defconfig file if this 'imply' is added.
211*4882a593SmuzhiyunThe location at the right is the Kconfig file and line number where the config
212*4882a593Smuzhiyunappears. For example, adding 'imply CONFIG_SCSI' to the 'config ARCH_LS1021A'
213*4882a593Smuzhiyunin arch/arm/cpu/armv7/ls102xa/Kconfig at line 1 will help 18 boards to reduce
214*4882a593Smuzhiyunthe size of their defconfig files.
215*4882a593Smuzhiyun
216*4882a593SmuzhiyunIf you want to add an 'imply' to every imply config in the list, you can use
217*4882a593Smuzhiyun
218*4882a593Smuzhiyun    ./tools/moveconfig.py -s -i CONFIG_SCSI -a all
219*4882a593Smuzhiyun
220*4882a593SmuzhiyunTo control which ones are displayed, use -I <list> where list is a list of
221*4882a593Smuzhiyunoptions (use '-I help' to see possible options and their meaning).
222*4882a593Smuzhiyun
223*4882a593SmuzhiyunTo skip showing you options that already have an 'imply' attached, use -A.
224*4882a593Smuzhiyun
225*4882a593SmuzhiyunWhen you have finished adding 'imply' options you can regenerate the
226*4882a593Smuzhiyundefconfig files for affected boards with something like:
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun    git show --stat | ./tools/moveconfig.py -s -d -
229*4882a593Smuzhiyun
230*4882a593SmuzhiyunThis will regenerate only those defconfigs changed in the current commit.
231*4882a593SmuzhiyunIf you start with (say) 100 defconfigs being changed in the commit, and add
232*4882a593Smuzhiyuna few 'imply' options as above, then regenerate, hopefully you can reduce the
233*4882a593Smuzhiyunnumber of defconfigs changed in the commit.
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun
236*4882a593SmuzhiyunAvailable options
237*4882a593Smuzhiyun-----------------
238*4882a593Smuzhiyun
239*4882a593Smuzhiyun -c, --color
240*4882a593Smuzhiyun   Surround each portion of the log with escape sequences to display it
241*4882a593Smuzhiyun   in color on the terminal.
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun -C, --commit
244*4882a593Smuzhiyun   Create a git commit with the changes when the operation is complete. A
245*4882a593Smuzhiyun   standard commit message is used which may need to be edited.
246*4882a593Smuzhiyun
247*4882a593Smuzhiyun -d, --defconfigs
248*4882a593Smuzhiyun  Specify a file containing a list of defconfigs to move.  The defconfig
249*4882a593Smuzhiyun  files can be given with shell-style wildcards. Use '-' to read from stdin.
250*4882a593Smuzhiyun
251*4882a593Smuzhiyun -n, --dry-run
252*4882a593Smuzhiyun   Perform a trial run that does not make any changes.  It is useful to
253*4882a593Smuzhiyun   see what is going to happen before one actually runs it.
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun -e, --exit-on-error
256*4882a593Smuzhiyun   Exit immediately if Make exits with a non-zero status while processing
257*4882a593Smuzhiyun   a defconfig file.
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun -s, --force-sync
260*4882a593Smuzhiyun   Do "make savedefconfig" forcibly for all the defconfig files.
261*4882a593Smuzhiyun   If not specified, "make savedefconfig" only occurs for cases
262*4882a593Smuzhiyun   where at least one CONFIG was moved.
263*4882a593Smuzhiyun
264*4882a593Smuzhiyun -S, --spl
265*4882a593Smuzhiyun   Look for moved config options in spl/include/autoconf.mk instead of
266*4882a593Smuzhiyun   include/autoconf.mk.  This is useful for moving options for SPL build
267*4882a593Smuzhiyun   because SPL related options (mostly prefixed with CONFIG_SPL_) are
268*4882a593Smuzhiyun   sometimes blocked by CONFIG_SPL_BUILD ifdef conditionals.
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun -H, --headers-only
271*4882a593Smuzhiyun   Only cleanup the headers; skip the defconfig processing
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun -j, --jobs
274*4882a593Smuzhiyun   Specify the number of threads to run simultaneously.  If not specified,
275*4882a593Smuzhiyun   the number of threads is the same as the number of CPU cores.
276*4882a593Smuzhiyun
277*4882a593Smuzhiyun -r, --git-ref
278*4882a593Smuzhiyun   Specify the git ref to clone for building the autoconf.mk. If unspecified
279*4882a593Smuzhiyun   use the CWD. This is useful for when changes to the Kconfig affect the
280*4882a593Smuzhiyun   default values and you want to capture the state of the defconfig from
281*4882a593Smuzhiyun   before that change was in effect. If in doubt, specify a ref pre-Kconfig
282*4882a593Smuzhiyun   changes (use HEAD if Kconfig changes are not committed). Worst case it will
283*4882a593Smuzhiyun   take a bit longer to run, but will always do the right thing.
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun -v, --verbose
286*4882a593Smuzhiyun   Show any build errors as boards are built
287*4882a593Smuzhiyun
288*4882a593Smuzhiyun -y, --yes
289*4882a593Smuzhiyun   Instead of prompting, automatically go ahead with all operations. This
290*4882a593Smuzhiyun   includes cleaning up headers, CONFIG_SYS_EXTRA_OPTIONS, the config whitelist
291*4882a593Smuzhiyun   and the README.
292*4882a593Smuzhiyun
293*4882a593SmuzhiyunTo see the complete list of supported options, run
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun  $ tools/moveconfig.py -h
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun"""
298*4882a593Smuzhiyun
299*4882a593Smuzhiyunimport collections
300*4882a593Smuzhiyunimport copy
301*4882a593Smuzhiyunimport difflib
302*4882a593Smuzhiyunimport filecmp
303*4882a593Smuzhiyunimport fnmatch
304*4882a593Smuzhiyunimport glob
305*4882a593Smuzhiyunimport multiprocessing
306*4882a593Smuzhiyunimport optparse
307*4882a593Smuzhiyunimport os
308*4882a593Smuzhiyunimport Queue
309*4882a593Smuzhiyunimport re
310*4882a593Smuzhiyunimport shutil
311*4882a593Smuzhiyunimport subprocess
312*4882a593Smuzhiyunimport sys
313*4882a593Smuzhiyunimport tempfile
314*4882a593Smuzhiyunimport threading
315*4882a593Smuzhiyunimport time
316*4882a593Smuzhiyun
317*4882a593Smuzhiyunsys.path.append(os.path.join(os.path.dirname(__file__), 'buildman'))
318*4882a593Smuzhiyunsys.path.append(os.path.join(os.path.dirname(__file__), 'patman'))
319*4882a593Smuzhiyunimport bsettings
320*4882a593Smuzhiyunimport kconfiglib
321*4882a593Smuzhiyunimport toolchain
322*4882a593Smuzhiyun
323*4882a593SmuzhiyunSHOW_GNU_MAKE = 'scripts/show-gnu-make'
324*4882a593SmuzhiyunSLEEP_TIME=0.03
325*4882a593Smuzhiyun
326*4882a593SmuzhiyunSTATE_IDLE = 0
327*4882a593SmuzhiyunSTATE_DEFCONFIG = 1
328*4882a593SmuzhiyunSTATE_AUTOCONF = 2
329*4882a593SmuzhiyunSTATE_SAVEDEFCONFIG = 3
330*4882a593Smuzhiyun
331*4882a593SmuzhiyunACTION_MOVE = 0
332*4882a593SmuzhiyunACTION_NO_ENTRY = 1
333*4882a593SmuzhiyunACTION_NO_ENTRY_WARN = 2
334*4882a593SmuzhiyunACTION_NO_CHANGE = 3
335*4882a593Smuzhiyun
336*4882a593SmuzhiyunCOLOR_BLACK        = '0;30'
337*4882a593SmuzhiyunCOLOR_RED          = '0;31'
338*4882a593SmuzhiyunCOLOR_GREEN        = '0;32'
339*4882a593SmuzhiyunCOLOR_BROWN        = '0;33'
340*4882a593SmuzhiyunCOLOR_BLUE         = '0;34'
341*4882a593SmuzhiyunCOLOR_PURPLE       = '0;35'
342*4882a593SmuzhiyunCOLOR_CYAN         = '0;36'
343*4882a593SmuzhiyunCOLOR_LIGHT_GRAY   = '0;37'
344*4882a593SmuzhiyunCOLOR_DARK_GRAY    = '1;30'
345*4882a593SmuzhiyunCOLOR_LIGHT_RED    = '1;31'
346*4882a593SmuzhiyunCOLOR_LIGHT_GREEN  = '1;32'
347*4882a593SmuzhiyunCOLOR_YELLOW       = '1;33'
348*4882a593SmuzhiyunCOLOR_LIGHT_BLUE   = '1;34'
349*4882a593SmuzhiyunCOLOR_LIGHT_PURPLE = '1;35'
350*4882a593SmuzhiyunCOLOR_LIGHT_CYAN   = '1;36'
351*4882a593SmuzhiyunCOLOR_WHITE        = '1;37'
352*4882a593Smuzhiyun
353*4882a593SmuzhiyunAUTO_CONF_PATH = 'include/config/auto.conf'
354*4882a593SmuzhiyunCONFIG_DATABASE = 'moveconfig.db'
355*4882a593Smuzhiyun
356*4882a593SmuzhiyunCONFIG_LEN = len('CONFIG_')
357*4882a593Smuzhiyun
358*4882a593Smuzhiyun### helper functions ###
359*4882a593Smuzhiyundef get_devnull():
360*4882a593Smuzhiyun    """Get the file object of '/dev/null' device."""
361*4882a593Smuzhiyun    try:
362*4882a593Smuzhiyun        devnull = subprocess.DEVNULL # py3k
363*4882a593Smuzhiyun    except AttributeError:
364*4882a593Smuzhiyun        devnull = open(os.devnull, 'wb')
365*4882a593Smuzhiyun    return devnull
366*4882a593Smuzhiyun
367*4882a593Smuzhiyundef check_top_directory():
368*4882a593Smuzhiyun    """Exit if we are not at the top of source directory."""
369*4882a593Smuzhiyun    for f in ('README', 'Licenses'):
370*4882a593Smuzhiyun        if not os.path.exists(f):
371*4882a593Smuzhiyun            sys.exit('Please run at the top of source directory.')
372*4882a593Smuzhiyun
373*4882a593Smuzhiyundef check_clean_directory():
374*4882a593Smuzhiyun    """Exit if the source tree is not clean."""
375*4882a593Smuzhiyun    for f in ('.config', 'include/config'):
376*4882a593Smuzhiyun        if os.path.exists(f):
377*4882a593Smuzhiyun            sys.exit("source tree is not clean, please run 'make mrproper'")
378*4882a593Smuzhiyun
379*4882a593Smuzhiyundef get_make_cmd():
380*4882a593Smuzhiyun    """Get the command name of GNU Make.
381*4882a593Smuzhiyun
382*4882a593Smuzhiyun    U-Boot needs GNU Make for building, but the command name is not
383*4882a593Smuzhiyun    necessarily "make". (for example, "gmake" on FreeBSD).
384*4882a593Smuzhiyun    Returns the most appropriate command name on your system.
385*4882a593Smuzhiyun    """
386*4882a593Smuzhiyun    process = subprocess.Popen([SHOW_GNU_MAKE], stdout=subprocess.PIPE)
387*4882a593Smuzhiyun    ret = process.communicate()
388*4882a593Smuzhiyun    if process.returncode:
389*4882a593Smuzhiyun        sys.exit('GNU Make not found')
390*4882a593Smuzhiyun    return ret[0].rstrip()
391*4882a593Smuzhiyun
392*4882a593Smuzhiyundef get_matched_defconfig(line):
393*4882a593Smuzhiyun    """Get the defconfig files that match a pattern
394*4882a593Smuzhiyun
395*4882a593Smuzhiyun    Args:
396*4882a593Smuzhiyun        line: Path or filename to match, e.g. 'configs/snow_defconfig' or
397*4882a593Smuzhiyun            'k2*_defconfig'. If no directory is provided, 'configs/' is
398*4882a593Smuzhiyun            prepended
399*4882a593Smuzhiyun
400*4882a593Smuzhiyun    Returns:
401*4882a593Smuzhiyun        a list of matching defconfig files
402*4882a593Smuzhiyun    """
403*4882a593Smuzhiyun    dirname = os.path.dirname(line)
404*4882a593Smuzhiyun    if dirname:
405*4882a593Smuzhiyun        pattern = line
406*4882a593Smuzhiyun    else:
407*4882a593Smuzhiyun        pattern = os.path.join('configs', line)
408*4882a593Smuzhiyun    return glob.glob(pattern) + glob.glob(pattern + '_defconfig')
409*4882a593Smuzhiyun
410*4882a593Smuzhiyundef get_matched_defconfigs(defconfigs_file):
411*4882a593Smuzhiyun    """Get all the defconfig files that match the patterns in a file.
412*4882a593Smuzhiyun
413*4882a593Smuzhiyun    Args:
414*4882a593Smuzhiyun        defconfigs_file: File containing a list of defconfigs to process, or
415*4882a593Smuzhiyun            '-' to read the list from stdin
416*4882a593Smuzhiyun
417*4882a593Smuzhiyun    Returns:
418*4882a593Smuzhiyun        A list of paths to defconfig files, with no duplicates
419*4882a593Smuzhiyun    """
420*4882a593Smuzhiyun    defconfigs = []
421*4882a593Smuzhiyun    if defconfigs_file == '-':
422*4882a593Smuzhiyun        fd = sys.stdin
423*4882a593Smuzhiyun        defconfigs_file = 'stdin'
424*4882a593Smuzhiyun    else:
425*4882a593Smuzhiyun        fd = open(defconfigs_file)
426*4882a593Smuzhiyun    for i, line in enumerate(fd):
427*4882a593Smuzhiyun        line = line.strip()
428*4882a593Smuzhiyun        if not line:
429*4882a593Smuzhiyun            continue # skip blank lines silently
430*4882a593Smuzhiyun        if ' ' in line:
431*4882a593Smuzhiyun            line = line.split(' ')[0]  # handle 'git log' input
432*4882a593Smuzhiyun        matched = get_matched_defconfig(line)
433*4882a593Smuzhiyun        if not matched:
434*4882a593Smuzhiyun            print >> sys.stderr, "warning: %s:%d: no defconfig matched '%s'" % \
435*4882a593Smuzhiyun                                                 (defconfigs_file, i + 1, line)
436*4882a593Smuzhiyun
437*4882a593Smuzhiyun        defconfigs += matched
438*4882a593Smuzhiyun
439*4882a593Smuzhiyun    # use set() to drop multiple matching
440*4882a593Smuzhiyun    return [ defconfig[len('configs') + 1:]  for defconfig in set(defconfigs) ]
441*4882a593Smuzhiyun
442*4882a593Smuzhiyundef get_all_defconfigs():
443*4882a593Smuzhiyun    """Get all the defconfig files under the configs/ directory."""
444*4882a593Smuzhiyun    defconfigs = []
445*4882a593Smuzhiyun    for (dirpath, dirnames, filenames) in os.walk('configs'):
446*4882a593Smuzhiyun        dirpath = dirpath[len('configs') + 1:]
447*4882a593Smuzhiyun        for filename in fnmatch.filter(filenames, '*_defconfig'):
448*4882a593Smuzhiyun            defconfigs.append(os.path.join(dirpath, filename))
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun    return defconfigs
451*4882a593Smuzhiyun
452*4882a593Smuzhiyundef color_text(color_enabled, color, string):
453*4882a593Smuzhiyun    """Return colored string."""
454*4882a593Smuzhiyun    if color_enabled:
455*4882a593Smuzhiyun        # LF should not be surrounded by the escape sequence.
456*4882a593Smuzhiyun        # Otherwise, additional whitespace or line-feed might be printed.
457*4882a593Smuzhiyun        return '\n'.join([ '\033[' + color + 'm' + s + '\033[0m' if s else ''
458*4882a593Smuzhiyun                           for s in string.split('\n') ])
459*4882a593Smuzhiyun    else:
460*4882a593Smuzhiyun        return string
461*4882a593Smuzhiyun
462*4882a593Smuzhiyundef show_diff(a, b, file_path, color_enabled):
463*4882a593Smuzhiyun    """Show unidified diff.
464*4882a593Smuzhiyun
465*4882a593Smuzhiyun    Arguments:
466*4882a593Smuzhiyun      a: A list of lines (before)
467*4882a593Smuzhiyun      b: A list of lines (after)
468*4882a593Smuzhiyun      file_path: Path to the file
469*4882a593Smuzhiyun      color_enabled: Display the diff in color
470*4882a593Smuzhiyun    """
471*4882a593Smuzhiyun
472*4882a593Smuzhiyun    diff = difflib.unified_diff(a, b,
473*4882a593Smuzhiyun                                fromfile=os.path.join('a', file_path),
474*4882a593Smuzhiyun                                tofile=os.path.join('b', file_path))
475*4882a593Smuzhiyun
476*4882a593Smuzhiyun    for line in diff:
477*4882a593Smuzhiyun        if line[0] == '-' and line[1] != '-':
478*4882a593Smuzhiyun            print color_text(color_enabled, COLOR_RED, line),
479*4882a593Smuzhiyun        elif line[0] == '+' and line[1] != '+':
480*4882a593Smuzhiyun            print color_text(color_enabled, COLOR_GREEN, line),
481*4882a593Smuzhiyun        else:
482*4882a593Smuzhiyun            print line,
483*4882a593Smuzhiyun
484*4882a593Smuzhiyundef extend_matched_lines(lines, matched, pre_patterns, post_patterns, extend_pre,
485*4882a593Smuzhiyun                         extend_post):
486*4882a593Smuzhiyun    """Extend matched lines if desired patterns are found before/after already
487*4882a593Smuzhiyun    matched lines.
488*4882a593Smuzhiyun
489*4882a593Smuzhiyun    Arguments:
490*4882a593Smuzhiyun      lines: A list of lines handled.
491*4882a593Smuzhiyun      matched: A list of line numbers that have been already matched.
492*4882a593Smuzhiyun               (will be updated by this function)
493*4882a593Smuzhiyun      pre_patterns: A list of regular expression that should be matched as
494*4882a593Smuzhiyun                    preamble.
495*4882a593Smuzhiyun      post_patterns: A list of regular expression that should be matched as
496*4882a593Smuzhiyun                     postamble.
497*4882a593Smuzhiyun      extend_pre: Add the line number of matched preamble to the matched list.
498*4882a593Smuzhiyun      extend_post: Add the line number of matched postamble to the matched list.
499*4882a593Smuzhiyun    """
500*4882a593Smuzhiyun    extended_matched = []
501*4882a593Smuzhiyun
502*4882a593Smuzhiyun    j = matched[0]
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun    for i in matched:
505*4882a593Smuzhiyun        if i == 0 or i < j:
506*4882a593Smuzhiyun            continue
507*4882a593Smuzhiyun        j = i
508*4882a593Smuzhiyun        while j in matched:
509*4882a593Smuzhiyun            j += 1
510*4882a593Smuzhiyun        if j >= len(lines):
511*4882a593Smuzhiyun            break
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun        for p in pre_patterns:
514*4882a593Smuzhiyun            if p.search(lines[i - 1]):
515*4882a593Smuzhiyun                break
516*4882a593Smuzhiyun        else:
517*4882a593Smuzhiyun            # not matched
518*4882a593Smuzhiyun            continue
519*4882a593Smuzhiyun
520*4882a593Smuzhiyun        for p in post_patterns:
521*4882a593Smuzhiyun            if p.search(lines[j]):
522*4882a593Smuzhiyun                break
523*4882a593Smuzhiyun        else:
524*4882a593Smuzhiyun            # not matched
525*4882a593Smuzhiyun            continue
526*4882a593Smuzhiyun
527*4882a593Smuzhiyun        if extend_pre:
528*4882a593Smuzhiyun            extended_matched.append(i - 1)
529*4882a593Smuzhiyun        if extend_post:
530*4882a593Smuzhiyun            extended_matched.append(j)
531*4882a593Smuzhiyun
532*4882a593Smuzhiyun    matched += extended_matched
533*4882a593Smuzhiyun    matched.sort()
534*4882a593Smuzhiyun
535*4882a593Smuzhiyundef confirm(options, prompt):
536*4882a593Smuzhiyun    if not options.yes:
537*4882a593Smuzhiyun        while True:
538*4882a593Smuzhiyun            choice = raw_input('{} [y/n]: '.format(prompt))
539*4882a593Smuzhiyun            choice = choice.lower()
540*4882a593Smuzhiyun            print choice
541*4882a593Smuzhiyun            if choice == 'y' or choice == 'n':
542*4882a593Smuzhiyun                break
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun        if choice == 'n':
545*4882a593Smuzhiyun            return False
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun    return True
548*4882a593Smuzhiyun
549*4882a593Smuzhiyundef cleanup_one_header(header_path, patterns, options):
550*4882a593Smuzhiyun    """Clean regex-matched lines away from a file.
551*4882a593Smuzhiyun
552*4882a593Smuzhiyun    Arguments:
553*4882a593Smuzhiyun      header_path: path to the cleaned file.
554*4882a593Smuzhiyun      patterns: list of regex patterns.  Any lines matching to these
555*4882a593Smuzhiyun                patterns are deleted.
556*4882a593Smuzhiyun      options: option flags.
557*4882a593Smuzhiyun    """
558*4882a593Smuzhiyun    with open(header_path) as f:
559*4882a593Smuzhiyun        lines = f.readlines()
560*4882a593Smuzhiyun
561*4882a593Smuzhiyun    matched = []
562*4882a593Smuzhiyun    for i, line in enumerate(lines):
563*4882a593Smuzhiyun        if i - 1 in matched and lines[i - 1][-2:] == '\\\n':
564*4882a593Smuzhiyun            matched.append(i)
565*4882a593Smuzhiyun            continue
566*4882a593Smuzhiyun        for pattern in patterns:
567*4882a593Smuzhiyun            if pattern.search(line):
568*4882a593Smuzhiyun                matched.append(i)
569*4882a593Smuzhiyun                break
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun    if not matched:
572*4882a593Smuzhiyun        return
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun    # remove empty #ifdef ... #endif, successive blank lines
575*4882a593Smuzhiyun    pattern_if = re.compile(r'#\s*if(def|ndef)?\W') #  #if, #ifdef, #ifndef
576*4882a593Smuzhiyun    pattern_elif = re.compile(r'#\s*el(if|se)\W')   #  #elif, #else
577*4882a593Smuzhiyun    pattern_endif = re.compile(r'#\s*endif\W')      #  #endif
578*4882a593Smuzhiyun    pattern_blank = re.compile(r'^\s*$')            #  empty line
579*4882a593Smuzhiyun
580*4882a593Smuzhiyun    while True:
581*4882a593Smuzhiyun        old_matched = copy.copy(matched)
582*4882a593Smuzhiyun        extend_matched_lines(lines, matched, [pattern_if],
583*4882a593Smuzhiyun                             [pattern_endif], True, True)
584*4882a593Smuzhiyun        extend_matched_lines(lines, matched, [pattern_elif],
585*4882a593Smuzhiyun                             [pattern_elif, pattern_endif], True, False)
586*4882a593Smuzhiyun        extend_matched_lines(lines, matched, [pattern_if, pattern_elif],
587*4882a593Smuzhiyun                             [pattern_blank], False, True)
588*4882a593Smuzhiyun        extend_matched_lines(lines, matched, [pattern_blank],
589*4882a593Smuzhiyun                             [pattern_elif, pattern_endif], True, False)
590*4882a593Smuzhiyun        extend_matched_lines(lines, matched, [pattern_blank],
591*4882a593Smuzhiyun                             [pattern_blank], True, False)
592*4882a593Smuzhiyun        if matched == old_matched:
593*4882a593Smuzhiyun            break
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun    tolines = copy.copy(lines)
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun    for i in reversed(matched):
598*4882a593Smuzhiyun        tolines.pop(i)
599*4882a593Smuzhiyun
600*4882a593Smuzhiyun    show_diff(lines, tolines, header_path, options.color)
601*4882a593Smuzhiyun
602*4882a593Smuzhiyun    if options.dry_run:
603*4882a593Smuzhiyun        return
604*4882a593Smuzhiyun
605*4882a593Smuzhiyun    with open(header_path, 'w') as f:
606*4882a593Smuzhiyun        for line in tolines:
607*4882a593Smuzhiyun            f.write(line)
608*4882a593Smuzhiyun
609*4882a593Smuzhiyundef cleanup_headers(configs, options):
610*4882a593Smuzhiyun    """Delete config defines from board headers.
611*4882a593Smuzhiyun
612*4882a593Smuzhiyun    Arguments:
613*4882a593Smuzhiyun      configs: A list of CONFIGs to remove.
614*4882a593Smuzhiyun      options: option flags.
615*4882a593Smuzhiyun    """
616*4882a593Smuzhiyun    if not confirm(options, 'Clean up headers?'):
617*4882a593Smuzhiyun        return
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun    patterns = []
620*4882a593Smuzhiyun    for config in configs:
621*4882a593Smuzhiyun        patterns.append(re.compile(r'#\s*define\s+%s\W' % config))
622*4882a593Smuzhiyun        patterns.append(re.compile(r'#\s*undef\s+%s\W' % config))
623*4882a593Smuzhiyun
624*4882a593Smuzhiyun    for dir in 'include', 'arch', 'board':
625*4882a593Smuzhiyun        for (dirpath, dirnames, filenames) in os.walk(dir):
626*4882a593Smuzhiyun            if dirpath == os.path.join('include', 'generated'):
627*4882a593Smuzhiyun                continue
628*4882a593Smuzhiyun            for filename in filenames:
629*4882a593Smuzhiyun                if not fnmatch.fnmatch(filename, '*~'):
630*4882a593Smuzhiyun                    cleanup_one_header(os.path.join(dirpath, filename),
631*4882a593Smuzhiyun                                       patterns, options)
632*4882a593Smuzhiyun
633*4882a593Smuzhiyundef cleanup_one_extra_option(defconfig_path, configs, options):
634*4882a593Smuzhiyun    """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in one defconfig file.
635*4882a593Smuzhiyun
636*4882a593Smuzhiyun    Arguments:
637*4882a593Smuzhiyun      defconfig_path: path to the cleaned defconfig file.
638*4882a593Smuzhiyun      configs: A list of CONFIGs to remove.
639*4882a593Smuzhiyun      options: option flags.
640*4882a593Smuzhiyun    """
641*4882a593Smuzhiyun
642*4882a593Smuzhiyun    start = 'CONFIG_SYS_EXTRA_OPTIONS="'
643*4882a593Smuzhiyun    end = '"\n'
644*4882a593Smuzhiyun
645*4882a593Smuzhiyun    with open(defconfig_path) as f:
646*4882a593Smuzhiyun        lines = f.readlines()
647*4882a593Smuzhiyun
648*4882a593Smuzhiyun    for i, line in enumerate(lines):
649*4882a593Smuzhiyun        if line.startswith(start) and line.endswith(end):
650*4882a593Smuzhiyun            break
651*4882a593Smuzhiyun    else:
652*4882a593Smuzhiyun        # CONFIG_SYS_EXTRA_OPTIONS was not found in this defconfig
653*4882a593Smuzhiyun        return
654*4882a593Smuzhiyun
655*4882a593Smuzhiyun    old_tokens = line[len(start):-len(end)].split(',')
656*4882a593Smuzhiyun    new_tokens = []
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun    for token in old_tokens:
659*4882a593Smuzhiyun        pos = token.find('=')
660*4882a593Smuzhiyun        if not (token[:pos] if pos >= 0 else token) in configs:
661*4882a593Smuzhiyun            new_tokens.append(token)
662*4882a593Smuzhiyun
663*4882a593Smuzhiyun    if new_tokens == old_tokens:
664*4882a593Smuzhiyun        return
665*4882a593Smuzhiyun
666*4882a593Smuzhiyun    tolines = copy.copy(lines)
667*4882a593Smuzhiyun
668*4882a593Smuzhiyun    if new_tokens:
669*4882a593Smuzhiyun        tolines[i] = start + ','.join(new_tokens) + end
670*4882a593Smuzhiyun    else:
671*4882a593Smuzhiyun        tolines.pop(i)
672*4882a593Smuzhiyun
673*4882a593Smuzhiyun    show_diff(lines, tolines, defconfig_path, options.color)
674*4882a593Smuzhiyun
675*4882a593Smuzhiyun    if options.dry_run:
676*4882a593Smuzhiyun        return
677*4882a593Smuzhiyun
678*4882a593Smuzhiyun    with open(defconfig_path, 'w') as f:
679*4882a593Smuzhiyun        for line in tolines:
680*4882a593Smuzhiyun            f.write(line)
681*4882a593Smuzhiyun
682*4882a593Smuzhiyundef cleanup_extra_options(configs, options):
683*4882a593Smuzhiyun    """Delete config defines in CONFIG_SYS_EXTRA_OPTIONS in defconfig files.
684*4882a593Smuzhiyun
685*4882a593Smuzhiyun    Arguments:
686*4882a593Smuzhiyun      configs: A list of CONFIGs to remove.
687*4882a593Smuzhiyun      options: option flags.
688*4882a593Smuzhiyun    """
689*4882a593Smuzhiyun    if not confirm(options, 'Clean up CONFIG_SYS_EXTRA_OPTIONS?'):
690*4882a593Smuzhiyun        return
691*4882a593Smuzhiyun
692*4882a593Smuzhiyun    configs = [ config[len('CONFIG_'):] for config in configs ]
693*4882a593Smuzhiyun
694*4882a593Smuzhiyun    defconfigs = get_all_defconfigs()
695*4882a593Smuzhiyun
696*4882a593Smuzhiyun    for defconfig in defconfigs:
697*4882a593Smuzhiyun        cleanup_one_extra_option(os.path.join('configs', defconfig), configs,
698*4882a593Smuzhiyun                                 options)
699*4882a593Smuzhiyun
700*4882a593Smuzhiyundef cleanup_whitelist(configs, options):
701*4882a593Smuzhiyun    """Delete config whitelist entries
702*4882a593Smuzhiyun
703*4882a593Smuzhiyun    Arguments:
704*4882a593Smuzhiyun      configs: A list of CONFIGs to remove.
705*4882a593Smuzhiyun      options: option flags.
706*4882a593Smuzhiyun    """
707*4882a593Smuzhiyun    if not confirm(options, 'Clean up whitelist entries?'):
708*4882a593Smuzhiyun        return
709*4882a593Smuzhiyun
710*4882a593Smuzhiyun    with open(os.path.join('scripts', 'config_whitelist.txt')) as f:
711*4882a593Smuzhiyun        lines = f.readlines()
712*4882a593Smuzhiyun
713*4882a593Smuzhiyun    lines = [x for x in lines if x.strip() not in configs]
714*4882a593Smuzhiyun
715*4882a593Smuzhiyun    with open(os.path.join('scripts', 'config_whitelist.txt'), 'w') as f:
716*4882a593Smuzhiyun        f.write(''.join(lines))
717*4882a593Smuzhiyun
718*4882a593Smuzhiyundef find_matching(patterns, line):
719*4882a593Smuzhiyun    for pat in patterns:
720*4882a593Smuzhiyun        if pat.search(line):
721*4882a593Smuzhiyun            return True
722*4882a593Smuzhiyun    return False
723*4882a593Smuzhiyun
724*4882a593Smuzhiyundef cleanup_readme(configs, options):
725*4882a593Smuzhiyun    """Delete config description in README
726*4882a593Smuzhiyun
727*4882a593Smuzhiyun    Arguments:
728*4882a593Smuzhiyun      configs: A list of CONFIGs to remove.
729*4882a593Smuzhiyun      options: option flags.
730*4882a593Smuzhiyun    """
731*4882a593Smuzhiyun    if not confirm(options, 'Clean up README?'):
732*4882a593Smuzhiyun        return
733*4882a593Smuzhiyun
734*4882a593Smuzhiyun    patterns = []
735*4882a593Smuzhiyun    for config in configs:
736*4882a593Smuzhiyun        patterns.append(re.compile(r'^\s+%s' % config))
737*4882a593Smuzhiyun
738*4882a593Smuzhiyun    with open('README') as f:
739*4882a593Smuzhiyun        lines = f.readlines()
740*4882a593Smuzhiyun
741*4882a593Smuzhiyun    found = False
742*4882a593Smuzhiyun    newlines = []
743*4882a593Smuzhiyun    for line in lines:
744*4882a593Smuzhiyun        if not found:
745*4882a593Smuzhiyun            found = find_matching(patterns, line)
746*4882a593Smuzhiyun            if found:
747*4882a593Smuzhiyun                continue
748*4882a593Smuzhiyun
749*4882a593Smuzhiyun        if found and re.search(r'^\s+CONFIG', line):
750*4882a593Smuzhiyun            found = False
751*4882a593Smuzhiyun
752*4882a593Smuzhiyun        if not found:
753*4882a593Smuzhiyun            newlines.append(line)
754*4882a593Smuzhiyun
755*4882a593Smuzhiyun    with open('README', 'w') as f:
756*4882a593Smuzhiyun        f.write(''.join(newlines))
757*4882a593Smuzhiyun
758*4882a593Smuzhiyun
759*4882a593Smuzhiyun### classes ###
760*4882a593Smuzhiyunclass Progress:
761*4882a593Smuzhiyun
762*4882a593Smuzhiyun    """Progress Indicator"""
763*4882a593Smuzhiyun
764*4882a593Smuzhiyun    def __init__(self, total):
765*4882a593Smuzhiyun        """Create a new progress indicator.
766*4882a593Smuzhiyun
767*4882a593Smuzhiyun        Arguments:
768*4882a593Smuzhiyun          total: A number of defconfig files to process.
769*4882a593Smuzhiyun        """
770*4882a593Smuzhiyun        self.current = 0
771*4882a593Smuzhiyun        self.total = total
772*4882a593Smuzhiyun
773*4882a593Smuzhiyun    def inc(self):
774*4882a593Smuzhiyun        """Increment the number of processed defconfig files."""
775*4882a593Smuzhiyun
776*4882a593Smuzhiyun        self.current += 1
777*4882a593Smuzhiyun
778*4882a593Smuzhiyun    def show(self):
779*4882a593Smuzhiyun        """Display the progress."""
780*4882a593Smuzhiyun        print ' %d defconfigs out of %d\r' % (self.current, self.total),
781*4882a593Smuzhiyun        sys.stdout.flush()
782*4882a593Smuzhiyun
783*4882a593Smuzhiyun
784*4882a593Smuzhiyunclass KconfigScanner:
785*4882a593Smuzhiyun    """Kconfig scanner."""
786*4882a593Smuzhiyun
787*4882a593Smuzhiyun    def __init__(self):
788*4882a593Smuzhiyun        """Scan all the Kconfig files and create a Config object."""
789*4882a593Smuzhiyun        # Define environment variables referenced from Kconfig
790*4882a593Smuzhiyun        os.environ['srctree'] = os.getcwd()
791*4882a593Smuzhiyun        os.environ['UBOOTVERSION'] = 'dummy'
792*4882a593Smuzhiyun        os.environ['KCONFIG_OBJDIR'] = ''
793*4882a593Smuzhiyun        self.conf = kconfiglib.Config()
794*4882a593Smuzhiyun
795*4882a593Smuzhiyun
796*4882a593Smuzhiyunclass KconfigParser:
797*4882a593Smuzhiyun
798*4882a593Smuzhiyun    """A parser of .config and include/autoconf.mk."""
799*4882a593Smuzhiyun
800*4882a593Smuzhiyun    re_arch = re.compile(r'CONFIG_SYS_ARCH="(.*)"')
801*4882a593Smuzhiyun    re_cpu = re.compile(r'CONFIG_SYS_CPU="(.*)"')
802*4882a593Smuzhiyun
803*4882a593Smuzhiyun    def __init__(self, configs, options, build_dir):
804*4882a593Smuzhiyun        """Create a new parser.
805*4882a593Smuzhiyun
806*4882a593Smuzhiyun        Arguments:
807*4882a593Smuzhiyun          configs: A list of CONFIGs to move.
808*4882a593Smuzhiyun          options: option flags.
809*4882a593Smuzhiyun          build_dir: Build directory.
810*4882a593Smuzhiyun        """
811*4882a593Smuzhiyun        self.configs = configs
812*4882a593Smuzhiyun        self.options = options
813*4882a593Smuzhiyun        self.dotconfig = os.path.join(build_dir, '.config')
814*4882a593Smuzhiyun        self.autoconf = os.path.join(build_dir, 'include', 'autoconf.mk')
815*4882a593Smuzhiyun        self.spl_autoconf = os.path.join(build_dir, 'spl', 'include',
816*4882a593Smuzhiyun                                         'autoconf.mk')
817*4882a593Smuzhiyun        self.config_autoconf = os.path.join(build_dir, AUTO_CONF_PATH)
818*4882a593Smuzhiyun        self.defconfig = os.path.join(build_dir, 'defconfig')
819*4882a593Smuzhiyun
820*4882a593Smuzhiyun    def get_arch(self):
821*4882a593Smuzhiyun        """Parse .config file and return the architecture.
822*4882a593Smuzhiyun
823*4882a593Smuzhiyun        Returns:
824*4882a593Smuzhiyun          Architecture name (e.g. 'arm').
825*4882a593Smuzhiyun        """
826*4882a593Smuzhiyun        arch = ''
827*4882a593Smuzhiyun        cpu = ''
828*4882a593Smuzhiyun        for line in open(self.dotconfig):
829*4882a593Smuzhiyun            m = self.re_arch.match(line)
830*4882a593Smuzhiyun            if m:
831*4882a593Smuzhiyun                arch = m.group(1)
832*4882a593Smuzhiyun                continue
833*4882a593Smuzhiyun            m = self.re_cpu.match(line)
834*4882a593Smuzhiyun            if m:
835*4882a593Smuzhiyun                cpu = m.group(1)
836*4882a593Smuzhiyun
837*4882a593Smuzhiyun        if not arch:
838*4882a593Smuzhiyun            return None
839*4882a593Smuzhiyun
840*4882a593Smuzhiyun        # fix-up for aarch64
841*4882a593Smuzhiyun        if arch == 'arm' and cpu == 'armv8':
842*4882a593Smuzhiyun            arch = 'aarch64'
843*4882a593Smuzhiyun
844*4882a593Smuzhiyun        return arch
845*4882a593Smuzhiyun
846*4882a593Smuzhiyun    def parse_one_config(self, config, dotconfig_lines, autoconf_lines):
847*4882a593Smuzhiyun        """Parse .config, defconfig, include/autoconf.mk for one config.
848*4882a593Smuzhiyun
849*4882a593Smuzhiyun        This function looks for the config options in the lines from
850*4882a593Smuzhiyun        defconfig, .config, and include/autoconf.mk in order to decide
851*4882a593Smuzhiyun        which action should be taken for this defconfig.
852*4882a593Smuzhiyun
853*4882a593Smuzhiyun        Arguments:
854*4882a593Smuzhiyun          config: CONFIG name to parse.
855*4882a593Smuzhiyun          dotconfig_lines: lines from the .config file.
856*4882a593Smuzhiyun          autoconf_lines: lines from the include/autoconf.mk file.
857*4882a593Smuzhiyun
858*4882a593Smuzhiyun        Returns:
859*4882a593Smuzhiyun          A tupple of the action for this defconfig and the line
860*4882a593Smuzhiyun          matched for the config.
861*4882a593Smuzhiyun        """
862*4882a593Smuzhiyun        not_set = '# %s is not set' % config
863*4882a593Smuzhiyun
864*4882a593Smuzhiyun        for line in autoconf_lines:
865*4882a593Smuzhiyun            line = line.rstrip()
866*4882a593Smuzhiyun            if line.startswith(config + '='):
867*4882a593Smuzhiyun                new_val = line
868*4882a593Smuzhiyun                break
869*4882a593Smuzhiyun        else:
870*4882a593Smuzhiyun            new_val = not_set
871*4882a593Smuzhiyun
872*4882a593Smuzhiyun        for line in dotconfig_lines:
873*4882a593Smuzhiyun            line = line.rstrip()
874*4882a593Smuzhiyun            if line.startswith(config + '=') or line == not_set:
875*4882a593Smuzhiyun                old_val = line
876*4882a593Smuzhiyun                break
877*4882a593Smuzhiyun        else:
878*4882a593Smuzhiyun            if new_val == not_set:
879*4882a593Smuzhiyun                return (ACTION_NO_ENTRY, config)
880*4882a593Smuzhiyun            else:
881*4882a593Smuzhiyun                return (ACTION_NO_ENTRY_WARN, config)
882*4882a593Smuzhiyun
883*4882a593Smuzhiyun        # If this CONFIG is neither bool nor trisate
884*4882a593Smuzhiyun        if old_val[-2:] != '=y' and old_val[-2:] != '=m' and old_val != not_set:
885*4882a593Smuzhiyun            # tools/scripts/define2mk.sed changes '1' to 'y'.
886*4882a593Smuzhiyun            # This is a problem if the CONFIG is int type.
887*4882a593Smuzhiyun            # Check the type in Kconfig and handle it correctly.
888*4882a593Smuzhiyun            if new_val[-2:] == '=y':
889*4882a593Smuzhiyun                new_val = new_val[:-1] + '1'
890*4882a593Smuzhiyun
891*4882a593Smuzhiyun        return (ACTION_NO_CHANGE if old_val == new_val else ACTION_MOVE,
892*4882a593Smuzhiyun                new_val)
893*4882a593Smuzhiyun
894*4882a593Smuzhiyun    def update_dotconfig(self):
895*4882a593Smuzhiyun        """Parse files for the config options and update the .config.
896*4882a593Smuzhiyun
897*4882a593Smuzhiyun        This function parses the generated .config and include/autoconf.mk
898*4882a593Smuzhiyun        searching the target options.
899*4882a593Smuzhiyun        Move the config option(s) to the .config as needed.
900*4882a593Smuzhiyun
901*4882a593Smuzhiyun        Arguments:
902*4882a593Smuzhiyun          defconfig: defconfig name.
903*4882a593Smuzhiyun
904*4882a593Smuzhiyun        Returns:
905*4882a593Smuzhiyun          Return a tuple of (updated flag, log string).
906*4882a593Smuzhiyun          The "updated flag" is True if the .config was updated, False
907*4882a593Smuzhiyun          otherwise.  The "log string" shows what happend to the .config.
908*4882a593Smuzhiyun        """
909*4882a593Smuzhiyun
910*4882a593Smuzhiyun        results = []
911*4882a593Smuzhiyun        updated = False
912*4882a593Smuzhiyun        suspicious = False
913*4882a593Smuzhiyun        rm_files = [self.config_autoconf, self.autoconf]
914*4882a593Smuzhiyun
915*4882a593Smuzhiyun        if self.options.spl:
916*4882a593Smuzhiyun            if os.path.exists(self.spl_autoconf):
917*4882a593Smuzhiyun                autoconf_path = self.spl_autoconf
918*4882a593Smuzhiyun                rm_files.append(self.spl_autoconf)
919*4882a593Smuzhiyun            else:
920*4882a593Smuzhiyun                for f in rm_files:
921*4882a593Smuzhiyun                    os.remove(f)
922*4882a593Smuzhiyun                return (updated, suspicious,
923*4882a593Smuzhiyun                        color_text(self.options.color, COLOR_BROWN,
924*4882a593Smuzhiyun                                   "SPL is not enabled.  Skipped.") + '\n')
925*4882a593Smuzhiyun        else:
926*4882a593Smuzhiyun            autoconf_path = self.autoconf
927*4882a593Smuzhiyun
928*4882a593Smuzhiyun        with open(self.dotconfig) as f:
929*4882a593Smuzhiyun            dotconfig_lines = f.readlines()
930*4882a593Smuzhiyun
931*4882a593Smuzhiyun        with open(autoconf_path) as f:
932*4882a593Smuzhiyun            autoconf_lines = f.readlines()
933*4882a593Smuzhiyun
934*4882a593Smuzhiyun        for config in self.configs:
935*4882a593Smuzhiyun            result = self.parse_one_config(config, dotconfig_lines,
936*4882a593Smuzhiyun                                           autoconf_lines)
937*4882a593Smuzhiyun            results.append(result)
938*4882a593Smuzhiyun
939*4882a593Smuzhiyun        log = ''
940*4882a593Smuzhiyun
941*4882a593Smuzhiyun        for (action, value) in results:
942*4882a593Smuzhiyun            if action == ACTION_MOVE:
943*4882a593Smuzhiyun                actlog = "Move '%s'" % value
944*4882a593Smuzhiyun                log_color = COLOR_LIGHT_GREEN
945*4882a593Smuzhiyun            elif action == ACTION_NO_ENTRY:
946*4882a593Smuzhiyun                actlog = "%s is not defined in Kconfig.  Do nothing." % value
947*4882a593Smuzhiyun                log_color = COLOR_LIGHT_BLUE
948*4882a593Smuzhiyun            elif action == ACTION_NO_ENTRY_WARN:
949*4882a593Smuzhiyun                actlog = "%s is not defined in Kconfig (suspicious).  Do nothing." % value
950*4882a593Smuzhiyun                log_color = COLOR_YELLOW
951*4882a593Smuzhiyun                suspicious = True
952*4882a593Smuzhiyun            elif action == ACTION_NO_CHANGE:
953*4882a593Smuzhiyun                actlog = "'%s' is the same as the define in Kconfig.  Do nothing." \
954*4882a593Smuzhiyun                         % value
955*4882a593Smuzhiyun                log_color = COLOR_LIGHT_PURPLE
956*4882a593Smuzhiyun            elif action == ACTION_SPL_NOT_EXIST:
957*4882a593Smuzhiyun                actlog = "SPL is not enabled for this defconfig.  Skip."
958*4882a593Smuzhiyun                log_color = COLOR_PURPLE
959*4882a593Smuzhiyun            else:
960*4882a593Smuzhiyun                sys.exit("Internal Error. This should not happen.")
961*4882a593Smuzhiyun
962*4882a593Smuzhiyun            log += color_text(self.options.color, log_color, actlog) + '\n'
963*4882a593Smuzhiyun
964*4882a593Smuzhiyun        with open(self.dotconfig, 'a') as f:
965*4882a593Smuzhiyun            for (action, value) in results:
966*4882a593Smuzhiyun                if action == ACTION_MOVE:
967*4882a593Smuzhiyun                    f.write(value + '\n')
968*4882a593Smuzhiyun                    updated = True
969*4882a593Smuzhiyun
970*4882a593Smuzhiyun        self.results = results
971*4882a593Smuzhiyun        for f in rm_files:
972*4882a593Smuzhiyun            os.remove(f)
973*4882a593Smuzhiyun
974*4882a593Smuzhiyun        return (updated, suspicious, log)
975*4882a593Smuzhiyun
976*4882a593Smuzhiyun    def check_defconfig(self):
977*4882a593Smuzhiyun        """Check the defconfig after savedefconfig
978*4882a593Smuzhiyun
979*4882a593Smuzhiyun        Returns:
980*4882a593Smuzhiyun          Return additional log if moved CONFIGs were removed again by
981*4882a593Smuzhiyun          'make savedefconfig'.
982*4882a593Smuzhiyun        """
983*4882a593Smuzhiyun
984*4882a593Smuzhiyun        log = ''
985*4882a593Smuzhiyun
986*4882a593Smuzhiyun        with open(self.defconfig) as f:
987*4882a593Smuzhiyun            defconfig_lines = f.readlines()
988*4882a593Smuzhiyun
989*4882a593Smuzhiyun        for (action, value) in self.results:
990*4882a593Smuzhiyun            if action != ACTION_MOVE:
991*4882a593Smuzhiyun                continue
992*4882a593Smuzhiyun            if not value + '\n' in defconfig_lines:
993*4882a593Smuzhiyun                log += color_text(self.options.color, COLOR_YELLOW,
994*4882a593Smuzhiyun                                  "'%s' was removed by savedefconfig.\n" %
995*4882a593Smuzhiyun                                  value)
996*4882a593Smuzhiyun
997*4882a593Smuzhiyun        return log
998*4882a593Smuzhiyun
999*4882a593Smuzhiyun
1000*4882a593Smuzhiyunclass DatabaseThread(threading.Thread):
1001*4882a593Smuzhiyun    """This thread processes results from Slot threads.
1002*4882a593Smuzhiyun
1003*4882a593Smuzhiyun    It collects the data in the master config directary. There is only one
1004*4882a593Smuzhiyun    result thread, and this helps to serialise the build output.
1005*4882a593Smuzhiyun    """
1006*4882a593Smuzhiyun    def __init__(self, config_db, db_queue):
1007*4882a593Smuzhiyun        """Set up a new result thread
1008*4882a593Smuzhiyun
1009*4882a593Smuzhiyun        Args:
1010*4882a593Smuzhiyun            builder: Builder which will be sent each result
1011*4882a593Smuzhiyun        """
1012*4882a593Smuzhiyun        threading.Thread.__init__(self)
1013*4882a593Smuzhiyun        self.config_db = config_db
1014*4882a593Smuzhiyun        self.db_queue= db_queue
1015*4882a593Smuzhiyun
1016*4882a593Smuzhiyun    def run(self):
1017*4882a593Smuzhiyun        """Called to start up the result thread.
1018*4882a593Smuzhiyun
1019*4882a593Smuzhiyun        We collect the next result job and pass it on to the build.
1020*4882a593Smuzhiyun        """
1021*4882a593Smuzhiyun        while True:
1022*4882a593Smuzhiyun            defconfig, configs = self.db_queue.get()
1023*4882a593Smuzhiyun            self.config_db[defconfig] = configs
1024*4882a593Smuzhiyun            self.db_queue.task_done()
1025*4882a593Smuzhiyun
1026*4882a593Smuzhiyun
1027*4882a593Smuzhiyunclass Slot:
1028*4882a593Smuzhiyun
1029*4882a593Smuzhiyun    """A slot to store a subprocess.
1030*4882a593Smuzhiyun
1031*4882a593Smuzhiyun    Each instance of this class handles one subprocess.
1032*4882a593Smuzhiyun    This class is useful to control multiple threads
1033*4882a593Smuzhiyun    for faster processing.
1034*4882a593Smuzhiyun    """
1035*4882a593Smuzhiyun
1036*4882a593Smuzhiyun    def __init__(self, toolchains, configs, options, progress, devnull,
1037*4882a593Smuzhiyun		 make_cmd, reference_src_dir, db_queue):
1038*4882a593Smuzhiyun        """Create a new process slot.
1039*4882a593Smuzhiyun
1040*4882a593Smuzhiyun        Arguments:
1041*4882a593Smuzhiyun          toolchains: Toolchains object containing toolchains.
1042*4882a593Smuzhiyun          configs: A list of CONFIGs to move.
1043*4882a593Smuzhiyun          options: option flags.
1044*4882a593Smuzhiyun          progress: A progress indicator.
1045*4882a593Smuzhiyun          devnull: A file object of '/dev/null'.
1046*4882a593Smuzhiyun          make_cmd: command name of GNU Make.
1047*4882a593Smuzhiyun          reference_src_dir: Determine the true starting config state from this
1048*4882a593Smuzhiyun                             source tree.
1049*4882a593Smuzhiyun          db_queue: output queue to write config info for the database
1050*4882a593Smuzhiyun        """
1051*4882a593Smuzhiyun        self.toolchains = toolchains
1052*4882a593Smuzhiyun        self.options = options
1053*4882a593Smuzhiyun        self.progress = progress
1054*4882a593Smuzhiyun        self.build_dir = tempfile.mkdtemp()
1055*4882a593Smuzhiyun        self.devnull = devnull
1056*4882a593Smuzhiyun        self.make_cmd = (make_cmd, 'O=' + self.build_dir)
1057*4882a593Smuzhiyun        self.reference_src_dir = reference_src_dir
1058*4882a593Smuzhiyun        self.db_queue = db_queue
1059*4882a593Smuzhiyun        self.parser = KconfigParser(configs, options, self.build_dir)
1060*4882a593Smuzhiyun        self.state = STATE_IDLE
1061*4882a593Smuzhiyun        self.failed_boards = set()
1062*4882a593Smuzhiyun        self.suspicious_boards = set()
1063*4882a593Smuzhiyun
1064*4882a593Smuzhiyun    def __del__(self):
1065*4882a593Smuzhiyun        """Delete the working directory
1066*4882a593Smuzhiyun
1067*4882a593Smuzhiyun        This function makes sure the temporary directory is cleaned away
1068*4882a593Smuzhiyun        even if Python suddenly dies due to error.  It should be done in here
1069*4882a593Smuzhiyun        because it is guaranteed the destructor is always invoked when the
1070*4882a593Smuzhiyun        instance of the class gets unreferenced.
1071*4882a593Smuzhiyun
1072*4882a593Smuzhiyun        If the subprocess is still running, wait until it finishes.
1073*4882a593Smuzhiyun        """
1074*4882a593Smuzhiyun        if self.state != STATE_IDLE:
1075*4882a593Smuzhiyun            while self.ps.poll() == None:
1076*4882a593Smuzhiyun                pass
1077*4882a593Smuzhiyun        shutil.rmtree(self.build_dir)
1078*4882a593Smuzhiyun
1079*4882a593Smuzhiyun    def add(self, defconfig):
1080*4882a593Smuzhiyun        """Assign a new subprocess for defconfig and add it to the slot.
1081*4882a593Smuzhiyun
1082*4882a593Smuzhiyun        If the slot is vacant, create a new subprocess for processing the
1083*4882a593Smuzhiyun        given defconfig and add it to the slot.  Just returns False if
1084*4882a593Smuzhiyun        the slot is occupied (i.e. the current subprocess is still running).
1085*4882a593Smuzhiyun
1086*4882a593Smuzhiyun        Arguments:
1087*4882a593Smuzhiyun          defconfig: defconfig name.
1088*4882a593Smuzhiyun
1089*4882a593Smuzhiyun        Returns:
1090*4882a593Smuzhiyun          Return True on success or False on failure
1091*4882a593Smuzhiyun        """
1092*4882a593Smuzhiyun        if self.state != STATE_IDLE:
1093*4882a593Smuzhiyun            return False
1094*4882a593Smuzhiyun
1095*4882a593Smuzhiyun        self.defconfig = defconfig
1096*4882a593Smuzhiyun        self.log = ''
1097*4882a593Smuzhiyun        self.current_src_dir = self.reference_src_dir
1098*4882a593Smuzhiyun        self.do_defconfig()
1099*4882a593Smuzhiyun        return True
1100*4882a593Smuzhiyun
1101*4882a593Smuzhiyun    def poll(self):
1102*4882a593Smuzhiyun        """Check the status of the subprocess and handle it as needed.
1103*4882a593Smuzhiyun
1104*4882a593Smuzhiyun        Returns True if the slot is vacant (i.e. in idle state).
1105*4882a593Smuzhiyun        If the configuration is successfully finished, assign a new
1106*4882a593Smuzhiyun        subprocess to build include/autoconf.mk.
1107*4882a593Smuzhiyun        If include/autoconf.mk is generated, invoke the parser to
1108*4882a593Smuzhiyun        parse the .config and the include/autoconf.mk, moving
1109*4882a593Smuzhiyun        config options to the .config as needed.
1110*4882a593Smuzhiyun        If the .config was updated, run "make savedefconfig" to sync
1111*4882a593Smuzhiyun        it, update the original defconfig, and then set the slot back
1112*4882a593Smuzhiyun        to the idle state.
1113*4882a593Smuzhiyun
1114*4882a593Smuzhiyun        Returns:
1115*4882a593Smuzhiyun          Return True if the subprocess is terminated, False otherwise
1116*4882a593Smuzhiyun        """
1117*4882a593Smuzhiyun        if self.state == STATE_IDLE:
1118*4882a593Smuzhiyun            return True
1119*4882a593Smuzhiyun
1120*4882a593Smuzhiyun        if self.ps.poll() == None:
1121*4882a593Smuzhiyun            return False
1122*4882a593Smuzhiyun
1123*4882a593Smuzhiyun        if self.ps.poll() != 0:
1124*4882a593Smuzhiyun            self.handle_error()
1125*4882a593Smuzhiyun        elif self.state == STATE_DEFCONFIG:
1126*4882a593Smuzhiyun            if self.reference_src_dir and not self.current_src_dir:
1127*4882a593Smuzhiyun                self.do_savedefconfig()
1128*4882a593Smuzhiyun            else:
1129*4882a593Smuzhiyun                self.do_autoconf()
1130*4882a593Smuzhiyun        elif self.state == STATE_AUTOCONF:
1131*4882a593Smuzhiyun            if self.current_src_dir:
1132*4882a593Smuzhiyun                self.current_src_dir = None
1133*4882a593Smuzhiyun                self.do_defconfig()
1134*4882a593Smuzhiyun            elif self.options.build_db:
1135*4882a593Smuzhiyun                self.do_build_db()
1136*4882a593Smuzhiyun            else:
1137*4882a593Smuzhiyun                self.do_savedefconfig()
1138*4882a593Smuzhiyun        elif self.state == STATE_SAVEDEFCONFIG:
1139*4882a593Smuzhiyun            self.update_defconfig()
1140*4882a593Smuzhiyun        else:
1141*4882a593Smuzhiyun            sys.exit("Internal Error. This should not happen.")
1142*4882a593Smuzhiyun
1143*4882a593Smuzhiyun        return True if self.state == STATE_IDLE else False
1144*4882a593Smuzhiyun
1145*4882a593Smuzhiyun    def handle_error(self):
1146*4882a593Smuzhiyun        """Handle error cases."""
1147*4882a593Smuzhiyun
1148*4882a593Smuzhiyun        self.log += color_text(self.options.color, COLOR_LIGHT_RED,
1149*4882a593Smuzhiyun                               "Failed to process.\n")
1150*4882a593Smuzhiyun        if self.options.verbose:
1151*4882a593Smuzhiyun            self.log += color_text(self.options.color, COLOR_LIGHT_CYAN,
1152*4882a593Smuzhiyun                                   self.ps.stderr.read())
1153*4882a593Smuzhiyun        self.finish(False)
1154*4882a593Smuzhiyun
1155*4882a593Smuzhiyun    def do_defconfig(self):
1156*4882a593Smuzhiyun        """Run 'make <board>_defconfig' to create the .config file."""
1157*4882a593Smuzhiyun
1158*4882a593Smuzhiyun        cmd = list(self.make_cmd)
1159*4882a593Smuzhiyun        cmd.append(self.defconfig)
1160*4882a593Smuzhiyun        self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1161*4882a593Smuzhiyun                                   stderr=subprocess.PIPE,
1162*4882a593Smuzhiyun                                   cwd=self.current_src_dir)
1163*4882a593Smuzhiyun        self.state = STATE_DEFCONFIG
1164*4882a593Smuzhiyun
1165*4882a593Smuzhiyun    def do_autoconf(self):
1166*4882a593Smuzhiyun        """Run 'make AUTO_CONF_PATH'."""
1167*4882a593Smuzhiyun
1168*4882a593Smuzhiyun        arch = self.parser.get_arch()
1169*4882a593Smuzhiyun        try:
1170*4882a593Smuzhiyun            toolchain = self.toolchains.Select(arch)
1171*4882a593Smuzhiyun        except ValueError:
1172*4882a593Smuzhiyun            self.log += color_text(self.options.color, COLOR_YELLOW,
1173*4882a593Smuzhiyun                    "Tool chain for '%s' is missing.  Do nothing.\n % arch")
1174*4882a593Smuzhiyun            self.finish(False)
1175*4882a593Smuzhiyun            return
1176*4882a593Smuzhiyun	env = toolchain.MakeEnvironment(False)
1177*4882a593Smuzhiyun
1178*4882a593Smuzhiyun        cmd = list(self.make_cmd)
1179*4882a593Smuzhiyun        cmd.append('KCONFIG_IGNORE_DUPLICATES=1')
1180*4882a593Smuzhiyun        cmd.append(AUTO_CONF_PATH)
1181*4882a593Smuzhiyun        self.ps = subprocess.Popen(cmd, stdout=self.devnull, env=env,
1182*4882a593Smuzhiyun                                   stderr=subprocess.PIPE,
1183*4882a593Smuzhiyun                                   cwd=self.current_src_dir)
1184*4882a593Smuzhiyun        self.state = STATE_AUTOCONF
1185*4882a593Smuzhiyun
1186*4882a593Smuzhiyun    def do_build_db(self):
1187*4882a593Smuzhiyun        """Add the board to the database"""
1188*4882a593Smuzhiyun        configs = {}
1189*4882a593Smuzhiyun        with open(os.path.join(self.build_dir, AUTO_CONF_PATH)) as fd:
1190*4882a593Smuzhiyun            for line in fd.readlines():
1191*4882a593Smuzhiyun                if line.startswith('CONFIG'):
1192*4882a593Smuzhiyun                    config, value = line.split('=', 1)
1193*4882a593Smuzhiyun                    configs[config] = value.rstrip()
1194*4882a593Smuzhiyun        self.db_queue.put([self.defconfig, configs])
1195*4882a593Smuzhiyun        self.finish(True)
1196*4882a593Smuzhiyun
1197*4882a593Smuzhiyun    def do_savedefconfig(self):
1198*4882a593Smuzhiyun        """Update the .config and run 'make savedefconfig'."""
1199*4882a593Smuzhiyun
1200*4882a593Smuzhiyun        (updated, suspicious, log) = self.parser.update_dotconfig()
1201*4882a593Smuzhiyun        if suspicious:
1202*4882a593Smuzhiyun            self.suspicious_boards.add(self.defconfig)
1203*4882a593Smuzhiyun        self.log += log
1204*4882a593Smuzhiyun
1205*4882a593Smuzhiyun        if not self.options.force_sync and not updated:
1206*4882a593Smuzhiyun            self.finish(True)
1207*4882a593Smuzhiyun            return
1208*4882a593Smuzhiyun        if updated:
1209*4882a593Smuzhiyun            self.log += color_text(self.options.color, COLOR_LIGHT_GREEN,
1210*4882a593Smuzhiyun                                   "Syncing by savedefconfig...\n")
1211*4882a593Smuzhiyun        else:
1212*4882a593Smuzhiyun            self.log += "Syncing by savedefconfig (forced by option)...\n"
1213*4882a593Smuzhiyun
1214*4882a593Smuzhiyun        cmd = list(self.make_cmd)
1215*4882a593Smuzhiyun        cmd.append('savedefconfig')
1216*4882a593Smuzhiyun        self.ps = subprocess.Popen(cmd, stdout=self.devnull,
1217*4882a593Smuzhiyun                                   stderr=subprocess.PIPE)
1218*4882a593Smuzhiyun        self.state = STATE_SAVEDEFCONFIG
1219*4882a593Smuzhiyun
1220*4882a593Smuzhiyun    def update_defconfig(self):
1221*4882a593Smuzhiyun        """Update the input defconfig and go back to the idle state."""
1222*4882a593Smuzhiyun
1223*4882a593Smuzhiyun        log = self.parser.check_defconfig()
1224*4882a593Smuzhiyun        if log:
1225*4882a593Smuzhiyun            self.suspicious_boards.add(self.defconfig)
1226*4882a593Smuzhiyun            self.log += log
1227*4882a593Smuzhiyun        orig_defconfig = os.path.join('configs', self.defconfig)
1228*4882a593Smuzhiyun        new_defconfig = os.path.join(self.build_dir, 'defconfig')
1229*4882a593Smuzhiyun        updated = not filecmp.cmp(orig_defconfig, new_defconfig)
1230*4882a593Smuzhiyun
1231*4882a593Smuzhiyun        if updated:
1232*4882a593Smuzhiyun            self.log += color_text(self.options.color, COLOR_LIGHT_BLUE,
1233*4882a593Smuzhiyun                                   "defconfig was updated.\n")
1234*4882a593Smuzhiyun
1235*4882a593Smuzhiyun        if not self.options.dry_run and updated:
1236*4882a593Smuzhiyun            shutil.move(new_defconfig, orig_defconfig)
1237*4882a593Smuzhiyun        self.finish(True)
1238*4882a593Smuzhiyun
1239*4882a593Smuzhiyun    def finish(self, success):
1240*4882a593Smuzhiyun        """Display log along with progress and go to the idle state.
1241*4882a593Smuzhiyun
1242*4882a593Smuzhiyun        Arguments:
1243*4882a593Smuzhiyun          success: Should be True when the defconfig was processed
1244*4882a593Smuzhiyun                   successfully, or False when it fails.
1245*4882a593Smuzhiyun        """
1246*4882a593Smuzhiyun        # output at least 30 characters to hide the "* defconfigs out of *".
1247*4882a593Smuzhiyun        log = self.defconfig.ljust(30) + '\n'
1248*4882a593Smuzhiyun
1249*4882a593Smuzhiyun        log += '\n'.join([ '    ' + s for s in self.log.split('\n') ])
1250*4882a593Smuzhiyun        # Some threads are running in parallel.
1251*4882a593Smuzhiyun        # Print log atomically to not mix up logs from different threads.
1252*4882a593Smuzhiyun        print >> (sys.stdout if success else sys.stderr), log
1253*4882a593Smuzhiyun
1254*4882a593Smuzhiyun        if not success:
1255*4882a593Smuzhiyun            if self.options.exit_on_error:
1256*4882a593Smuzhiyun                sys.exit("Exit on error.")
1257*4882a593Smuzhiyun            # If --exit-on-error flag is not set, skip this board and continue.
1258*4882a593Smuzhiyun            # Record the failed board.
1259*4882a593Smuzhiyun            self.failed_boards.add(self.defconfig)
1260*4882a593Smuzhiyun
1261*4882a593Smuzhiyun        self.progress.inc()
1262*4882a593Smuzhiyun        self.progress.show()
1263*4882a593Smuzhiyun        self.state = STATE_IDLE
1264*4882a593Smuzhiyun
1265*4882a593Smuzhiyun    def get_failed_boards(self):
1266*4882a593Smuzhiyun        """Returns a set of failed boards (defconfigs) in this slot.
1267*4882a593Smuzhiyun        """
1268*4882a593Smuzhiyun        return self.failed_boards
1269*4882a593Smuzhiyun
1270*4882a593Smuzhiyun    def get_suspicious_boards(self):
1271*4882a593Smuzhiyun        """Returns a set of boards (defconfigs) with possible misconversion.
1272*4882a593Smuzhiyun        """
1273*4882a593Smuzhiyun        return self.suspicious_boards - self.failed_boards
1274*4882a593Smuzhiyun
1275*4882a593Smuzhiyunclass Slots:
1276*4882a593Smuzhiyun
1277*4882a593Smuzhiyun    """Controller of the array of subprocess slots."""
1278*4882a593Smuzhiyun
1279*4882a593Smuzhiyun    def __init__(self, toolchains, configs, options, progress,
1280*4882a593Smuzhiyun		 reference_src_dir, db_queue):
1281*4882a593Smuzhiyun        """Create a new slots controller.
1282*4882a593Smuzhiyun
1283*4882a593Smuzhiyun        Arguments:
1284*4882a593Smuzhiyun          toolchains: Toolchains object containing toolchains.
1285*4882a593Smuzhiyun          configs: A list of CONFIGs to move.
1286*4882a593Smuzhiyun          options: option flags.
1287*4882a593Smuzhiyun          progress: A progress indicator.
1288*4882a593Smuzhiyun          reference_src_dir: Determine the true starting config state from this
1289*4882a593Smuzhiyun                             source tree.
1290*4882a593Smuzhiyun          db_queue: output queue to write config info for the database
1291*4882a593Smuzhiyun        """
1292*4882a593Smuzhiyun        self.options = options
1293*4882a593Smuzhiyun        self.slots = []
1294*4882a593Smuzhiyun        devnull = get_devnull()
1295*4882a593Smuzhiyun        make_cmd = get_make_cmd()
1296*4882a593Smuzhiyun        for i in range(options.jobs):
1297*4882a593Smuzhiyun            self.slots.append(Slot(toolchains, configs, options, progress,
1298*4882a593Smuzhiyun				   devnull, make_cmd, reference_src_dir,
1299*4882a593Smuzhiyun				   db_queue))
1300*4882a593Smuzhiyun
1301*4882a593Smuzhiyun    def add(self, defconfig):
1302*4882a593Smuzhiyun        """Add a new subprocess if a vacant slot is found.
1303*4882a593Smuzhiyun
1304*4882a593Smuzhiyun        Arguments:
1305*4882a593Smuzhiyun          defconfig: defconfig name to be put into.
1306*4882a593Smuzhiyun
1307*4882a593Smuzhiyun        Returns:
1308*4882a593Smuzhiyun          Return True on success or False on failure
1309*4882a593Smuzhiyun        """
1310*4882a593Smuzhiyun        for slot in self.slots:
1311*4882a593Smuzhiyun            if slot.add(defconfig):
1312*4882a593Smuzhiyun                return True
1313*4882a593Smuzhiyun        return False
1314*4882a593Smuzhiyun
1315*4882a593Smuzhiyun    def available(self):
1316*4882a593Smuzhiyun        """Check if there is a vacant slot.
1317*4882a593Smuzhiyun
1318*4882a593Smuzhiyun        Returns:
1319*4882a593Smuzhiyun          Return True if at lease one vacant slot is found, False otherwise.
1320*4882a593Smuzhiyun        """
1321*4882a593Smuzhiyun        for slot in self.slots:
1322*4882a593Smuzhiyun            if slot.poll():
1323*4882a593Smuzhiyun                return True
1324*4882a593Smuzhiyun        return False
1325*4882a593Smuzhiyun
1326*4882a593Smuzhiyun    def empty(self):
1327*4882a593Smuzhiyun        """Check if all slots are vacant.
1328*4882a593Smuzhiyun
1329*4882a593Smuzhiyun        Returns:
1330*4882a593Smuzhiyun          Return True if all the slots are vacant, False otherwise.
1331*4882a593Smuzhiyun        """
1332*4882a593Smuzhiyun        ret = True
1333*4882a593Smuzhiyun        for slot in self.slots:
1334*4882a593Smuzhiyun            if not slot.poll():
1335*4882a593Smuzhiyun                ret = False
1336*4882a593Smuzhiyun        return ret
1337*4882a593Smuzhiyun
1338*4882a593Smuzhiyun    def show_failed_boards(self):
1339*4882a593Smuzhiyun        """Display all of the failed boards (defconfigs)."""
1340*4882a593Smuzhiyun        boards = set()
1341*4882a593Smuzhiyun        output_file = 'moveconfig.failed'
1342*4882a593Smuzhiyun
1343*4882a593Smuzhiyun        for slot in self.slots:
1344*4882a593Smuzhiyun            boards |= slot.get_failed_boards()
1345*4882a593Smuzhiyun
1346*4882a593Smuzhiyun        if boards:
1347*4882a593Smuzhiyun            boards = '\n'.join(boards) + '\n'
1348*4882a593Smuzhiyun            msg = "The following boards were not processed due to error:\n"
1349*4882a593Smuzhiyun            msg += boards
1350*4882a593Smuzhiyun            msg += "(the list has been saved in %s)\n" % output_file
1351*4882a593Smuzhiyun            print >> sys.stderr, color_text(self.options.color, COLOR_LIGHT_RED,
1352*4882a593Smuzhiyun                                            msg)
1353*4882a593Smuzhiyun
1354*4882a593Smuzhiyun            with open(output_file, 'w') as f:
1355*4882a593Smuzhiyun                f.write(boards)
1356*4882a593Smuzhiyun
1357*4882a593Smuzhiyun    def show_suspicious_boards(self):
1358*4882a593Smuzhiyun        """Display all boards (defconfigs) with possible misconversion."""
1359*4882a593Smuzhiyun        boards = set()
1360*4882a593Smuzhiyun        output_file = 'moveconfig.suspicious'
1361*4882a593Smuzhiyun
1362*4882a593Smuzhiyun        for slot in self.slots:
1363*4882a593Smuzhiyun            boards |= slot.get_suspicious_boards()
1364*4882a593Smuzhiyun
1365*4882a593Smuzhiyun        if boards:
1366*4882a593Smuzhiyun            boards = '\n'.join(boards) + '\n'
1367*4882a593Smuzhiyun            msg = "The following boards might have been converted incorrectly.\n"
1368*4882a593Smuzhiyun            msg += "It is highly recommended to check them manually:\n"
1369*4882a593Smuzhiyun            msg += boards
1370*4882a593Smuzhiyun            msg += "(the list has been saved in %s)\n" % output_file
1371*4882a593Smuzhiyun            print >> sys.stderr, color_text(self.options.color, COLOR_YELLOW,
1372*4882a593Smuzhiyun                                            msg)
1373*4882a593Smuzhiyun
1374*4882a593Smuzhiyun            with open(output_file, 'w') as f:
1375*4882a593Smuzhiyun                f.write(boards)
1376*4882a593Smuzhiyun
1377*4882a593Smuzhiyunclass ReferenceSource:
1378*4882a593Smuzhiyun
1379*4882a593Smuzhiyun    """Reference source against which original configs should be parsed."""
1380*4882a593Smuzhiyun
1381*4882a593Smuzhiyun    def __init__(self, commit):
1382*4882a593Smuzhiyun        """Create a reference source directory based on a specified commit.
1383*4882a593Smuzhiyun
1384*4882a593Smuzhiyun        Arguments:
1385*4882a593Smuzhiyun          commit: commit to git-clone
1386*4882a593Smuzhiyun        """
1387*4882a593Smuzhiyun        self.src_dir = tempfile.mkdtemp()
1388*4882a593Smuzhiyun        print "Cloning git repo to a separate work directory..."
1389*4882a593Smuzhiyun        subprocess.check_output(['git', 'clone', os.getcwd(), '.'],
1390*4882a593Smuzhiyun                                cwd=self.src_dir)
1391*4882a593Smuzhiyun        print "Checkout '%s' to build the original autoconf.mk." % \
1392*4882a593Smuzhiyun            subprocess.check_output(['git', 'rev-parse', '--short', commit]).strip()
1393*4882a593Smuzhiyun        subprocess.check_output(['git', 'checkout', commit],
1394*4882a593Smuzhiyun                                stderr=subprocess.STDOUT, cwd=self.src_dir)
1395*4882a593Smuzhiyun
1396*4882a593Smuzhiyun    def __del__(self):
1397*4882a593Smuzhiyun        """Delete the reference source directory
1398*4882a593Smuzhiyun
1399*4882a593Smuzhiyun        This function makes sure the temporary directory is cleaned away
1400*4882a593Smuzhiyun        even if Python suddenly dies due to error.  It should be done in here
1401*4882a593Smuzhiyun        because it is guaranteed the destructor is always invoked when the
1402*4882a593Smuzhiyun        instance of the class gets unreferenced.
1403*4882a593Smuzhiyun        """
1404*4882a593Smuzhiyun        shutil.rmtree(self.src_dir)
1405*4882a593Smuzhiyun
1406*4882a593Smuzhiyun    def get_dir(self):
1407*4882a593Smuzhiyun        """Return the absolute path to the reference source directory."""
1408*4882a593Smuzhiyun
1409*4882a593Smuzhiyun        return self.src_dir
1410*4882a593Smuzhiyun
1411*4882a593Smuzhiyundef move_config(toolchains, configs, options, db_queue):
1412*4882a593Smuzhiyun    """Move config options to defconfig files.
1413*4882a593Smuzhiyun
1414*4882a593Smuzhiyun    Arguments:
1415*4882a593Smuzhiyun      configs: A list of CONFIGs to move.
1416*4882a593Smuzhiyun      options: option flags
1417*4882a593Smuzhiyun    """
1418*4882a593Smuzhiyun    if len(configs) == 0:
1419*4882a593Smuzhiyun        if options.force_sync:
1420*4882a593Smuzhiyun            print 'No CONFIG is specified. You are probably syncing defconfigs.',
1421*4882a593Smuzhiyun        elif options.build_db:
1422*4882a593Smuzhiyun            print 'Building %s database' % CONFIG_DATABASE
1423*4882a593Smuzhiyun        else:
1424*4882a593Smuzhiyun            print 'Neither CONFIG nor --force-sync is specified. Nothing will happen.',
1425*4882a593Smuzhiyun    else:
1426*4882a593Smuzhiyun        print 'Move ' + ', '.join(configs),
1427*4882a593Smuzhiyun    print '(jobs: %d)\n' % options.jobs
1428*4882a593Smuzhiyun
1429*4882a593Smuzhiyun    if options.git_ref:
1430*4882a593Smuzhiyun        reference_src = ReferenceSource(options.git_ref)
1431*4882a593Smuzhiyun        reference_src_dir = reference_src.get_dir()
1432*4882a593Smuzhiyun    else:
1433*4882a593Smuzhiyun        reference_src_dir = None
1434*4882a593Smuzhiyun
1435*4882a593Smuzhiyun    if options.defconfigs:
1436*4882a593Smuzhiyun        defconfigs = get_matched_defconfigs(options.defconfigs)
1437*4882a593Smuzhiyun    else:
1438*4882a593Smuzhiyun        defconfigs = get_all_defconfigs()
1439*4882a593Smuzhiyun
1440*4882a593Smuzhiyun    progress = Progress(len(defconfigs))
1441*4882a593Smuzhiyun    slots = Slots(toolchains, configs, options, progress, reference_src_dir,
1442*4882a593Smuzhiyun		  db_queue)
1443*4882a593Smuzhiyun
1444*4882a593Smuzhiyun    # Main loop to process defconfig files:
1445*4882a593Smuzhiyun    #  Add a new subprocess into a vacant slot.
1446*4882a593Smuzhiyun    #  Sleep if there is no available slot.
1447*4882a593Smuzhiyun    for defconfig in defconfigs:
1448*4882a593Smuzhiyun        while not slots.add(defconfig):
1449*4882a593Smuzhiyun            while not slots.available():
1450*4882a593Smuzhiyun                # No available slot: sleep for a while
1451*4882a593Smuzhiyun                time.sleep(SLEEP_TIME)
1452*4882a593Smuzhiyun
1453*4882a593Smuzhiyun    # wait until all the subprocesses finish
1454*4882a593Smuzhiyun    while not slots.empty():
1455*4882a593Smuzhiyun        time.sleep(SLEEP_TIME)
1456*4882a593Smuzhiyun
1457*4882a593Smuzhiyun    print ''
1458*4882a593Smuzhiyun    slots.show_failed_boards()
1459*4882a593Smuzhiyun    slots.show_suspicious_boards()
1460*4882a593Smuzhiyun
1461*4882a593Smuzhiyundef find_kconfig_rules(kconf, config, imply_config):
1462*4882a593Smuzhiyun    """Check whether a config has a 'select' or 'imply' keyword
1463*4882a593Smuzhiyun
1464*4882a593Smuzhiyun    Args:
1465*4882a593Smuzhiyun        kconf: Kconfig.Config object
1466*4882a593Smuzhiyun        config: Name of config to check (without CONFIG_ prefix)
1467*4882a593Smuzhiyun        imply_config: Implying config (without CONFIG_ prefix) which may or
1468*4882a593Smuzhiyun            may not have an 'imply' for 'config')
1469*4882a593Smuzhiyun
1470*4882a593Smuzhiyun    Returns:
1471*4882a593Smuzhiyun        Symbol object for 'config' if found, else None
1472*4882a593Smuzhiyun    """
1473*4882a593Smuzhiyun    sym = kconf.get_symbol(imply_config)
1474*4882a593Smuzhiyun    if sym:
1475*4882a593Smuzhiyun        for sel in sym.get_selected_symbols():
1476*4882a593Smuzhiyun            if sel.get_name() == config:
1477*4882a593Smuzhiyun                return sym
1478*4882a593Smuzhiyun    return None
1479*4882a593Smuzhiyun
1480*4882a593Smuzhiyundef check_imply_rule(kconf, config, imply_config):
1481*4882a593Smuzhiyun    """Check if we can add an 'imply' option
1482*4882a593Smuzhiyun
1483*4882a593Smuzhiyun    This finds imply_config in the Kconfig and looks to see if it is possible
1484*4882a593Smuzhiyun    to add an 'imply' for 'config' to that part of the Kconfig.
1485*4882a593Smuzhiyun
1486*4882a593Smuzhiyun    Args:
1487*4882a593Smuzhiyun        kconf: Kconfig.Config object
1488*4882a593Smuzhiyun        config: Name of config to check (without CONFIG_ prefix)
1489*4882a593Smuzhiyun        imply_config: Implying config (without CONFIG_ prefix) which may or
1490*4882a593Smuzhiyun            may not have an 'imply' for 'config')
1491*4882a593Smuzhiyun
1492*4882a593Smuzhiyun    Returns:
1493*4882a593Smuzhiyun        tuple:
1494*4882a593Smuzhiyun            filename of Kconfig file containing imply_config, or None if none
1495*4882a593Smuzhiyun            line number within the Kconfig file, or 0 if none
1496*4882a593Smuzhiyun            message indicating the result
1497*4882a593Smuzhiyun    """
1498*4882a593Smuzhiyun    sym = kconf.get_symbol(imply_config)
1499*4882a593Smuzhiyun    if not sym:
1500*4882a593Smuzhiyun        return 'cannot find sym'
1501*4882a593Smuzhiyun    locs = sym.get_def_locations()
1502*4882a593Smuzhiyun    if len(locs) != 1:
1503*4882a593Smuzhiyun        return '%d locations' % len(locs)
1504*4882a593Smuzhiyun    fname, linenum = locs[0]
1505*4882a593Smuzhiyun    cwd = os.getcwd()
1506*4882a593Smuzhiyun    if cwd and fname.startswith(cwd):
1507*4882a593Smuzhiyun        fname = fname[len(cwd) + 1:]
1508*4882a593Smuzhiyun    file_line = ' at %s:%d' % (fname, linenum)
1509*4882a593Smuzhiyun    with open(fname) as fd:
1510*4882a593Smuzhiyun        data = fd.read().splitlines()
1511*4882a593Smuzhiyun    if data[linenum - 1] != 'config %s' % imply_config:
1512*4882a593Smuzhiyun        return None, 0, 'bad sym format %s%s' % (data[linenum], file_line)
1513*4882a593Smuzhiyun    return fname, linenum, 'adding%s' % file_line
1514*4882a593Smuzhiyun
1515*4882a593Smuzhiyundef add_imply_rule(config, fname, linenum):
1516*4882a593Smuzhiyun    """Add a new 'imply' option to a Kconfig
1517*4882a593Smuzhiyun
1518*4882a593Smuzhiyun    Args:
1519*4882a593Smuzhiyun        config: config option to add an imply for (without CONFIG_ prefix)
1520*4882a593Smuzhiyun        fname: Kconfig filename to update
1521*4882a593Smuzhiyun        linenum: Line number to place the 'imply' before
1522*4882a593Smuzhiyun
1523*4882a593Smuzhiyun    Returns:
1524*4882a593Smuzhiyun        Message indicating the result
1525*4882a593Smuzhiyun    """
1526*4882a593Smuzhiyun    file_line = ' at %s:%d' % (fname, linenum)
1527*4882a593Smuzhiyun    data = open(fname).read().splitlines()
1528*4882a593Smuzhiyun    linenum -= 1
1529*4882a593Smuzhiyun
1530*4882a593Smuzhiyun    for offset, line in enumerate(data[linenum:]):
1531*4882a593Smuzhiyun        if line.strip().startswith('help') or not line:
1532*4882a593Smuzhiyun            data.insert(linenum + offset, '\timply %s' % config)
1533*4882a593Smuzhiyun            with open(fname, 'w') as fd:
1534*4882a593Smuzhiyun                fd.write('\n'.join(data) + '\n')
1535*4882a593Smuzhiyun            return 'added%s' % file_line
1536*4882a593Smuzhiyun
1537*4882a593Smuzhiyun    return 'could not insert%s'
1538*4882a593Smuzhiyun
1539*4882a593Smuzhiyun(IMPLY_MIN_2, IMPLY_TARGET, IMPLY_CMD, IMPLY_NON_ARCH_BOARD) = (
1540*4882a593Smuzhiyun    1, 2, 4, 8)
1541*4882a593Smuzhiyun
1542*4882a593SmuzhiyunIMPLY_FLAGS = {
1543*4882a593Smuzhiyun    'min2': [IMPLY_MIN_2, 'Show options which imply >2 boards (normally >5)'],
1544*4882a593Smuzhiyun    'target': [IMPLY_TARGET, 'Allow CONFIG_TARGET_... options to imply'],
1545*4882a593Smuzhiyun    'cmd': [IMPLY_CMD, 'Allow CONFIG_CMD_... to imply'],
1546*4882a593Smuzhiyun    'non-arch-board': [
1547*4882a593Smuzhiyun        IMPLY_NON_ARCH_BOARD,
1548*4882a593Smuzhiyun        'Allow Kconfig options outside arch/ and /board/ to imply'],
1549*4882a593Smuzhiyun};
1550*4882a593Smuzhiyun
1551*4882a593Smuzhiyundef do_imply_config(config_list, add_imply, imply_flags, skip_added,
1552*4882a593Smuzhiyun                    check_kconfig=True, find_superset=False):
1553*4882a593Smuzhiyun    """Find CONFIG options which imply those in the list
1554*4882a593Smuzhiyun
1555*4882a593Smuzhiyun    Some CONFIG options can be implied by others and this can help to reduce
1556*4882a593Smuzhiyun    the size of the defconfig files. For example, CONFIG_X86 implies
1557*4882a593Smuzhiyun    CONFIG_CMD_IRQ, so we can put 'imply CMD_IRQ' under 'config X86' and
1558*4882a593Smuzhiyun    all x86 boards will have that option, avoiding adding CONFIG_CMD_IRQ to
1559*4882a593Smuzhiyun    each of the x86 defconfig files.
1560*4882a593Smuzhiyun
1561*4882a593Smuzhiyun    This function uses the moveconfig database to find such options. It
1562*4882a593Smuzhiyun    displays a list of things that could possibly imply those in the list.
1563*4882a593Smuzhiyun    The algorithm ignores any that start with CONFIG_TARGET since these
1564*4882a593Smuzhiyun    typically refer to only a few defconfigs (often one). It also does not
1565*4882a593Smuzhiyun    display a config with less than 5 defconfigs.
1566*4882a593Smuzhiyun
1567*4882a593Smuzhiyun    The algorithm works using sets. For each target config in config_list:
1568*4882a593Smuzhiyun        - Get the set 'defconfigs' which use that target config
1569*4882a593Smuzhiyun        - For each config (from a list of all configs):
1570*4882a593Smuzhiyun            - Get the set 'imply_defconfig' of defconfigs which use that config
1571*4882a593Smuzhiyun            -
1572*4882a593Smuzhiyun            - If imply_defconfigs contains anything not in defconfigs then
1573*4882a593Smuzhiyun              this config does not imply the target config
1574*4882a593Smuzhiyun
1575*4882a593Smuzhiyun    Params:
1576*4882a593Smuzhiyun        config_list: List of CONFIG options to check (each a string)
1577*4882a593Smuzhiyun        add_imply: Automatically add an 'imply' for each config.
1578*4882a593Smuzhiyun        imply_flags: Flags which control which implying configs are allowed
1579*4882a593Smuzhiyun           (IMPLY_...)
1580*4882a593Smuzhiyun        skip_added: Don't show options which already have an imply added.
1581*4882a593Smuzhiyun        check_kconfig: Check if implied symbols already have an 'imply' or
1582*4882a593Smuzhiyun            'select' for the target config, and show this information if so.
1583*4882a593Smuzhiyun        find_superset: True to look for configs which are a superset of those
1584*4882a593Smuzhiyun            already found. So for example if CONFIG_EXYNOS5 implies an option,
1585*4882a593Smuzhiyun            but CONFIG_EXYNOS covers a larger set of defconfigs and also
1586*4882a593Smuzhiyun            implies that option, this will drop the former in favour of the
1587*4882a593Smuzhiyun            latter. In practice this option has not proved very used.
1588*4882a593Smuzhiyun
1589*4882a593Smuzhiyun    Note the terminoloy:
1590*4882a593Smuzhiyun        config - a CONFIG_XXX options (a string, e.g. 'CONFIG_CMD_EEPROM')
1591*4882a593Smuzhiyun        defconfig - a defconfig file (a string, e.g. 'configs/snow_defconfig')
1592*4882a593Smuzhiyun    """
1593*4882a593Smuzhiyun    kconf = KconfigScanner().conf if check_kconfig else None
1594*4882a593Smuzhiyun    if add_imply and add_imply != 'all':
1595*4882a593Smuzhiyun        add_imply = add_imply.split()
1596*4882a593Smuzhiyun
1597*4882a593Smuzhiyun    # key is defconfig name, value is dict of (CONFIG_xxx, value)
1598*4882a593Smuzhiyun    config_db = {}
1599*4882a593Smuzhiyun
1600*4882a593Smuzhiyun    # Holds a dict containing the set of defconfigs that contain each config
1601*4882a593Smuzhiyun    # key is config, value is set of defconfigs using that config
1602*4882a593Smuzhiyun    defconfig_db = collections.defaultdict(set)
1603*4882a593Smuzhiyun
1604*4882a593Smuzhiyun    # Set of all config options we have seen
1605*4882a593Smuzhiyun    all_configs = set()
1606*4882a593Smuzhiyun
1607*4882a593Smuzhiyun    # Set of all defconfigs we have seen
1608*4882a593Smuzhiyun    all_defconfigs = set()
1609*4882a593Smuzhiyun
1610*4882a593Smuzhiyun    # Read in the database
1611*4882a593Smuzhiyun    configs = {}
1612*4882a593Smuzhiyun    with open(CONFIG_DATABASE) as fd:
1613*4882a593Smuzhiyun        for line in fd.readlines():
1614*4882a593Smuzhiyun            line = line.rstrip()
1615*4882a593Smuzhiyun            if not line:  # Separator between defconfigs
1616*4882a593Smuzhiyun                config_db[defconfig] = configs
1617*4882a593Smuzhiyun                all_defconfigs.add(defconfig)
1618*4882a593Smuzhiyun                configs = {}
1619*4882a593Smuzhiyun            elif line[0] == ' ':  # CONFIG line
1620*4882a593Smuzhiyun                config, value = line.strip().split('=', 1)
1621*4882a593Smuzhiyun                configs[config] = value
1622*4882a593Smuzhiyun                defconfig_db[config].add(defconfig)
1623*4882a593Smuzhiyun                all_configs.add(config)
1624*4882a593Smuzhiyun            else:  # New defconfig
1625*4882a593Smuzhiyun                defconfig = line
1626*4882a593Smuzhiyun
1627*4882a593Smuzhiyun    # Work through each target config option in tern, independently
1628*4882a593Smuzhiyun    for config in config_list:
1629*4882a593Smuzhiyun        defconfigs = defconfig_db.get(config)
1630*4882a593Smuzhiyun        if not defconfigs:
1631*4882a593Smuzhiyun            print '%s not found in any defconfig' % config
1632*4882a593Smuzhiyun            continue
1633*4882a593Smuzhiyun
1634*4882a593Smuzhiyun        # Get the set of defconfigs without this one (since a config cannot
1635*4882a593Smuzhiyun        # imply itself)
1636*4882a593Smuzhiyun        non_defconfigs = all_defconfigs - defconfigs
1637*4882a593Smuzhiyun        num_defconfigs = len(defconfigs)
1638*4882a593Smuzhiyun        print '%s found in %d/%d defconfigs' % (config, num_defconfigs,
1639*4882a593Smuzhiyun                                                len(all_configs))
1640*4882a593Smuzhiyun
1641*4882a593Smuzhiyun        # This will hold the results: key=config, value=defconfigs containing it
1642*4882a593Smuzhiyun        imply_configs = {}
1643*4882a593Smuzhiyun        rest_configs = all_configs - set([config])
1644*4882a593Smuzhiyun
1645*4882a593Smuzhiyun        # Look at every possible config, except the target one
1646*4882a593Smuzhiyun        for imply_config in rest_configs:
1647*4882a593Smuzhiyun            if 'ERRATUM' in imply_config:
1648*4882a593Smuzhiyun                continue
1649*4882a593Smuzhiyun            if not (imply_flags & IMPLY_CMD):
1650*4882a593Smuzhiyun                if 'CONFIG_CMD' in imply_config:
1651*4882a593Smuzhiyun                    continue
1652*4882a593Smuzhiyun            if not (imply_flags & IMPLY_TARGET):
1653*4882a593Smuzhiyun                if 'CONFIG_TARGET' in imply_config:
1654*4882a593Smuzhiyun                    continue
1655*4882a593Smuzhiyun
1656*4882a593Smuzhiyun            # Find set of defconfigs that have this config
1657*4882a593Smuzhiyun            imply_defconfig = defconfig_db[imply_config]
1658*4882a593Smuzhiyun
1659*4882a593Smuzhiyun            # Get the intersection of this with defconfigs containing the
1660*4882a593Smuzhiyun            # target config
1661*4882a593Smuzhiyun            common_defconfigs = imply_defconfig & defconfigs
1662*4882a593Smuzhiyun
1663*4882a593Smuzhiyun            # Get the set of defconfigs containing this config which DO NOT
1664*4882a593Smuzhiyun            # also contain the taret config. If this set is non-empty it means
1665*4882a593Smuzhiyun            # that this config affects other defconfigs as well as (possibly)
1666*4882a593Smuzhiyun            # the ones affected by the target config. This means it implies
1667*4882a593Smuzhiyun            # things we don't want to imply.
1668*4882a593Smuzhiyun            not_common_defconfigs = imply_defconfig & non_defconfigs
1669*4882a593Smuzhiyun            if not_common_defconfigs:
1670*4882a593Smuzhiyun                continue
1671*4882a593Smuzhiyun
1672*4882a593Smuzhiyun            # If there are common defconfigs, imply_config may be useful
1673*4882a593Smuzhiyun            if common_defconfigs:
1674*4882a593Smuzhiyun                skip = False
1675*4882a593Smuzhiyun                if find_superset:
1676*4882a593Smuzhiyun                    for prev in imply_configs.keys():
1677*4882a593Smuzhiyun                        prev_count = len(imply_configs[prev])
1678*4882a593Smuzhiyun                        count = len(common_defconfigs)
1679*4882a593Smuzhiyun                        if (prev_count > count and
1680*4882a593Smuzhiyun                            (imply_configs[prev] & common_defconfigs ==
1681*4882a593Smuzhiyun                            common_defconfigs)):
1682*4882a593Smuzhiyun                            # skip imply_config because prev is a superset
1683*4882a593Smuzhiyun                            skip = True
1684*4882a593Smuzhiyun                            break
1685*4882a593Smuzhiyun                        elif count > prev_count:
1686*4882a593Smuzhiyun                            # delete prev because imply_config is a superset
1687*4882a593Smuzhiyun                            del imply_configs[prev]
1688*4882a593Smuzhiyun                if not skip:
1689*4882a593Smuzhiyun                    imply_configs[imply_config] = common_defconfigs
1690*4882a593Smuzhiyun
1691*4882a593Smuzhiyun        # Now we have a dict imply_configs of configs which imply each config
1692*4882a593Smuzhiyun        # The value of each dict item is the set of defconfigs containing that
1693*4882a593Smuzhiyun        # config. Rank them so that we print the configs that imply the largest
1694*4882a593Smuzhiyun        # number of defconfigs first.
1695*4882a593Smuzhiyun        ranked_iconfigs = sorted(imply_configs,
1696*4882a593Smuzhiyun                            key=lambda k: len(imply_configs[k]), reverse=True)
1697*4882a593Smuzhiyun        kconfig_info = ''
1698*4882a593Smuzhiyun        cwd = os.getcwd()
1699*4882a593Smuzhiyun        add_list = collections.defaultdict(list)
1700*4882a593Smuzhiyun        for iconfig in ranked_iconfigs:
1701*4882a593Smuzhiyun            num_common = len(imply_configs[iconfig])
1702*4882a593Smuzhiyun
1703*4882a593Smuzhiyun            # Don't bother if there are less than 5 defconfigs affected.
1704*4882a593Smuzhiyun            if num_common < (2 if imply_flags & IMPLY_MIN_2 else 5):
1705*4882a593Smuzhiyun                continue
1706*4882a593Smuzhiyun            missing = defconfigs - imply_configs[iconfig]
1707*4882a593Smuzhiyun            missing_str = ', '.join(missing) if missing else 'all'
1708*4882a593Smuzhiyun            missing_str = ''
1709*4882a593Smuzhiyun            show = True
1710*4882a593Smuzhiyun            if kconf:
1711*4882a593Smuzhiyun                sym = find_kconfig_rules(kconf, config[CONFIG_LEN:],
1712*4882a593Smuzhiyun                                         iconfig[CONFIG_LEN:])
1713*4882a593Smuzhiyun                kconfig_info = ''
1714*4882a593Smuzhiyun                if sym:
1715*4882a593Smuzhiyun                    locs = sym.get_def_locations()
1716*4882a593Smuzhiyun                    if len(locs) == 1:
1717*4882a593Smuzhiyun                        fname, linenum = locs[0]
1718*4882a593Smuzhiyun                        if cwd and fname.startswith(cwd):
1719*4882a593Smuzhiyun                            fname = fname[len(cwd) + 1:]
1720*4882a593Smuzhiyun                        kconfig_info = '%s:%d' % (fname, linenum)
1721*4882a593Smuzhiyun                        if skip_added:
1722*4882a593Smuzhiyun                            show = False
1723*4882a593Smuzhiyun                else:
1724*4882a593Smuzhiyun                    sym = kconf.get_symbol(iconfig[CONFIG_LEN:])
1725*4882a593Smuzhiyun                    fname = ''
1726*4882a593Smuzhiyun                    if sym:
1727*4882a593Smuzhiyun                        locs = sym.get_def_locations()
1728*4882a593Smuzhiyun                        if len(locs) == 1:
1729*4882a593Smuzhiyun                            fname, linenum = locs[0]
1730*4882a593Smuzhiyun                            if cwd and fname.startswith(cwd):
1731*4882a593Smuzhiyun                                fname = fname[len(cwd) + 1:]
1732*4882a593Smuzhiyun                    in_arch_board = not sym or (fname.startswith('arch') or
1733*4882a593Smuzhiyun                                                fname.startswith('board'))
1734*4882a593Smuzhiyun                    if (not in_arch_board and
1735*4882a593Smuzhiyun                        not (imply_flags & IMPLY_NON_ARCH_BOARD)):
1736*4882a593Smuzhiyun                        continue
1737*4882a593Smuzhiyun
1738*4882a593Smuzhiyun                    if add_imply and (add_imply == 'all' or
1739*4882a593Smuzhiyun                                      iconfig in add_imply):
1740*4882a593Smuzhiyun                        fname, linenum, kconfig_info = (check_imply_rule(kconf,
1741*4882a593Smuzhiyun                                config[CONFIG_LEN:], iconfig[CONFIG_LEN:]))
1742*4882a593Smuzhiyun                        if fname:
1743*4882a593Smuzhiyun                            add_list[fname].append(linenum)
1744*4882a593Smuzhiyun
1745*4882a593Smuzhiyun            if show and kconfig_info != 'skip':
1746*4882a593Smuzhiyun                print '%5d : %-30s%-25s %s' % (num_common, iconfig.ljust(30),
1747*4882a593Smuzhiyun                                              kconfig_info, missing_str)
1748*4882a593Smuzhiyun
1749*4882a593Smuzhiyun        # Having collected a list of things to add, now we add them. We process
1750*4882a593Smuzhiyun        # each file from the largest line number to the smallest so that
1751*4882a593Smuzhiyun        # earlier additions do not affect our line numbers. E.g. if we added an
1752*4882a593Smuzhiyun        # imply at line 20 it would change the position of each line after
1753*4882a593Smuzhiyun        # that.
1754*4882a593Smuzhiyun        for fname, linenums in add_list.iteritems():
1755*4882a593Smuzhiyun            for linenum in sorted(linenums, reverse=True):
1756*4882a593Smuzhiyun                add_imply_rule(config[CONFIG_LEN:], fname, linenum)
1757*4882a593Smuzhiyun
1758*4882a593Smuzhiyun
1759*4882a593Smuzhiyundef main():
1760*4882a593Smuzhiyun    try:
1761*4882a593Smuzhiyun        cpu_count = multiprocessing.cpu_count()
1762*4882a593Smuzhiyun    except NotImplementedError:
1763*4882a593Smuzhiyun        cpu_count = 1
1764*4882a593Smuzhiyun
1765*4882a593Smuzhiyun    parser = optparse.OptionParser()
1766*4882a593Smuzhiyun    # Add options here
1767*4882a593Smuzhiyun    parser.add_option('-a', '--add-imply', type='string', default='',
1768*4882a593Smuzhiyun                      help='comma-separated list of CONFIG options to add '
1769*4882a593Smuzhiyun                      "an 'imply' statement to for the CONFIG in -i")
1770*4882a593Smuzhiyun    parser.add_option('-A', '--skip-added', action='store_true', default=False,
1771*4882a593Smuzhiyun                      help="don't show options which are already marked as "
1772*4882a593Smuzhiyun                      'implying others')
1773*4882a593Smuzhiyun    parser.add_option('-b', '--build-db', action='store_true', default=False,
1774*4882a593Smuzhiyun                      help='build a CONFIG database')
1775*4882a593Smuzhiyun    parser.add_option('-c', '--color', action='store_true', default=False,
1776*4882a593Smuzhiyun                      help='display the log in color')
1777*4882a593Smuzhiyun    parser.add_option('-C', '--commit', action='store_true', default=False,
1778*4882a593Smuzhiyun                      help='Create a git commit for the operation')
1779*4882a593Smuzhiyun    parser.add_option('-d', '--defconfigs', type='string',
1780*4882a593Smuzhiyun                      help='a file containing a list of defconfigs to move, '
1781*4882a593Smuzhiyun                      "one per line (for example 'snow_defconfig') "
1782*4882a593Smuzhiyun                      "or '-' to read from stdin")
1783*4882a593Smuzhiyun    parser.add_option('-i', '--imply', action='store_true', default=False,
1784*4882a593Smuzhiyun                      help='find options which imply others')
1785*4882a593Smuzhiyun    parser.add_option('-I', '--imply-flags', type='string', default='',
1786*4882a593Smuzhiyun                      help="control the -i option ('help' for help")
1787*4882a593Smuzhiyun    parser.add_option('-n', '--dry-run', action='store_true', default=False,
1788*4882a593Smuzhiyun                      help='perform a trial run (show log with no changes)')
1789*4882a593Smuzhiyun    parser.add_option('-e', '--exit-on-error', action='store_true',
1790*4882a593Smuzhiyun                      default=False,
1791*4882a593Smuzhiyun                      help='exit immediately on any error')
1792*4882a593Smuzhiyun    parser.add_option('-s', '--force-sync', action='store_true', default=False,
1793*4882a593Smuzhiyun                      help='force sync by savedefconfig')
1794*4882a593Smuzhiyun    parser.add_option('-S', '--spl', action='store_true', default=False,
1795*4882a593Smuzhiyun                      help='parse config options defined for SPL build')
1796*4882a593Smuzhiyun    parser.add_option('-H', '--headers-only', dest='cleanup_headers_only',
1797*4882a593Smuzhiyun                      action='store_true', default=False,
1798*4882a593Smuzhiyun                      help='only cleanup the headers')
1799*4882a593Smuzhiyun    parser.add_option('-j', '--jobs', type='int', default=cpu_count,
1800*4882a593Smuzhiyun                      help='the number of jobs to run simultaneously')
1801*4882a593Smuzhiyun    parser.add_option('-r', '--git-ref', type='string',
1802*4882a593Smuzhiyun                      help='the git ref to clone for building the autoconf.mk')
1803*4882a593Smuzhiyun    parser.add_option('-y', '--yes', action='store_true', default=False,
1804*4882a593Smuzhiyun                      help="respond 'yes' to any prompts")
1805*4882a593Smuzhiyun    parser.add_option('-v', '--verbose', action='store_true', default=False,
1806*4882a593Smuzhiyun                      help='show any build errors as boards are built')
1807*4882a593Smuzhiyun    parser.usage += ' CONFIG ...'
1808*4882a593Smuzhiyun
1809*4882a593Smuzhiyun    (options, configs) = parser.parse_args()
1810*4882a593Smuzhiyun
1811*4882a593Smuzhiyun    if len(configs) == 0 and not any((options.force_sync, options.build_db,
1812*4882a593Smuzhiyun                                      options.imply)):
1813*4882a593Smuzhiyun        parser.print_usage()
1814*4882a593Smuzhiyun        sys.exit(1)
1815*4882a593Smuzhiyun
1816*4882a593Smuzhiyun    # prefix the option name with CONFIG_ if missing
1817*4882a593Smuzhiyun    configs = [ config if config.startswith('CONFIG_') else 'CONFIG_' + config
1818*4882a593Smuzhiyun                for config in configs ]
1819*4882a593Smuzhiyun
1820*4882a593Smuzhiyun    check_top_directory()
1821*4882a593Smuzhiyun
1822*4882a593Smuzhiyun    if options.imply:
1823*4882a593Smuzhiyun        imply_flags = 0
1824*4882a593Smuzhiyun        if options.imply_flags == 'all':
1825*4882a593Smuzhiyun            imply_flags = -1
1826*4882a593Smuzhiyun
1827*4882a593Smuzhiyun        elif options.imply_flags:
1828*4882a593Smuzhiyun            for flag in options.imply_flags.split(','):
1829*4882a593Smuzhiyun                bad = flag not in IMPLY_FLAGS
1830*4882a593Smuzhiyun                if bad:
1831*4882a593Smuzhiyun                    print "Invalid flag '%s'" % flag
1832*4882a593Smuzhiyun                if flag == 'help' or bad:
1833*4882a593Smuzhiyun                    print "Imply flags: (separate with ',')"
1834*4882a593Smuzhiyun                    for name, info in IMPLY_FLAGS.iteritems():
1835*4882a593Smuzhiyun                        print ' %-15s: %s' % (name, info[1])
1836*4882a593Smuzhiyun                    parser.print_usage()
1837*4882a593Smuzhiyun                    sys.exit(1)
1838*4882a593Smuzhiyun                imply_flags |= IMPLY_FLAGS[flag][0]
1839*4882a593Smuzhiyun
1840*4882a593Smuzhiyun        do_imply_config(configs, options.add_imply, imply_flags,
1841*4882a593Smuzhiyun                        options.skip_added)
1842*4882a593Smuzhiyun        return
1843*4882a593Smuzhiyun
1844*4882a593Smuzhiyun    config_db = {}
1845*4882a593Smuzhiyun    db_queue = Queue.Queue()
1846*4882a593Smuzhiyun    t = DatabaseThread(config_db, db_queue)
1847*4882a593Smuzhiyun    t.setDaemon(True)
1848*4882a593Smuzhiyun    t.start()
1849*4882a593Smuzhiyun
1850*4882a593Smuzhiyun    if not options.cleanup_headers_only:
1851*4882a593Smuzhiyun        check_clean_directory()
1852*4882a593Smuzhiyun	bsettings.Setup('')
1853*4882a593Smuzhiyun        toolchains = toolchain.Toolchains()
1854*4882a593Smuzhiyun        toolchains.GetSettings()
1855*4882a593Smuzhiyun        toolchains.Scan(verbose=False)
1856*4882a593Smuzhiyun        move_config(toolchains, configs, options, db_queue)
1857*4882a593Smuzhiyun        db_queue.join()
1858*4882a593Smuzhiyun
1859*4882a593Smuzhiyun    if configs:
1860*4882a593Smuzhiyun        cleanup_headers(configs, options)
1861*4882a593Smuzhiyun        cleanup_extra_options(configs, options)
1862*4882a593Smuzhiyun        cleanup_whitelist(configs, options)
1863*4882a593Smuzhiyun        cleanup_readme(configs, options)
1864*4882a593Smuzhiyun
1865*4882a593Smuzhiyun    if options.commit:
1866*4882a593Smuzhiyun        subprocess.call(['git', 'add', '-u'])
1867*4882a593Smuzhiyun        if configs:
1868*4882a593Smuzhiyun            msg = 'Convert %s %sto Kconfig' % (configs[0],
1869*4882a593Smuzhiyun                    'et al ' if len(configs) > 1 else '')
1870*4882a593Smuzhiyun            msg += ('\n\nThis converts the following to Kconfig:\n   %s\n' %
1871*4882a593Smuzhiyun                    '\n   '.join(configs))
1872*4882a593Smuzhiyun        else:
1873*4882a593Smuzhiyun            msg = 'configs: Resync with savedefconfig'
1874*4882a593Smuzhiyun            msg += '\n\nRsync all defconfig files using moveconfig.py'
1875*4882a593Smuzhiyun        subprocess.call(['git', 'commit', '-s', '-m', msg])
1876*4882a593Smuzhiyun
1877*4882a593Smuzhiyun    if options.build_db:
1878*4882a593Smuzhiyun        with open(CONFIG_DATABASE, 'w') as fd:
1879*4882a593Smuzhiyun            for defconfig, configs in config_db.iteritems():
1880*4882a593Smuzhiyun                fd.write('%s\n' % defconfig)
1881*4882a593Smuzhiyun                for config in sorted(configs.keys()):
1882*4882a593Smuzhiyun                    fd.write('   %s=%s\n' % (config, configs[config]))
1883*4882a593Smuzhiyun                fd.write('\n')
1884*4882a593Smuzhiyun
1885*4882a593Smuzhiyunif __name__ == '__main__':
1886*4882a593Smuzhiyun    main()
1887