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