1*d9f75844SAndroid Build Coastguard Worker#!/usr/bin/env vpython3 2*d9f75844SAndroid Build Coastguard Worker 3*d9f75844SAndroid Build Coastguard Worker# Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. 4*d9f75844SAndroid Build Coastguard Worker# 5*d9f75844SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license 6*d9f75844SAndroid Build Coastguard Worker# that can be found in the LICENSE file in the root of the source 7*d9f75844SAndroid Build Coastguard Worker# tree. An additional intellectual property rights grant can be found 8*d9f75844SAndroid Build Coastguard Worker# in the file PATENTS. All contributing project authors may 9*d9f75844SAndroid Build Coastguard Worker# be found in the AUTHORS file in the root of the source tree. 10*d9f75844SAndroid Build Coastguard Worker""" 11*d9f75844SAndroid Build Coastguard WorkerThis tool tries to fix (some) errors reported by `gn gen --check` or 12*d9f75844SAndroid Build Coastguard Worker`gn check`. 13*d9f75844SAndroid Build Coastguard WorkerIt will run `mb gen` in a temporary directory and it is really useful to 14*d9f75844SAndroid Build Coastguard Workercheck for different configurations. 15*d9f75844SAndroid Build Coastguard Worker 16*d9f75844SAndroid Build Coastguard WorkerUsage: 17*d9f75844SAndroid Build Coastguard Worker $ vpython3 tools_webrtc/gn_check_autofix.py -m some_mater -b some_bot 18*d9f75844SAndroid Build Coastguard Worker or 19*d9f75844SAndroid Build Coastguard Worker $ vpython3 tools_webrtc/gn_check_autofix.py -c some_mb_config 20*d9f75844SAndroid Build Coastguard Worker""" 21*d9f75844SAndroid Build Coastguard Worker 22*d9f75844SAndroid Build Coastguard Workerimport os 23*d9f75844SAndroid Build Coastguard Workerimport re 24*d9f75844SAndroid Build Coastguard Workerimport shutil 25*d9f75844SAndroid Build Coastguard Workerimport subprocess 26*d9f75844SAndroid Build Coastguard Workerimport sys 27*d9f75844SAndroid Build Coastguard Workerimport tempfile 28*d9f75844SAndroid Build Coastguard Worker 29*d9f75844SAndroid Build Coastguard Workerfrom collections import defaultdict 30*d9f75844SAndroid Build Coastguard Worker 31*d9f75844SAndroid Build Coastguard WorkerSCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) 32*d9f75844SAndroid Build Coastguard Worker 33*d9f75844SAndroid Build Coastguard WorkerCHROMIUM_DIRS = [ 34*d9f75844SAndroid Build Coastguard Worker 'base', 'build', 'buildtools', 'testing', 'third_party', 'tools' 35*d9f75844SAndroid Build Coastguard Worker] 36*d9f75844SAndroid Build Coastguard Worker 37*d9f75844SAndroid Build Coastguard WorkerTARGET_RE = re.compile( 38*d9f75844SAndroid Build Coastguard Worker r'(?P<indentation_level>\s*)\w*\("(?P<target_name>\w*)"\) {$') 39*d9f75844SAndroid Build Coastguard Worker 40*d9f75844SAndroid Build Coastguard Worker 41*d9f75844SAndroid Build Coastguard Workerclass TemporaryDirectory: 42*d9f75844SAndroid Build Coastguard Worker def __init__(self): 43*d9f75844SAndroid Build Coastguard Worker self._closed = False 44*d9f75844SAndroid Build Coastguard Worker self._name = None 45*d9f75844SAndroid Build Coastguard Worker self._name = tempfile.mkdtemp() 46*d9f75844SAndroid Build Coastguard Worker 47*d9f75844SAndroid Build Coastguard Worker def __enter__(self): 48*d9f75844SAndroid Build Coastguard Worker return self._name 49*d9f75844SAndroid Build Coastguard Worker 50*d9f75844SAndroid Build Coastguard Worker def __exit__(self, exc, value, _tb): 51*d9f75844SAndroid Build Coastguard Worker if self._name and not self._closed: 52*d9f75844SAndroid Build Coastguard Worker shutil.rmtree(self._name) 53*d9f75844SAndroid Build Coastguard Worker self._closed = True 54*d9f75844SAndroid Build Coastguard Worker 55*d9f75844SAndroid Build Coastguard Worker 56*d9f75844SAndroid Build Coastguard Workerdef Run(cmd): 57*d9f75844SAndroid Build Coastguard Worker print('Running:', ' '.join(cmd)) 58*d9f75844SAndroid Build Coastguard Worker sub = subprocess.Popen(cmd, 59*d9f75844SAndroid Build Coastguard Worker stdout=subprocess.PIPE, 60*d9f75844SAndroid Build Coastguard Worker stderr=subprocess.PIPE, 61*d9f75844SAndroid Build Coastguard Worker universal_newlines=True) 62*d9f75844SAndroid Build Coastguard Worker return sub.communicate() 63*d9f75844SAndroid Build Coastguard Worker 64*d9f75844SAndroid Build Coastguard Worker 65*d9f75844SAndroid Build Coastguard Workerdef FixErrors(filename, missing_deps, deleted_sources): 66*d9f75844SAndroid Build Coastguard Worker with open(filename) as f: 67*d9f75844SAndroid Build Coastguard Worker lines = f.readlines() 68*d9f75844SAndroid Build Coastguard Worker 69*d9f75844SAndroid Build Coastguard Worker fixed_file = '' 70*d9f75844SAndroid Build Coastguard Worker indentation_level = None 71*d9f75844SAndroid Build Coastguard Worker for line in lines: 72*d9f75844SAndroid Build Coastguard Worker match = TARGET_RE.match(line) 73*d9f75844SAndroid Build Coastguard Worker if match: 74*d9f75844SAndroid Build Coastguard Worker target = match.group('target_name') 75*d9f75844SAndroid Build Coastguard Worker if target in missing_deps: 76*d9f75844SAndroid Build Coastguard Worker indentation_level = match.group('indentation_level') 77*d9f75844SAndroid Build Coastguard Worker elif indentation_level is not None: 78*d9f75844SAndroid Build Coastguard Worker match = re.match(indentation_level + '}$', line) 79*d9f75844SAndroid Build Coastguard Worker if match: 80*d9f75844SAndroid Build Coastguard Worker line = ('deps = [\n' + ''.join(' "' + dep + '",\n' 81*d9f75844SAndroid Build Coastguard Worker for dep in missing_deps[target]) + 82*d9f75844SAndroid Build Coastguard Worker ']\n') + line 83*d9f75844SAndroid Build Coastguard Worker indentation_level = None 84*d9f75844SAndroid Build Coastguard Worker elif line.strip().startswith('deps = ['): 85*d9f75844SAndroid Build Coastguard Worker joined_deps = ''.join(' "' + dep + '",\n' 86*d9f75844SAndroid Build Coastguard Worker for dep in missing_deps[target]) 87*d9f75844SAndroid Build Coastguard Worker line = line.replace('deps = [', 'deps = [' + joined_deps) 88*d9f75844SAndroid Build Coastguard Worker indentation_level = None 89*d9f75844SAndroid Build Coastguard Worker 90*d9f75844SAndroid Build Coastguard Worker if line.strip() not in deleted_sources: 91*d9f75844SAndroid Build Coastguard Worker fixed_file += line 92*d9f75844SAndroid Build Coastguard Worker 93*d9f75844SAndroid Build Coastguard Worker with open(filename, 'w') as f: 94*d9f75844SAndroid Build Coastguard Worker f.write(fixed_file) 95*d9f75844SAndroid Build Coastguard Worker 96*d9f75844SAndroid Build Coastguard Worker Run(['gn', 'format', filename]) 97*d9f75844SAndroid Build Coastguard Worker 98*d9f75844SAndroid Build Coastguard Worker 99*d9f75844SAndroid Build Coastguard Workerdef FirstNonEmpty(iterable): 100*d9f75844SAndroid Build Coastguard Worker """Return first item which evaluates to True, or fallback to None.""" 101*d9f75844SAndroid Build Coastguard Worker return next((x for x in iterable if x), None) 102*d9f75844SAndroid Build Coastguard Worker 103*d9f75844SAndroid Build Coastguard Worker 104*d9f75844SAndroid Build Coastguard Workerdef Rebase(base_path, dependency_path, dependency): 105*d9f75844SAndroid Build Coastguard Worker """Adapt paths so they work both in stand-alone WebRTC and Chromium tree. 106*d9f75844SAndroid Build Coastguard Worker 107*d9f75844SAndroid Build Coastguard Worker To cope with varying top-level directory (WebRTC VS Chromium), we use: 108*d9f75844SAndroid Build Coastguard Worker * relative paths for WebRTC modules. 109*d9f75844SAndroid Build Coastguard Worker * absolute paths for shared ones. 110*d9f75844SAndroid Build Coastguard Worker E.g. '//common_audio/...' -> '../../common_audio/' 111*d9f75844SAndroid Build Coastguard Worker '//third_party/...' remains as is. 112*d9f75844SAndroid Build Coastguard Worker 113*d9f75844SAndroid Build Coastguard Worker Args: 114*d9f75844SAndroid Build Coastguard Worker base_path: current module path (E.g. '//video') 115*d9f75844SAndroid Build Coastguard Worker dependency_path: path from root (E.g. '//rtc_base/time') 116*d9f75844SAndroid Build Coastguard Worker dependency: target itself (E.g. 'timestamp_extrapolator') 117*d9f75844SAndroid Build Coastguard Worker 118*d9f75844SAndroid Build Coastguard Worker Returns: 119*d9f75844SAndroid Build Coastguard Worker Full target path (E.g. '../rtc_base/time:timestamp_extrapolator'). 120*d9f75844SAndroid Build Coastguard Worker """ 121*d9f75844SAndroid Build Coastguard Worker 122*d9f75844SAndroid Build Coastguard Worker root = FirstNonEmpty(dependency_path.split('/')) 123*d9f75844SAndroid Build Coastguard Worker if root in CHROMIUM_DIRS: 124*d9f75844SAndroid Build Coastguard Worker # Chromium paths must remain absolute. E.g. //third_party//abseil-cpp... 125*d9f75844SAndroid Build Coastguard Worker rebased = dependency_path 126*d9f75844SAndroid Build Coastguard Worker else: 127*d9f75844SAndroid Build Coastguard Worker base_path = base_path.split(os.path.sep) 128*d9f75844SAndroid Build Coastguard Worker dependency_path = dependency_path.split(os.path.sep) 129*d9f75844SAndroid Build Coastguard Worker 130*d9f75844SAndroid Build Coastguard Worker first_difference = None 131*d9f75844SAndroid Build Coastguard Worker shortest_length = min(len(dependency_path), len(base_path)) 132*d9f75844SAndroid Build Coastguard Worker for i in range(shortest_length): 133*d9f75844SAndroid Build Coastguard Worker if dependency_path[i] != base_path[i]: 134*d9f75844SAndroid Build Coastguard Worker first_difference = i 135*d9f75844SAndroid Build Coastguard Worker break 136*d9f75844SAndroid Build Coastguard Worker 137*d9f75844SAndroid Build Coastguard Worker first_difference = first_difference or shortest_length 138*d9f75844SAndroid Build Coastguard Worker base_path = base_path[first_difference:] 139*d9f75844SAndroid Build Coastguard Worker dependency_path = dependency_path[first_difference:] 140*d9f75844SAndroid Build Coastguard Worker rebased = os.path.sep.join((['..'] * len(base_path)) + dependency_path) 141*d9f75844SAndroid Build Coastguard Worker return rebased + ':' + dependency 142*d9f75844SAndroid Build Coastguard Worker 143*d9f75844SAndroid Build Coastguard Worker 144*d9f75844SAndroid Build Coastguard Workerdef main(): 145*d9f75844SAndroid Build Coastguard Worker deleted_sources = set() 146*d9f75844SAndroid Build Coastguard Worker errors_by_file = defaultdict(lambda: defaultdict(set)) 147*d9f75844SAndroid Build Coastguard Worker 148*d9f75844SAndroid Build Coastguard Worker with TemporaryDirectory() as tmp_dir: 149*d9f75844SAndroid Build Coastguard Worker mb_script_path = os.path.join(SCRIPT_DIR, 'mb', 'mb.py') 150*d9f75844SAndroid Build Coastguard Worker mb_config_file_path = os.path.join(SCRIPT_DIR, 'mb', 'mb_config.pyl') 151*d9f75844SAndroid Build Coastguard Worker mb_gen_command = ([ 152*d9f75844SAndroid Build Coastguard Worker mb_script_path, 153*d9f75844SAndroid Build Coastguard Worker 'gen', 154*d9f75844SAndroid Build Coastguard Worker tmp_dir, 155*d9f75844SAndroid Build Coastguard Worker '--config-file', 156*d9f75844SAndroid Build Coastguard Worker mb_config_file_path, 157*d9f75844SAndroid Build Coastguard Worker ] + sys.argv[1:]) 158*d9f75844SAndroid Build Coastguard Worker 159*d9f75844SAndroid Build Coastguard Worker mb_output = Run(mb_gen_command) 160*d9f75844SAndroid Build Coastguard Worker errors = mb_output[0].split('ERROR')[1:] 161*d9f75844SAndroid Build Coastguard Worker 162*d9f75844SAndroid Build Coastguard Worker if mb_output[1]: 163*d9f75844SAndroid Build Coastguard Worker print(mb_output[1]) 164*d9f75844SAndroid Build Coastguard Worker return 1 165*d9f75844SAndroid Build Coastguard Worker 166*d9f75844SAndroid Build Coastguard Worker for error in errors: 167*d9f75844SAndroid Build Coastguard Worker error = error.split('\n') 168*d9f75844SAndroid Build Coastguard Worker target_msg = 'The target:' 169*d9f75844SAndroid Build Coastguard Worker if target_msg not in error: 170*d9f75844SAndroid Build Coastguard Worker target_msg = 'It is not in any dependency of' 171*d9f75844SAndroid Build Coastguard Worker if target_msg not in error: 172*d9f75844SAndroid Build Coastguard Worker print('\n'.join(error)) 173*d9f75844SAndroid Build Coastguard Worker continue 174*d9f75844SAndroid Build Coastguard Worker index = error.index(target_msg) + 1 175*d9f75844SAndroid Build Coastguard Worker path, target = error[index].strip().split(':') 176*d9f75844SAndroid Build Coastguard Worker if error[index + 1] in ('is including a file from the target:', 177*d9f75844SAndroid Build Coastguard Worker 'The include file is in the target(s):'): 178*d9f75844SAndroid Build Coastguard Worker dep = error[index + 2].strip() 179*d9f75844SAndroid Build Coastguard Worker dep_path, dep = dep.split(':') 180*d9f75844SAndroid Build Coastguard Worker dep = Rebase(path, dep_path, dep) 181*d9f75844SAndroid Build Coastguard Worker # Replacing /target:target with /target 182*d9f75844SAndroid Build Coastguard Worker dep = re.sub(r'/(\w+):(\1)$', r'/\1', dep) 183*d9f75844SAndroid Build Coastguard Worker # Replacing target:target with target 184*d9f75844SAndroid Build Coastguard Worker dep = re.sub(r'^(\w+):(\1)$', r'\1', dep) 185*d9f75844SAndroid Build Coastguard Worker path = os.path.join(path[2:], 'BUILD.gn') 186*d9f75844SAndroid Build Coastguard Worker errors_by_file[path][target].add(dep) 187*d9f75844SAndroid Build Coastguard Worker elif error[index + 1] == 'has a source file:': 188*d9f75844SAndroid Build Coastguard Worker deleted_file = '"' + os.path.basename(error[index + 2].strip()) + '",' 189*d9f75844SAndroid Build Coastguard Worker deleted_sources.add(deleted_file) 190*d9f75844SAndroid Build Coastguard Worker else: 191*d9f75844SAndroid Build Coastguard Worker print('\n'.join(error)) 192*d9f75844SAndroid Build Coastguard Worker continue 193*d9f75844SAndroid Build Coastguard Worker 194*d9f75844SAndroid Build Coastguard Worker for path, missing_deps in list(errors_by_file.items()): 195*d9f75844SAndroid Build Coastguard Worker FixErrors(path, missing_deps, deleted_sources) 196*d9f75844SAndroid Build Coastguard Worker 197*d9f75844SAndroid Build Coastguard Worker return 0 198*d9f75844SAndroid Build Coastguard Worker 199*d9f75844SAndroid Build Coastguard Worker 200*d9f75844SAndroid Build Coastguard Workerif __name__ == '__main__': 201*d9f75844SAndroid Build Coastguard Worker sys.exit(main()) 202