1*6777b538SAndroid Build Coastguard Worker#!/usr/bin/env vpython3 2*6777b538SAndroid Build Coastguard Worker# Copyright 2019 The Chromium Authors 3*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 5*6777b538SAndroid Build Coastguard Worker 6*6777b538SAndroid Build Coastguard Worker"""Helper script used to manage locale-related files in Chromium. 7*6777b538SAndroid Build Coastguard Worker 8*6777b538SAndroid Build Coastguard WorkerThis script is used to check, and potentially fix, many locale-related files 9*6777b538SAndroid Build Coastguard Workerin your Chromium workspace, such as: 10*6777b538SAndroid Build Coastguard Worker 11*6777b538SAndroid Build Coastguard Worker - GRIT input files (.grd) and the corresponding translations (.xtb). 12*6777b538SAndroid Build Coastguard Worker 13*6777b538SAndroid Build Coastguard Worker - BUILD.gn files listing Android localized resource string resource .xml 14*6777b538SAndroid Build Coastguard Worker generated by GRIT for all supported Chrome locales. These correspond to 15*6777b538SAndroid Build Coastguard Worker <output> elements that use the type="android" attribute. 16*6777b538SAndroid Build Coastguard Worker 17*6777b538SAndroid Build Coastguard WorkerThe --scan-dir <dir> option can be used to check for all files under a specific 18*6777b538SAndroid Build Coastguard Workerdirectory, and the --fix-inplace option can be used to try fixing any file 19*6777b538SAndroid Build Coastguard Workerthat doesn't pass the check. 20*6777b538SAndroid Build Coastguard Worker 21*6777b538SAndroid Build Coastguard WorkerThis can be very handy to avoid tedious and repetitive work when adding new 22*6777b538SAndroid Build Coastguard Workertranslations / locales to the Chrome code base, since this script can update 23*6777b538SAndroid Build Coastguard Workersaid input files for you. 24*6777b538SAndroid Build Coastguard Worker 25*6777b538SAndroid Build Coastguard WorkerImportant note: checks and fix may fail on some input files. For example 26*6777b538SAndroid Build Coastguard Workerremoting/resources/remoting_strings.grd contains an in-line comment element 27*6777b538SAndroid Build Coastguard Workerinside its <outputs> section that breaks the script. The check will fail, and 28*6777b538SAndroid Build Coastguard Workertrying to fix it too, but at least the file will not be modified. 29*6777b538SAndroid Build Coastguard Worker""" 30*6777b538SAndroid Build Coastguard Worker 31*6777b538SAndroid Build Coastguard Worker 32*6777b538SAndroid Build Coastguard Workerimport argparse 33*6777b538SAndroid Build Coastguard Workerimport json 34*6777b538SAndroid Build Coastguard Workerimport os 35*6777b538SAndroid Build Coastguard Workerimport re 36*6777b538SAndroid Build Coastguard Workerimport shutil 37*6777b538SAndroid Build Coastguard Workerimport subprocess 38*6777b538SAndroid Build Coastguard Workerimport sys 39*6777b538SAndroid Build Coastguard Workerimport unittest 40*6777b538SAndroid Build Coastguard Worker 41*6777b538SAndroid Build Coastguard Worker# Assume this script is under build/ 42*6777b538SAndroid Build Coastguard Worker_SCRIPT_DIR = os.path.dirname(__file__) 43*6777b538SAndroid Build Coastguard Worker_SCRIPT_NAME = os.path.join(_SCRIPT_DIR, os.path.basename(__file__)) 44*6777b538SAndroid Build Coastguard Worker_TOP_SRC_DIR = os.path.join(_SCRIPT_DIR, '..') 45*6777b538SAndroid Build Coastguard Worker 46*6777b538SAndroid Build Coastguard Worker# Need to import android/gyp/util/resource_utils.py here. 47*6777b538SAndroid Build Coastguard Workersys.path.insert(0, os.path.join(_SCRIPT_DIR, 'android/gyp')) 48*6777b538SAndroid Build Coastguard Worker 49*6777b538SAndroid Build Coastguard Workerfrom util import build_utils 50*6777b538SAndroid Build Coastguard Workerfrom util import resource_utils 51*6777b538SAndroid Build Coastguard Worker 52*6777b538SAndroid Build Coastguard Worker 53*6777b538SAndroid Build Coastguard Worker# This locale is the default and doesn't have translations. 54*6777b538SAndroid Build Coastguard Worker_DEFAULT_LOCALE = 'en-US' 55*6777b538SAndroid Build Coastguard Worker 56*6777b538SAndroid Build Coastguard Worker# Misc terminal codes to provide human friendly progress output. 57*6777b538SAndroid Build Coastguard Worker_CONSOLE_CODE_MOVE_CURSOR_TO_COLUMN_0 = '\x1b[0G' 58*6777b538SAndroid Build Coastguard Worker_CONSOLE_CODE_ERASE_LINE = '\x1b[K' 59*6777b538SAndroid Build Coastguard Worker_CONSOLE_START_LINE = ( 60*6777b538SAndroid Build Coastguard Worker _CONSOLE_CODE_MOVE_CURSOR_TO_COLUMN_0 + _CONSOLE_CODE_ERASE_LINE) 61*6777b538SAndroid Build Coastguard Worker 62*6777b538SAndroid Build Coastguard Worker########################################################################## 63*6777b538SAndroid Build Coastguard Worker########################################################################## 64*6777b538SAndroid Build Coastguard Worker##### 65*6777b538SAndroid Build Coastguard Worker##### G E N E R I C H E L P E R F U N C T I O N S 66*6777b538SAndroid Build Coastguard Worker##### 67*6777b538SAndroid Build Coastguard Worker########################################################################## 68*6777b538SAndroid Build Coastguard Worker########################################################################## 69*6777b538SAndroid Build Coastguard Worker 70*6777b538SAndroid Build Coastguard Workerdef _FixChromiumLangAttribute(lang): 71*6777b538SAndroid Build Coastguard Worker """Map XML "lang" attribute values to Chromium locale names.""" 72*6777b538SAndroid Build Coastguard Worker _CHROMIUM_LANG_FIXES = { 73*6777b538SAndroid Build Coastguard Worker 'en': 'en-US', # For now, Chromium doesn't have an 'en' locale. 74*6777b538SAndroid Build Coastguard Worker 'iw': 'he', # 'iw' is the obsolete form of ISO 639-1 for Hebrew 75*6777b538SAndroid Build Coastguard Worker 'no': 'nb', # 'no' is used by the Translation Console for Norwegian (nb). 76*6777b538SAndroid Build Coastguard Worker } 77*6777b538SAndroid Build Coastguard Worker return _CHROMIUM_LANG_FIXES.get(lang, lang) 78*6777b538SAndroid Build Coastguard Worker 79*6777b538SAndroid Build Coastguard Worker 80*6777b538SAndroid Build Coastguard Workerdef _FixTranslationConsoleLocaleName(locale): 81*6777b538SAndroid Build Coastguard Worker _FIXES = { 82*6777b538SAndroid Build Coastguard Worker 'nb': 'no', # Norwegian. 83*6777b538SAndroid Build Coastguard Worker 'he': 'iw', # Hebrew 84*6777b538SAndroid Build Coastguard Worker } 85*6777b538SAndroid Build Coastguard Worker return _FIXES.get(locale, locale) 86*6777b538SAndroid Build Coastguard Worker 87*6777b538SAndroid Build Coastguard Worker 88*6777b538SAndroid Build Coastguard Workerdef _CompareLocaleLists(list_a, list_expected, list_name): 89*6777b538SAndroid Build Coastguard Worker """Compare two lists of locale names. Print errors if they differ. 90*6777b538SAndroid Build Coastguard Worker 91*6777b538SAndroid Build Coastguard Worker Args: 92*6777b538SAndroid Build Coastguard Worker list_a: First list of locales. 93*6777b538SAndroid Build Coastguard Worker list_expected: Second list of locales, as expected. 94*6777b538SAndroid Build Coastguard Worker list_name: Name of list printed in error messages. 95*6777b538SAndroid Build Coastguard Worker Returns: 96*6777b538SAndroid Build Coastguard Worker On success, return False. On error, print error messages and return True. 97*6777b538SAndroid Build Coastguard Worker """ 98*6777b538SAndroid Build Coastguard Worker errors = [] 99*6777b538SAndroid Build Coastguard Worker missing_locales = sorted(set(list_a) - set(list_expected)) 100*6777b538SAndroid Build Coastguard Worker if missing_locales: 101*6777b538SAndroid Build Coastguard Worker errors.append('Missing locales: %s' % missing_locales) 102*6777b538SAndroid Build Coastguard Worker 103*6777b538SAndroid Build Coastguard Worker extra_locales = sorted(set(list_expected) - set(list_a)) 104*6777b538SAndroid Build Coastguard Worker if extra_locales: 105*6777b538SAndroid Build Coastguard Worker errors.append('Unexpected locales: %s' % extra_locales) 106*6777b538SAndroid Build Coastguard Worker 107*6777b538SAndroid Build Coastguard Worker if errors: 108*6777b538SAndroid Build Coastguard Worker print('Errors in %s definition:' % list_name) 109*6777b538SAndroid Build Coastguard Worker for error in errors: 110*6777b538SAndroid Build Coastguard Worker print(' %s\n' % error) 111*6777b538SAndroid Build Coastguard Worker return True 112*6777b538SAndroid Build Coastguard Worker 113*6777b538SAndroid Build Coastguard Worker return False 114*6777b538SAndroid Build Coastguard Worker 115*6777b538SAndroid Build Coastguard Worker 116*6777b538SAndroid Build Coastguard Workerdef _BuildIntervalList(input_list, predicate): 117*6777b538SAndroid Build Coastguard Worker """Find ranges of contiguous list items that pass a given predicate. 118*6777b538SAndroid Build Coastguard Worker 119*6777b538SAndroid Build Coastguard Worker Args: 120*6777b538SAndroid Build Coastguard Worker input_list: An input list of items of any type. 121*6777b538SAndroid Build Coastguard Worker predicate: A function that takes a list item and return True if it 122*6777b538SAndroid Build Coastguard Worker passes a given test. 123*6777b538SAndroid Build Coastguard Worker Returns: 124*6777b538SAndroid Build Coastguard Worker A list of (start_pos, end_pos) tuples, where all items in 125*6777b538SAndroid Build Coastguard Worker [start_pos, end_pos) pass the predicate. 126*6777b538SAndroid Build Coastguard Worker """ 127*6777b538SAndroid Build Coastguard Worker result = [] 128*6777b538SAndroid Build Coastguard Worker size = len(input_list) 129*6777b538SAndroid Build Coastguard Worker start = 0 130*6777b538SAndroid Build Coastguard Worker while True: 131*6777b538SAndroid Build Coastguard Worker # Find first item in list that passes the predicate. 132*6777b538SAndroid Build Coastguard Worker while start < size and not predicate(input_list[start]): 133*6777b538SAndroid Build Coastguard Worker start += 1 134*6777b538SAndroid Build Coastguard Worker 135*6777b538SAndroid Build Coastguard Worker if start >= size: 136*6777b538SAndroid Build Coastguard Worker return result 137*6777b538SAndroid Build Coastguard Worker 138*6777b538SAndroid Build Coastguard Worker # Find first item in the rest of the list that does not pass the 139*6777b538SAndroid Build Coastguard Worker # predicate. 140*6777b538SAndroid Build Coastguard Worker end = start + 1 141*6777b538SAndroid Build Coastguard Worker while end < size and predicate(input_list[end]): 142*6777b538SAndroid Build Coastguard Worker end += 1 143*6777b538SAndroid Build Coastguard Worker 144*6777b538SAndroid Build Coastguard Worker result.append((start, end)) 145*6777b538SAndroid Build Coastguard Worker start = end + 1 146*6777b538SAndroid Build Coastguard Worker 147*6777b538SAndroid Build Coastguard Worker 148*6777b538SAndroid Build Coastguard Workerdef _SortListSubRange(input_list, start, end, key_func): 149*6777b538SAndroid Build Coastguard Worker """Sort an input list's sub-range according to a specific key function. 150*6777b538SAndroid Build Coastguard Worker 151*6777b538SAndroid Build Coastguard Worker Args: 152*6777b538SAndroid Build Coastguard Worker input_list: An input list. 153*6777b538SAndroid Build Coastguard Worker start: Sub-range starting position in list. 154*6777b538SAndroid Build Coastguard Worker end: Sub-range limit position in list. 155*6777b538SAndroid Build Coastguard Worker key_func: A function that extracts a sort key from a line. 156*6777b538SAndroid Build Coastguard Worker Returns: 157*6777b538SAndroid Build Coastguard Worker A copy of |input_list|, with all items in [|start|, |end|) sorted 158*6777b538SAndroid Build Coastguard Worker according to |key_func|. 159*6777b538SAndroid Build Coastguard Worker """ 160*6777b538SAndroid Build Coastguard Worker result = input_list[:start] 161*6777b538SAndroid Build Coastguard Worker inputs = [] 162*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 163*6777b538SAndroid Build Coastguard Worker line = input_list[pos] 164*6777b538SAndroid Build Coastguard Worker key = key_func(line) 165*6777b538SAndroid Build Coastguard Worker inputs.append((key, line)) 166*6777b538SAndroid Build Coastguard Worker 167*6777b538SAndroid Build Coastguard Worker for _, line in sorted(inputs): 168*6777b538SAndroid Build Coastguard Worker result.append(line) 169*6777b538SAndroid Build Coastguard Worker 170*6777b538SAndroid Build Coastguard Worker result += input_list[end:] 171*6777b538SAndroid Build Coastguard Worker return result 172*6777b538SAndroid Build Coastguard Worker 173*6777b538SAndroid Build Coastguard Worker 174*6777b538SAndroid Build Coastguard Workerdef _SortElementsRanges(lines, element_predicate, element_key): 175*6777b538SAndroid Build Coastguard Worker """Sort all elements of a given type in a list of lines by a given key. 176*6777b538SAndroid Build Coastguard Worker 177*6777b538SAndroid Build Coastguard Worker Args: 178*6777b538SAndroid Build Coastguard Worker lines: input lines. 179*6777b538SAndroid Build Coastguard Worker element_predicate: predicate function to select elements to sort. 180*6777b538SAndroid Build Coastguard Worker element_key: lambda returning a comparison key for each element that 181*6777b538SAndroid Build Coastguard Worker passes the predicate. 182*6777b538SAndroid Build Coastguard Worker Returns: 183*6777b538SAndroid Build Coastguard Worker A new list of input lines, with lines [start..end) sorted. 184*6777b538SAndroid Build Coastguard Worker """ 185*6777b538SAndroid Build Coastguard Worker intervals = _BuildIntervalList(lines, element_predicate) 186*6777b538SAndroid Build Coastguard Worker for start, end in intervals: 187*6777b538SAndroid Build Coastguard Worker lines = _SortListSubRange(lines, start, end, element_key) 188*6777b538SAndroid Build Coastguard Worker 189*6777b538SAndroid Build Coastguard Worker return lines 190*6777b538SAndroid Build Coastguard Worker 191*6777b538SAndroid Build Coastguard Worker 192*6777b538SAndroid Build Coastguard Workerdef _ProcessFile(input_file, locales, check_func, fix_func): 193*6777b538SAndroid Build Coastguard Worker """Process a given input file, potentially fixing it. 194*6777b538SAndroid Build Coastguard Worker 195*6777b538SAndroid Build Coastguard Worker Args: 196*6777b538SAndroid Build Coastguard Worker input_file: Input file path. 197*6777b538SAndroid Build Coastguard Worker locales: List of Chrome locales to consider / expect. 198*6777b538SAndroid Build Coastguard Worker check_func: A lambda called to check the input file lines with 199*6777b538SAndroid Build Coastguard Worker (input_lines, locales) argument. It must return an list of error 200*6777b538SAndroid Build Coastguard Worker messages, or None on success. 201*6777b538SAndroid Build Coastguard Worker fix_func: None, or a lambda called to fix the input file lines with 202*6777b538SAndroid Build Coastguard Worker (input_lines, locales). It must return the new list of lines for 203*6777b538SAndroid Build Coastguard Worker the input file, and may raise an Exception in case of error. 204*6777b538SAndroid Build Coastguard Worker Returns: 205*6777b538SAndroid Build Coastguard Worker True at the moment. 206*6777b538SAndroid Build Coastguard Worker """ 207*6777b538SAndroid Build Coastguard Worker print('%sProcessing %s...' % (_CONSOLE_START_LINE, input_file), end=' ') 208*6777b538SAndroid Build Coastguard Worker sys.stdout.flush() 209*6777b538SAndroid Build Coastguard Worker with open(input_file) as f: 210*6777b538SAndroid Build Coastguard Worker input_lines = f.readlines() 211*6777b538SAndroid Build Coastguard Worker errors = check_func(input_file, input_lines, locales) 212*6777b538SAndroid Build Coastguard Worker if errors: 213*6777b538SAndroid Build Coastguard Worker print('\n%s%s' % (_CONSOLE_START_LINE, '\n'.join(errors))) 214*6777b538SAndroid Build Coastguard Worker if fix_func: 215*6777b538SAndroid Build Coastguard Worker try: 216*6777b538SAndroid Build Coastguard Worker input_lines = fix_func(input_file, input_lines, locales) 217*6777b538SAndroid Build Coastguard Worker output = ''.join(input_lines) 218*6777b538SAndroid Build Coastguard Worker with open(input_file, 'wt') as f: 219*6777b538SAndroid Build Coastguard Worker f.write(output) 220*6777b538SAndroid Build Coastguard Worker print('Fixed %s.' % input_file) 221*6777b538SAndroid Build Coastguard Worker except Exception as e: # pylint: disable=broad-except 222*6777b538SAndroid Build Coastguard Worker print('Skipped %s: %s' % (input_file, e)) 223*6777b538SAndroid Build Coastguard Worker 224*6777b538SAndroid Build Coastguard Worker return True 225*6777b538SAndroid Build Coastguard Worker 226*6777b538SAndroid Build Coastguard Worker 227*6777b538SAndroid Build Coastguard Workerdef _ScanDirectoriesForFiles(scan_dirs, file_predicate): 228*6777b538SAndroid Build Coastguard Worker """Scan a directory for files that match a given predicate. 229*6777b538SAndroid Build Coastguard Worker 230*6777b538SAndroid Build Coastguard Worker Args: 231*6777b538SAndroid Build Coastguard Worker scan_dir: A list of top-level directories to start scan in. 232*6777b538SAndroid Build Coastguard Worker file_predicate: lambda function which is passed the file's base name 233*6777b538SAndroid Build Coastguard Worker and returns True if its full path, relative to |scan_dir|, should be 234*6777b538SAndroid Build Coastguard Worker passed in the result. 235*6777b538SAndroid Build Coastguard Worker Returns: 236*6777b538SAndroid Build Coastguard Worker A list of file full paths. 237*6777b538SAndroid Build Coastguard Worker """ 238*6777b538SAndroid Build Coastguard Worker result = [] 239*6777b538SAndroid Build Coastguard Worker for src_dir in scan_dirs: 240*6777b538SAndroid Build Coastguard Worker for root, _, files in os.walk(src_dir): 241*6777b538SAndroid Build Coastguard Worker result.extend(os.path.join(root, f) for f in files if file_predicate(f)) 242*6777b538SAndroid Build Coastguard Worker return result 243*6777b538SAndroid Build Coastguard Worker 244*6777b538SAndroid Build Coastguard Worker 245*6777b538SAndroid Build Coastguard Workerdef _WriteFile(file_path, file_data): 246*6777b538SAndroid Build Coastguard Worker """Write |file_data| to |file_path|.""" 247*6777b538SAndroid Build Coastguard Worker with open(file_path, 'w') as f: 248*6777b538SAndroid Build Coastguard Worker f.write(file_data) 249*6777b538SAndroid Build Coastguard Worker 250*6777b538SAndroid Build Coastguard Worker 251*6777b538SAndroid Build Coastguard Workerdef _FindGnExecutable(): 252*6777b538SAndroid Build Coastguard Worker """Locate the real GN executable used by this Chromium checkout. 253*6777b538SAndroid Build Coastguard Worker 254*6777b538SAndroid Build Coastguard Worker This is needed because the depot_tools 'gn' wrapper script will look 255*6777b538SAndroid Build Coastguard Worker for .gclient and other things we really don't need here. 256*6777b538SAndroid Build Coastguard Worker 257*6777b538SAndroid Build Coastguard Worker Returns: 258*6777b538SAndroid Build Coastguard Worker Path of real host GN executable from current Chromium src/ checkout. 259*6777b538SAndroid Build Coastguard Worker """ 260*6777b538SAndroid Build Coastguard Worker # Simply scan buildtools/*/gn and return the first one found so we don't 261*6777b538SAndroid Build Coastguard Worker # have to guess the platform-specific sub-directory name (e.g. 'linux64' 262*6777b538SAndroid Build Coastguard Worker # for 64-bit Linux machines). 263*6777b538SAndroid Build Coastguard Worker buildtools_dir = os.path.join(_TOP_SRC_DIR, 'buildtools') 264*6777b538SAndroid Build Coastguard Worker for subdir in os.listdir(buildtools_dir): 265*6777b538SAndroid Build Coastguard Worker subdir_path = os.path.join(buildtools_dir, subdir) 266*6777b538SAndroid Build Coastguard Worker if not os.path.isdir(subdir_path): 267*6777b538SAndroid Build Coastguard Worker continue 268*6777b538SAndroid Build Coastguard Worker gn_path = os.path.join(subdir_path, 'gn') 269*6777b538SAndroid Build Coastguard Worker if os.path.exists(gn_path): 270*6777b538SAndroid Build Coastguard Worker return gn_path 271*6777b538SAndroid Build Coastguard Worker return None 272*6777b538SAndroid Build Coastguard Worker 273*6777b538SAndroid Build Coastguard Worker 274*6777b538SAndroid Build Coastguard Workerdef _PrettyPrintListAsLines(input_list, available_width, trailing_comma=False): 275*6777b538SAndroid Build Coastguard Worker result = [] 276*6777b538SAndroid Build Coastguard Worker input_str = ', '.join(input_list) 277*6777b538SAndroid Build Coastguard Worker while len(input_str) > available_width: 278*6777b538SAndroid Build Coastguard Worker pos = input_str.rfind(',', 0, available_width) 279*6777b538SAndroid Build Coastguard Worker result.append(input_str[:pos + 1]) 280*6777b538SAndroid Build Coastguard Worker input_str = input_str[pos + 1:].lstrip() 281*6777b538SAndroid Build Coastguard Worker if trailing_comma and input_str: 282*6777b538SAndroid Build Coastguard Worker input_str += ',' 283*6777b538SAndroid Build Coastguard Worker result.append(input_str) 284*6777b538SAndroid Build Coastguard Worker return result 285*6777b538SAndroid Build Coastguard Worker 286*6777b538SAndroid Build Coastguard Worker 287*6777b538SAndroid Build Coastguard Workerclass _PrettyPrintListAsLinesTest(unittest.TestCase): 288*6777b538SAndroid Build Coastguard Worker 289*6777b538SAndroid Build Coastguard Worker def test_empty_list(self): 290*6777b538SAndroid Build Coastguard Worker self.assertListEqual([''], _PrettyPrintListAsLines([], 10)) 291*6777b538SAndroid Build Coastguard Worker 292*6777b538SAndroid Build Coastguard Worker def test_wrapping(self): 293*6777b538SAndroid Build Coastguard Worker input_list = ['foo', 'bar', 'zoo', 'tool'] 294*6777b538SAndroid Build Coastguard Worker self.assertListEqual( 295*6777b538SAndroid Build Coastguard Worker _PrettyPrintListAsLines(input_list, 8), 296*6777b538SAndroid Build Coastguard Worker ['foo,', 'bar,', 'zoo,', 'tool']) 297*6777b538SAndroid Build Coastguard Worker self.assertListEqual( 298*6777b538SAndroid Build Coastguard Worker _PrettyPrintListAsLines(input_list, 12), ['foo, bar,', 'zoo, tool']) 299*6777b538SAndroid Build Coastguard Worker self.assertListEqual( 300*6777b538SAndroid Build Coastguard Worker _PrettyPrintListAsLines(input_list, 79), ['foo, bar, zoo, tool']) 301*6777b538SAndroid Build Coastguard Worker 302*6777b538SAndroid Build Coastguard Worker def test_trailing_comma(self): 303*6777b538SAndroid Build Coastguard Worker input_list = ['foo', 'bar', 'zoo', 'tool'] 304*6777b538SAndroid Build Coastguard Worker self.assertListEqual( 305*6777b538SAndroid Build Coastguard Worker _PrettyPrintListAsLines(input_list, 8, trailing_comma=True), 306*6777b538SAndroid Build Coastguard Worker ['foo,', 'bar,', 'zoo,', 'tool,']) 307*6777b538SAndroid Build Coastguard Worker self.assertListEqual( 308*6777b538SAndroid Build Coastguard Worker _PrettyPrintListAsLines(input_list, 12, trailing_comma=True), 309*6777b538SAndroid Build Coastguard Worker ['foo, bar,', 'zoo, tool,']) 310*6777b538SAndroid Build Coastguard Worker self.assertListEqual( 311*6777b538SAndroid Build Coastguard Worker _PrettyPrintListAsLines(input_list, 79, trailing_comma=True), 312*6777b538SAndroid Build Coastguard Worker ['foo, bar, zoo, tool,']) 313*6777b538SAndroid Build Coastguard Worker 314*6777b538SAndroid Build Coastguard Worker 315*6777b538SAndroid Build Coastguard Worker########################################################################## 316*6777b538SAndroid Build Coastguard Worker########################################################################## 317*6777b538SAndroid Build Coastguard Worker##### 318*6777b538SAndroid Build Coastguard Worker##### L O C A L E S L I S T S 319*6777b538SAndroid Build Coastguard Worker##### 320*6777b538SAndroid Build Coastguard Worker########################################################################## 321*6777b538SAndroid Build Coastguard Worker########################################################################## 322*6777b538SAndroid Build Coastguard Worker 323*6777b538SAndroid Build Coastguard Worker# Various list of locales that will be extracted from build/config/locales.gni 324*6777b538SAndroid Build Coastguard Worker# Do not use these directly, use ChromeLocales(), and IosUnsupportedLocales() 325*6777b538SAndroid Build Coastguard Worker# instead to access these lists. 326*6777b538SAndroid Build Coastguard Worker_INTERNAL_CHROME_LOCALES = [] 327*6777b538SAndroid Build Coastguard Worker_INTERNAL_IOS_UNSUPPORTED_LOCALES = [] 328*6777b538SAndroid Build Coastguard Worker 329*6777b538SAndroid Build Coastguard Worker 330*6777b538SAndroid Build Coastguard Workerdef ChromeLocales(): 331*6777b538SAndroid Build Coastguard Worker """Return the list of all locales supported by Chrome.""" 332*6777b538SAndroid Build Coastguard Worker if not _INTERNAL_CHROME_LOCALES: 333*6777b538SAndroid Build Coastguard Worker _ExtractAllChromeLocalesLists() 334*6777b538SAndroid Build Coastguard Worker return _INTERNAL_CHROME_LOCALES 335*6777b538SAndroid Build Coastguard Worker 336*6777b538SAndroid Build Coastguard Worker 337*6777b538SAndroid Build Coastguard Workerdef IosUnsupportedLocales(): 338*6777b538SAndroid Build Coastguard Worker """Return the list of locales that are unsupported on iOS.""" 339*6777b538SAndroid Build Coastguard Worker if not _INTERNAL_IOS_UNSUPPORTED_LOCALES: 340*6777b538SAndroid Build Coastguard Worker _ExtractAllChromeLocalesLists() 341*6777b538SAndroid Build Coastguard Worker return _INTERNAL_IOS_UNSUPPORTED_LOCALES 342*6777b538SAndroid Build Coastguard Worker 343*6777b538SAndroid Build Coastguard Worker 344*6777b538SAndroid Build Coastguard Workerdef _PrepareTinyGnWorkspace(work_dir, out_subdir_name='out'): 345*6777b538SAndroid Build Coastguard Worker """Populate an empty directory with a tiny set of working GN config files. 346*6777b538SAndroid Build Coastguard Worker 347*6777b538SAndroid Build Coastguard Worker This allows us to run 'gn gen <out> --root <work_dir>' as fast as possible 348*6777b538SAndroid Build Coastguard Worker to generate files containing the locales list. This takes about 300ms on 349*6777b538SAndroid Build Coastguard Worker a decent machine, instead of more than 5 seconds when running the equivalent 350*6777b538SAndroid Build Coastguard Worker commands from a real Chromium workspace, which requires regenerating more 351*6777b538SAndroid Build Coastguard Worker than 23k targets. 352*6777b538SAndroid Build Coastguard Worker 353*6777b538SAndroid Build Coastguard Worker Args: 354*6777b538SAndroid Build Coastguard Worker work_dir: target working directory. 355*6777b538SAndroid Build Coastguard Worker out_subdir_name: Name of output sub-directory. 356*6777b538SAndroid Build Coastguard Worker Returns: 357*6777b538SAndroid Build Coastguard Worker Full path of output directory created inside |work_dir|. 358*6777b538SAndroid Build Coastguard Worker """ 359*6777b538SAndroid Build Coastguard Worker # Create top-level .gn file that must point to the BUILDCONFIG.gn. 360*6777b538SAndroid Build Coastguard Worker _WriteFile(os.path.join(work_dir, '.gn'), 361*6777b538SAndroid Build Coastguard Worker 'buildconfig = "//BUILDCONFIG.gn"\n') 362*6777b538SAndroid Build Coastguard Worker # Create BUILDCONFIG.gn which must set a default toolchain. Also add 363*6777b538SAndroid Build Coastguard Worker # all variables that may be used in locales.gni in a declare_args() block. 364*6777b538SAndroid Build Coastguard Worker _WriteFile( 365*6777b538SAndroid Build Coastguard Worker os.path.join(work_dir, 'BUILDCONFIG.gn'), 366*6777b538SAndroid Build Coastguard Worker r'''set_default_toolchain("toolchain") 367*6777b538SAndroid Build Coastguard Workerdeclare_args () { 368*6777b538SAndroid Build Coastguard Worker is_ios = false 369*6777b538SAndroid Build Coastguard Worker is_android = true 370*6777b538SAndroid Build Coastguard Worker} 371*6777b538SAndroid Build Coastguard Worker''') 372*6777b538SAndroid Build Coastguard Worker 373*6777b538SAndroid Build Coastguard Worker # Create fake toolchain required by BUILDCONFIG.gn. 374*6777b538SAndroid Build Coastguard Worker os.mkdir(os.path.join(work_dir, 'toolchain')) 375*6777b538SAndroid Build Coastguard Worker _WriteFile(os.path.join(work_dir, 'toolchain', 'BUILD.gn'), 376*6777b538SAndroid Build Coastguard Worker r'''toolchain("toolchain") { 377*6777b538SAndroid Build Coastguard Worker tool("stamp") { 378*6777b538SAndroid Build Coastguard Worker command = "touch {{output}}" # Required by action() 379*6777b538SAndroid Build Coastguard Worker } 380*6777b538SAndroid Build Coastguard Worker} 381*6777b538SAndroid Build Coastguard Worker''') 382*6777b538SAndroid Build Coastguard Worker 383*6777b538SAndroid Build Coastguard Worker # Create top-level BUILD.gn, GN requires at least one target to build so do 384*6777b538SAndroid Build Coastguard Worker # that with a fake action which will never be invoked. Also write the locales 385*6777b538SAndroid Build Coastguard Worker # to misc files in the output directory. 386*6777b538SAndroid Build Coastguard Worker _WriteFile( 387*6777b538SAndroid Build Coastguard Worker os.path.join(work_dir, 'BUILD.gn'), r'''import("//locales.gni") 388*6777b538SAndroid Build Coastguard Worker 389*6777b538SAndroid Build Coastguard Workeraction("create_foo") { # fake action to avoid GN complaints. 390*6777b538SAndroid Build Coastguard Worker script = "//build/create_foo.py" 391*6777b538SAndroid Build Coastguard Worker inputs = [] 392*6777b538SAndroid Build Coastguard Worker outputs = [ "$target_out_dir/$target_name" ] 393*6777b538SAndroid Build Coastguard Worker} 394*6777b538SAndroid Build Coastguard Worker 395*6777b538SAndroid Build Coastguard Worker# Write the locales lists to files in the output directory. 396*6777b538SAndroid Build Coastguard Worker_filename = root_build_dir + "/foo" 397*6777b538SAndroid Build Coastguard Workerwrite_file(_filename + ".locales", locales, "json") 398*6777b538SAndroid Build Coastguard Workerwrite_file(_filename + ".ios_unsupported_locales", 399*6777b538SAndroid Build Coastguard Worker ios_unsupported_locales, 400*6777b538SAndroid Build Coastguard Worker "json") 401*6777b538SAndroid Build Coastguard Worker''') 402*6777b538SAndroid Build Coastguard Worker 403*6777b538SAndroid Build Coastguard Worker # Copy build/config/locales.gni to the workspace, as required by BUILD.gn. 404*6777b538SAndroid Build Coastguard Worker shutil.copyfile(os.path.join(_TOP_SRC_DIR, 'build', 'config', 'locales.gni'), 405*6777b538SAndroid Build Coastguard Worker os.path.join(work_dir, 'locales.gni')) 406*6777b538SAndroid Build Coastguard Worker 407*6777b538SAndroid Build Coastguard Worker # Create output directory. 408*6777b538SAndroid Build Coastguard Worker out_path = os.path.join(work_dir, out_subdir_name) 409*6777b538SAndroid Build Coastguard Worker os.mkdir(out_path) 410*6777b538SAndroid Build Coastguard Worker 411*6777b538SAndroid Build Coastguard Worker # And ... we're good. 412*6777b538SAndroid Build Coastguard Worker return out_path 413*6777b538SAndroid Build Coastguard Worker 414*6777b538SAndroid Build Coastguard Worker 415*6777b538SAndroid Build Coastguard Worker# Set this global variable to the path of a given temporary directory 416*6777b538SAndroid Build Coastguard Worker# before calling _ExtractAllChromeLocalesLists() if you want to debug 417*6777b538SAndroid Build Coastguard Worker# the locales list extraction process. 418*6777b538SAndroid Build Coastguard Worker_DEBUG_LOCALES_WORK_DIR = None 419*6777b538SAndroid Build Coastguard Worker 420*6777b538SAndroid Build Coastguard Worker 421*6777b538SAndroid Build Coastguard Workerdef _ReadJsonList(file_path): 422*6777b538SAndroid Build Coastguard Worker """Read a JSON file that must contain a list, and return it.""" 423*6777b538SAndroid Build Coastguard Worker with open(file_path) as f: 424*6777b538SAndroid Build Coastguard Worker data = json.load(f) 425*6777b538SAndroid Build Coastguard Worker assert isinstance(data, list), "JSON file %s is not a list!" % file_path 426*6777b538SAndroid Build Coastguard Worker return [item.encode('utf8') for item in data] 427*6777b538SAndroid Build Coastguard Worker 428*6777b538SAndroid Build Coastguard Worker 429*6777b538SAndroid Build Coastguard Workerdef _ExtractAllChromeLocalesLists(): 430*6777b538SAndroid Build Coastguard Worker with build_utils.TempDir() as tmp_path: 431*6777b538SAndroid Build Coastguard Worker if _DEBUG_LOCALES_WORK_DIR: 432*6777b538SAndroid Build Coastguard Worker tmp_path = _DEBUG_LOCALES_WORK_DIR 433*6777b538SAndroid Build Coastguard Worker build_utils.DeleteDirectory(tmp_path) 434*6777b538SAndroid Build Coastguard Worker build_utils.MakeDirectory(tmp_path) 435*6777b538SAndroid Build Coastguard Worker 436*6777b538SAndroid Build Coastguard Worker out_path = _PrepareTinyGnWorkspace(tmp_path, 'out') 437*6777b538SAndroid Build Coastguard Worker 438*6777b538SAndroid Build Coastguard Worker # NOTE: The file suffixes used here should be kept in sync with 439*6777b538SAndroid Build Coastguard Worker # build/config/locales.gni 440*6777b538SAndroid Build Coastguard Worker gn_executable = _FindGnExecutable() 441*6777b538SAndroid Build Coastguard Worker try: 442*6777b538SAndroid Build Coastguard Worker subprocess.check_output( 443*6777b538SAndroid Build Coastguard Worker [gn_executable, 'gen', out_path, '--root=' + tmp_path]) 444*6777b538SAndroid Build Coastguard Worker except subprocess.CalledProcessError as e: 445*6777b538SAndroid Build Coastguard Worker print(e.output) 446*6777b538SAndroid Build Coastguard Worker raise e 447*6777b538SAndroid Build Coastguard Worker 448*6777b538SAndroid Build Coastguard Worker global _INTERNAL_CHROME_LOCALES 449*6777b538SAndroid Build Coastguard Worker _INTERNAL_CHROME_LOCALES = _ReadJsonList( 450*6777b538SAndroid Build Coastguard Worker os.path.join(out_path, 'foo.locales')) 451*6777b538SAndroid Build Coastguard Worker 452*6777b538SAndroid Build Coastguard Worker global _INTERNAL_IOS_UNSUPPORTED_LOCALES 453*6777b538SAndroid Build Coastguard Worker _INTERNAL_IOS_UNSUPPORTED_LOCALES = _ReadJsonList( 454*6777b538SAndroid Build Coastguard Worker os.path.join(out_path, 'foo.ios_unsupported_locales')) 455*6777b538SAndroid Build Coastguard Worker 456*6777b538SAndroid Build Coastguard Worker 457*6777b538SAndroid Build Coastguard Worker########################################################################## 458*6777b538SAndroid Build Coastguard Worker########################################################################## 459*6777b538SAndroid Build Coastguard Worker##### 460*6777b538SAndroid Build Coastguard Worker##### G R D H E L P E R F U N C T I O N S 461*6777b538SAndroid Build Coastguard Worker##### 462*6777b538SAndroid Build Coastguard Worker########################################################################## 463*6777b538SAndroid Build Coastguard Worker########################################################################## 464*6777b538SAndroid Build Coastguard Worker 465*6777b538SAndroid Build Coastguard Worker# Technical note: 466*6777b538SAndroid Build Coastguard Worker# 467*6777b538SAndroid Build Coastguard Worker# Even though .grd files are XML, an xml parser library is not used in order 468*6777b538SAndroid Build Coastguard Worker# to preserve the original file's structure after modification. ElementTree 469*6777b538SAndroid Build Coastguard Worker# tends to re-order attributes in each element when re-writing an XML 470*6777b538SAndroid Build Coastguard Worker# document tree, which is undesirable here. 471*6777b538SAndroid Build Coastguard Worker# 472*6777b538SAndroid Build Coastguard Worker# Thus simple line-based regular expression matching is used instead. 473*6777b538SAndroid Build Coastguard Worker# 474*6777b538SAndroid Build Coastguard Worker 475*6777b538SAndroid Build Coastguard Worker# Misc regular expressions used to match elements and their attributes. 476*6777b538SAndroid Build Coastguard Worker_RE_OUTPUT_ELEMENT = re.compile(r'<output (.*)\s*/>') 477*6777b538SAndroid Build Coastguard Worker_RE_TRANSLATION_ELEMENT = re.compile(r'<file( | .* )path="(.*\.xtb)".*/>') 478*6777b538SAndroid Build Coastguard Worker_RE_FILENAME_ATTRIBUTE = re.compile(r'filename="([^"]*)"') 479*6777b538SAndroid Build Coastguard Worker_RE_LANG_ATTRIBUTE = re.compile(r'lang="([^"]*)"') 480*6777b538SAndroid Build Coastguard Worker_RE_PATH_ATTRIBUTE = re.compile(r'path="([^"]*)"') 481*6777b538SAndroid Build Coastguard Worker_RE_TYPE_ANDROID_ATTRIBUTE = re.compile(r'type="android"') 482*6777b538SAndroid Build Coastguard Worker 483*6777b538SAndroid Build Coastguard Worker 484*6777b538SAndroid Build Coastguard Worker 485*6777b538SAndroid Build Coastguard Workerdef _IsGritInputFile(input_file): 486*6777b538SAndroid Build Coastguard Worker """Returns True iff this is a GRIT input file.""" 487*6777b538SAndroid Build Coastguard Worker return input_file.endswith('.grd') 488*6777b538SAndroid Build Coastguard Worker 489*6777b538SAndroid Build Coastguard Worker 490*6777b538SAndroid Build Coastguard Workerdef _GetXmlLangAttribute(xml_line): 491*6777b538SAndroid Build Coastguard Worker """Extract the lang attribute value from an XML input line.""" 492*6777b538SAndroid Build Coastguard Worker m = _RE_LANG_ATTRIBUTE.search(xml_line) 493*6777b538SAndroid Build Coastguard Worker if not m: 494*6777b538SAndroid Build Coastguard Worker return None 495*6777b538SAndroid Build Coastguard Worker return m.group(1) 496*6777b538SAndroid Build Coastguard Worker 497*6777b538SAndroid Build Coastguard Worker 498*6777b538SAndroid Build Coastguard Workerclass _GetXmlLangAttributeTest(unittest.TestCase): 499*6777b538SAndroid Build Coastguard Worker TEST_DATA = { 500*6777b538SAndroid Build Coastguard Worker '': None, 501*6777b538SAndroid Build Coastguard Worker 'foo': None, 502*6777b538SAndroid Build Coastguard Worker 'lang=foo': None, 503*6777b538SAndroid Build Coastguard Worker 'lang="foo"': 'foo', 504*6777b538SAndroid Build Coastguard Worker '<something lang="foo bar" />': 'foo bar', 505*6777b538SAndroid Build Coastguard Worker '<file lang="fr-CA" path="path/to/strings_fr-CA.xtb" />': 'fr-CA', 506*6777b538SAndroid Build Coastguard Worker } 507*6777b538SAndroid Build Coastguard Worker 508*6777b538SAndroid Build Coastguard Worker def test_GetXmlLangAttribute(self): 509*6777b538SAndroid Build Coastguard Worker for test_line, expected in self.TEST_DATA.items(): 510*6777b538SAndroid Build Coastguard Worker self.assertEquals(_GetXmlLangAttribute(test_line), expected) 511*6777b538SAndroid Build Coastguard Worker 512*6777b538SAndroid Build Coastguard Worker 513*6777b538SAndroid Build Coastguard Workerdef _SortGrdElementsRanges(grd_lines, element_predicate): 514*6777b538SAndroid Build Coastguard Worker """Sort all .grd elements of a given type by their lang attribute.""" 515*6777b538SAndroid Build Coastguard Worker return _SortElementsRanges(grd_lines, element_predicate, _GetXmlLangAttribute) 516*6777b538SAndroid Build Coastguard Worker 517*6777b538SAndroid Build Coastguard Worker 518*6777b538SAndroid Build Coastguard Workerdef _CheckGrdElementRangeLang(grd_lines, start, end, wanted_locales): 519*6777b538SAndroid Build Coastguard Worker """Check the element 'lang' attributes in specific .grd lines range. 520*6777b538SAndroid Build Coastguard Worker 521*6777b538SAndroid Build Coastguard Worker This really checks the following: 522*6777b538SAndroid Build Coastguard Worker - Each item has a correct 'lang' attribute. 523*6777b538SAndroid Build Coastguard Worker - There are no duplicated lines for the same 'lang' attribute. 524*6777b538SAndroid Build Coastguard Worker - That there are no extra locales that Chromium doesn't want. 525*6777b538SAndroid Build Coastguard Worker - That no wanted locale is missing. 526*6777b538SAndroid Build Coastguard Worker 527*6777b538SAndroid Build Coastguard Worker Args: 528*6777b538SAndroid Build Coastguard Worker grd_lines: Input .grd lines. 529*6777b538SAndroid Build Coastguard Worker start: Sub-range start position in input line list. 530*6777b538SAndroid Build Coastguard Worker end: Sub-range limit position in input line list. 531*6777b538SAndroid Build Coastguard Worker wanted_locales: Set of wanted Chromium locale names. 532*6777b538SAndroid Build Coastguard Worker Returns: 533*6777b538SAndroid Build Coastguard Worker List of error message strings for this input. Empty on success. 534*6777b538SAndroid Build Coastguard Worker """ 535*6777b538SAndroid Build Coastguard Worker errors = [] 536*6777b538SAndroid Build Coastguard Worker locales = set() 537*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 538*6777b538SAndroid Build Coastguard Worker line = grd_lines[pos] 539*6777b538SAndroid Build Coastguard Worker lang = _GetXmlLangAttribute(line) 540*6777b538SAndroid Build Coastguard Worker if not lang: 541*6777b538SAndroid Build Coastguard Worker errors.append('%d: Missing "lang" attribute in <output> element' % pos + 542*6777b538SAndroid Build Coastguard Worker 1) 543*6777b538SAndroid Build Coastguard Worker continue 544*6777b538SAndroid Build Coastguard Worker cr_locale = _FixChromiumLangAttribute(lang) 545*6777b538SAndroid Build Coastguard Worker if cr_locale in locales: 546*6777b538SAndroid Build Coastguard Worker errors.append( 547*6777b538SAndroid Build Coastguard Worker '%d: Redefinition of <output> for "%s" locale' % (pos + 1, lang)) 548*6777b538SAndroid Build Coastguard Worker locales.add(cr_locale) 549*6777b538SAndroid Build Coastguard Worker 550*6777b538SAndroid Build Coastguard Worker extra_locales = locales.difference(wanted_locales) 551*6777b538SAndroid Build Coastguard Worker if extra_locales: 552*6777b538SAndroid Build Coastguard Worker errors.append('%d-%d: Extra locales found: %s' % (start + 1, end + 1, 553*6777b538SAndroid Build Coastguard Worker sorted(extra_locales))) 554*6777b538SAndroid Build Coastguard Worker 555*6777b538SAndroid Build Coastguard Worker missing_locales = wanted_locales.difference(locales) 556*6777b538SAndroid Build Coastguard Worker if missing_locales: 557*6777b538SAndroid Build Coastguard Worker errors.append('%d-%d: Missing locales: %s' % (start + 1, end + 1, 558*6777b538SAndroid Build Coastguard Worker sorted(missing_locales))) 559*6777b538SAndroid Build Coastguard Worker 560*6777b538SAndroid Build Coastguard Worker return errors 561*6777b538SAndroid Build Coastguard Worker 562*6777b538SAndroid Build Coastguard Worker 563*6777b538SAndroid Build Coastguard Worker########################################################################## 564*6777b538SAndroid Build Coastguard Worker########################################################################## 565*6777b538SAndroid Build Coastguard Worker##### 566*6777b538SAndroid Build Coastguard Worker##### G R D A N D R O I D O U T P U T S 567*6777b538SAndroid Build Coastguard Worker##### 568*6777b538SAndroid Build Coastguard Worker########################################################################## 569*6777b538SAndroid Build Coastguard Worker########################################################################## 570*6777b538SAndroid Build Coastguard Worker 571*6777b538SAndroid Build Coastguard Workerdef _IsGrdAndroidOutputLine(line): 572*6777b538SAndroid Build Coastguard Worker """Returns True iff this is an Android-specific <output> line.""" 573*6777b538SAndroid Build Coastguard Worker m = _RE_OUTPUT_ELEMENT.search(line) 574*6777b538SAndroid Build Coastguard Worker if m: 575*6777b538SAndroid Build Coastguard Worker return 'type="android"' in m.group(1) 576*6777b538SAndroid Build Coastguard Worker return False 577*6777b538SAndroid Build Coastguard Worker 578*6777b538SAndroid Build Coastguard Workerassert _IsGrdAndroidOutputLine(' <output type="android"/>') 579*6777b538SAndroid Build Coastguard Worker 580*6777b538SAndroid Build Coastguard Worker# Many of the functions below have unused arguments due to genericity. 581*6777b538SAndroid Build Coastguard Worker# pylint: disable=unused-argument 582*6777b538SAndroid Build Coastguard Worker 583*6777b538SAndroid Build Coastguard Workerdef _CheckGrdElementRangeAndroidOutputFilename(grd_lines, start, end, 584*6777b538SAndroid Build Coastguard Worker wanted_locales): 585*6777b538SAndroid Build Coastguard Worker """Check all <output> elements in specific input .grd lines range. 586*6777b538SAndroid Build Coastguard Worker 587*6777b538SAndroid Build Coastguard Worker This really checks the following: 588*6777b538SAndroid Build Coastguard Worker - Filenames exist for each listed locale. 589*6777b538SAndroid Build Coastguard Worker - Filenames are well-formed. 590*6777b538SAndroid Build Coastguard Worker 591*6777b538SAndroid Build Coastguard Worker Args: 592*6777b538SAndroid Build Coastguard Worker grd_lines: Input .grd lines. 593*6777b538SAndroid Build Coastguard Worker start: Sub-range start position in input line list. 594*6777b538SAndroid Build Coastguard Worker end: Sub-range limit position in input line list. 595*6777b538SAndroid Build Coastguard Worker wanted_locales: Set of wanted Chromium locale names. 596*6777b538SAndroid Build Coastguard Worker Returns: 597*6777b538SAndroid Build Coastguard Worker List of error message strings for this input. Empty on success. 598*6777b538SAndroid Build Coastguard Worker """ 599*6777b538SAndroid Build Coastguard Worker errors = [] 600*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 601*6777b538SAndroid Build Coastguard Worker line = grd_lines[pos] 602*6777b538SAndroid Build Coastguard Worker lang = _GetXmlLangAttribute(line) 603*6777b538SAndroid Build Coastguard Worker if not lang: 604*6777b538SAndroid Build Coastguard Worker continue 605*6777b538SAndroid Build Coastguard Worker cr_locale = _FixChromiumLangAttribute(lang) 606*6777b538SAndroid Build Coastguard Worker 607*6777b538SAndroid Build Coastguard Worker m = _RE_FILENAME_ATTRIBUTE.search(line) 608*6777b538SAndroid Build Coastguard Worker if not m: 609*6777b538SAndroid Build Coastguard Worker errors.append('%d: Missing filename attribute in <output> element' % pos + 610*6777b538SAndroid Build Coastguard Worker 1) 611*6777b538SAndroid Build Coastguard Worker else: 612*6777b538SAndroid Build Coastguard Worker filename = m.group(1) 613*6777b538SAndroid Build Coastguard Worker if not filename.endswith('.xml'): 614*6777b538SAndroid Build Coastguard Worker errors.append( 615*6777b538SAndroid Build Coastguard Worker '%d: Filename should end with ".xml": %s' % (pos + 1, filename)) 616*6777b538SAndroid Build Coastguard Worker 617*6777b538SAndroid Build Coastguard Worker dirname = os.path.basename(os.path.dirname(filename)) 618*6777b538SAndroid Build Coastguard Worker prefix = ('values-%s' % resource_utils.ToAndroidLocaleName(cr_locale) 619*6777b538SAndroid Build Coastguard Worker if cr_locale != _DEFAULT_LOCALE else 'values') 620*6777b538SAndroid Build Coastguard Worker if dirname != prefix: 621*6777b538SAndroid Build Coastguard Worker errors.append( 622*6777b538SAndroid Build Coastguard Worker '%s: Directory name should be %s: %s' % (pos + 1, prefix, filename)) 623*6777b538SAndroid Build Coastguard Worker 624*6777b538SAndroid Build Coastguard Worker return errors 625*6777b538SAndroid Build Coastguard Worker 626*6777b538SAndroid Build Coastguard Worker 627*6777b538SAndroid Build Coastguard Workerdef _CheckGrdAndroidOutputElements(grd_file, grd_lines, wanted_locales): 628*6777b538SAndroid Build Coastguard Worker """Check all <output> elements related to Android. 629*6777b538SAndroid Build Coastguard Worker 630*6777b538SAndroid Build Coastguard Worker Args: 631*6777b538SAndroid Build Coastguard Worker grd_file: Input .grd file path. 632*6777b538SAndroid Build Coastguard Worker grd_lines: List of input .grd lines. 633*6777b538SAndroid Build Coastguard Worker wanted_locales: set of wanted Chromium locale names. 634*6777b538SAndroid Build Coastguard Worker Returns: 635*6777b538SAndroid Build Coastguard Worker List of error message strings. Empty on success. 636*6777b538SAndroid Build Coastguard Worker """ 637*6777b538SAndroid Build Coastguard Worker intervals = _BuildIntervalList(grd_lines, _IsGrdAndroidOutputLine) 638*6777b538SAndroid Build Coastguard Worker errors = [] 639*6777b538SAndroid Build Coastguard Worker for start, end in intervals: 640*6777b538SAndroid Build Coastguard Worker errors += _CheckGrdElementRangeLang(grd_lines, start, end, wanted_locales) 641*6777b538SAndroid Build Coastguard Worker errors += _CheckGrdElementRangeAndroidOutputFilename(grd_lines, start, end, 642*6777b538SAndroid Build Coastguard Worker wanted_locales) 643*6777b538SAndroid Build Coastguard Worker return errors 644*6777b538SAndroid Build Coastguard Worker 645*6777b538SAndroid Build Coastguard Worker 646*6777b538SAndroid Build Coastguard Workerdef _AddMissingLocalesInGrdAndroidOutputs(grd_file, grd_lines, wanted_locales): 647*6777b538SAndroid Build Coastguard Worker """Fix an input .grd line by adding missing Android outputs. 648*6777b538SAndroid Build Coastguard Worker 649*6777b538SAndroid Build Coastguard Worker Args: 650*6777b538SAndroid Build Coastguard Worker grd_file: Input .grd file path. 651*6777b538SAndroid Build Coastguard Worker grd_lines: Input .grd line list. 652*6777b538SAndroid Build Coastguard Worker wanted_locales: set of Chromium locale names. 653*6777b538SAndroid Build Coastguard Worker Returns: 654*6777b538SAndroid Build Coastguard Worker A new list of .grd lines, containing new <output> elements when needed 655*6777b538SAndroid Build Coastguard Worker for locales from |wanted_locales| that were not part of the input. 656*6777b538SAndroid Build Coastguard Worker """ 657*6777b538SAndroid Build Coastguard Worker intervals = _BuildIntervalList(grd_lines, _IsGrdAndroidOutputLine) 658*6777b538SAndroid Build Coastguard Worker for start, end in reversed(intervals): 659*6777b538SAndroid Build Coastguard Worker locales = set() 660*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 661*6777b538SAndroid Build Coastguard Worker lang = _GetXmlLangAttribute(grd_lines[pos]) 662*6777b538SAndroid Build Coastguard Worker locale = _FixChromiumLangAttribute(lang) 663*6777b538SAndroid Build Coastguard Worker locales.add(locale) 664*6777b538SAndroid Build Coastguard Worker 665*6777b538SAndroid Build Coastguard Worker missing_locales = wanted_locales.difference(locales) 666*6777b538SAndroid Build Coastguard Worker if not missing_locales: 667*6777b538SAndroid Build Coastguard Worker continue 668*6777b538SAndroid Build Coastguard Worker 669*6777b538SAndroid Build Coastguard Worker src_locale = 'bg' 670*6777b538SAndroid Build Coastguard Worker src_lang_attribute = 'lang="%s"' % src_locale 671*6777b538SAndroid Build Coastguard Worker src_line = None 672*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 673*6777b538SAndroid Build Coastguard Worker if src_lang_attribute in grd_lines[pos]: 674*6777b538SAndroid Build Coastguard Worker src_line = grd_lines[pos] 675*6777b538SAndroid Build Coastguard Worker break 676*6777b538SAndroid Build Coastguard Worker 677*6777b538SAndroid Build Coastguard Worker if not src_line: 678*6777b538SAndroid Build Coastguard Worker raise Exception( 679*6777b538SAndroid Build Coastguard Worker 'Cannot find <output> element with "%s" lang attribute' % src_locale) 680*6777b538SAndroid Build Coastguard Worker 681*6777b538SAndroid Build Coastguard Worker line_count = end - 1 682*6777b538SAndroid Build Coastguard Worker for locale in missing_locales: 683*6777b538SAndroid Build Coastguard Worker android_locale = resource_utils.ToAndroidLocaleName(locale) 684*6777b538SAndroid Build Coastguard Worker dst_line = src_line.replace( 685*6777b538SAndroid Build Coastguard Worker 'lang="%s"' % src_locale, 'lang="%s"' % locale).replace( 686*6777b538SAndroid Build Coastguard Worker 'values-%s/' % src_locale, 'values-%s/' % android_locale) 687*6777b538SAndroid Build Coastguard Worker grd_lines.insert(line_count, dst_line) 688*6777b538SAndroid Build Coastguard Worker line_count += 1 689*6777b538SAndroid Build Coastguard Worker 690*6777b538SAndroid Build Coastguard Worker # Sort the new <output> elements. 691*6777b538SAndroid Build Coastguard Worker return _SortGrdElementsRanges(grd_lines, _IsGrdAndroidOutputLine) 692*6777b538SAndroid Build Coastguard Worker 693*6777b538SAndroid Build Coastguard Worker 694*6777b538SAndroid Build Coastguard Worker########################################################################## 695*6777b538SAndroid Build Coastguard Worker########################################################################## 696*6777b538SAndroid Build Coastguard Worker##### 697*6777b538SAndroid Build Coastguard Worker##### G R D T R A N S L A T I O N S 698*6777b538SAndroid Build Coastguard Worker##### 699*6777b538SAndroid Build Coastguard Worker########################################################################## 700*6777b538SAndroid Build Coastguard Worker########################################################################## 701*6777b538SAndroid Build Coastguard Worker 702*6777b538SAndroid Build Coastguard Worker 703*6777b538SAndroid Build Coastguard Workerdef _IsTranslationGrdOutputLine(line): 704*6777b538SAndroid Build Coastguard Worker """Returns True iff this is an output .xtb <file> element.""" 705*6777b538SAndroid Build Coastguard Worker m = _RE_TRANSLATION_ELEMENT.search(line) 706*6777b538SAndroid Build Coastguard Worker return m is not None 707*6777b538SAndroid Build Coastguard Worker 708*6777b538SAndroid Build Coastguard Worker 709*6777b538SAndroid Build Coastguard Workerclass _IsTranslationGrdOutputLineTest(unittest.TestCase): 710*6777b538SAndroid Build Coastguard Worker 711*6777b538SAndroid Build Coastguard Worker def test_GrdTranslationOutputLines(self): 712*6777b538SAndroid Build Coastguard Worker _VALID_INPUT_LINES = [ 713*6777b538SAndroid Build Coastguard Worker '<file path="foo/bar.xtb" />', 714*6777b538SAndroid Build Coastguard Worker '<file path="foo/bar.xtb"/>', 715*6777b538SAndroid Build Coastguard Worker '<file lang="fr-CA" path="translations/aw_strings_fr-CA.xtb"/>', 716*6777b538SAndroid Build Coastguard Worker '<file lang="fr-CA" path="translations/aw_strings_fr-CA.xtb" />', 717*6777b538SAndroid Build Coastguard Worker ' <file path="translations/aw_strings_ar.xtb" lang="ar" />', 718*6777b538SAndroid Build Coastguard Worker ] 719*6777b538SAndroid Build Coastguard Worker _INVALID_INPUT_LINES = ['<file path="foo/bar.xml" />'] 720*6777b538SAndroid Build Coastguard Worker 721*6777b538SAndroid Build Coastguard Worker for line in _VALID_INPUT_LINES: 722*6777b538SAndroid Build Coastguard Worker self.assertTrue( 723*6777b538SAndroid Build Coastguard Worker _IsTranslationGrdOutputLine(line), 724*6777b538SAndroid Build Coastguard Worker '_IsTranslationGrdOutputLine() returned False for [%s]' % line) 725*6777b538SAndroid Build Coastguard Worker 726*6777b538SAndroid Build Coastguard Worker for line in _INVALID_INPUT_LINES: 727*6777b538SAndroid Build Coastguard Worker self.assertFalse( 728*6777b538SAndroid Build Coastguard Worker _IsTranslationGrdOutputLine(line), 729*6777b538SAndroid Build Coastguard Worker '_IsTranslationGrdOutputLine() returned True for [%s]' % line) 730*6777b538SAndroid Build Coastguard Worker 731*6777b538SAndroid Build Coastguard Worker 732*6777b538SAndroid Build Coastguard Workerdef _CheckGrdTranslationElementRange(grd_lines, start, end, 733*6777b538SAndroid Build Coastguard Worker wanted_locales): 734*6777b538SAndroid Build Coastguard Worker """Check all <translations> sub-elements in specific input .grd lines range. 735*6777b538SAndroid Build Coastguard Worker 736*6777b538SAndroid Build Coastguard Worker This really checks the following: 737*6777b538SAndroid Build Coastguard Worker - Each item has a 'path' attribute. 738*6777b538SAndroid Build Coastguard Worker - Each such path value ends up with '.xtb'. 739*6777b538SAndroid Build Coastguard Worker 740*6777b538SAndroid Build Coastguard Worker Args: 741*6777b538SAndroid Build Coastguard Worker grd_lines: Input .grd lines. 742*6777b538SAndroid Build Coastguard Worker start: Sub-range start position in input line list. 743*6777b538SAndroid Build Coastguard Worker end: Sub-range limit position in input line list. 744*6777b538SAndroid Build Coastguard Worker wanted_locales: Set of wanted Chromium locale names. 745*6777b538SAndroid Build Coastguard Worker Returns: 746*6777b538SAndroid Build Coastguard Worker List of error message strings for this input. Empty on success. 747*6777b538SAndroid Build Coastguard Worker """ 748*6777b538SAndroid Build Coastguard Worker errors = [] 749*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 750*6777b538SAndroid Build Coastguard Worker line = grd_lines[pos] 751*6777b538SAndroid Build Coastguard Worker lang = _GetXmlLangAttribute(line) 752*6777b538SAndroid Build Coastguard Worker if not lang: 753*6777b538SAndroid Build Coastguard Worker continue 754*6777b538SAndroid Build Coastguard Worker m = _RE_PATH_ATTRIBUTE.search(line) 755*6777b538SAndroid Build Coastguard Worker if not m: 756*6777b538SAndroid Build Coastguard Worker errors.append('%d: Missing path attribute in <file> element' % pos + 757*6777b538SAndroid Build Coastguard Worker 1) 758*6777b538SAndroid Build Coastguard Worker else: 759*6777b538SAndroid Build Coastguard Worker filename = m.group(1) 760*6777b538SAndroid Build Coastguard Worker if not filename.endswith('.xtb'): 761*6777b538SAndroid Build Coastguard Worker errors.append( 762*6777b538SAndroid Build Coastguard Worker '%d: Path should end with ".xtb": %s' % (pos + 1, filename)) 763*6777b538SAndroid Build Coastguard Worker 764*6777b538SAndroid Build Coastguard Worker return errors 765*6777b538SAndroid Build Coastguard Worker 766*6777b538SAndroid Build Coastguard Worker 767*6777b538SAndroid Build Coastguard Workerdef _CheckGrdTranslations(grd_file, grd_lines, wanted_locales): 768*6777b538SAndroid Build Coastguard Worker """Check all <file> elements that correspond to an .xtb output file. 769*6777b538SAndroid Build Coastguard Worker 770*6777b538SAndroid Build Coastguard Worker Args: 771*6777b538SAndroid Build Coastguard Worker grd_file: Input .grd file path. 772*6777b538SAndroid Build Coastguard Worker grd_lines: List of input .grd lines. 773*6777b538SAndroid Build Coastguard Worker wanted_locales: set of wanted Chromium locale names. 774*6777b538SAndroid Build Coastguard Worker Returns: 775*6777b538SAndroid Build Coastguard Worker List of error message strings. Empty on success. 776*6777b538SAndroid Build Coastguard Worker """ 777*6777b538SAndroid Build Coastguard Worker wanted_locales = wanted_locales - set([_DEFAULT_LOCALE]) 778*6777b538SAndroid Build Coastguard Worker intervals = _BuildIntervalList(grd_lines, _IsTranslationGrdOutputLine) 779*6777b538SAndroid Build Coastguard Worker errors = [] 780*6777b538SAndroid Build Coastguard Worker for start, end in intervals: 781*6777b538SAndroid Build Coastguard Worker errors += _CheckGrdElementRangeLang(grd_lines, start, end, wanted_locales) 782*6777b538SAndroid Build Coastguard Worker errors += _CheckGrdTranslationElementRange(grd_lines, start, end, 783*6777b538SAndroid Build Coastguard Worker wanted_locales) 784*6777b538SAndroid Build Coastguard Worker return errors 785*6777b538SAndroid Build Coastguard Worker 786*6777b538SAndroid Build Coastguard Worker 787*6777b538SAndroid Build Coastguard Worker# Regular expression used to replace the lang attribute inside .xtb files. 788*6777b538SAndroid Build Coastguard Worker_RE_TRANSLATIONBUNDLE = re.compile('<translationbundle lang="(.*)">') 789*6777b538SAndroid Build Coastguard Worker 790*6777b538SAndroid Build Coastguard Worker 791*6777b538SAndroid Build Coastguard Workerdef _CreateFakeXtbFileFrom(src_xtb_path, dst_xtb_path, dst_locale): 792*6777b538SAndroid Build Coastguard Worker """Create a fake .xtb file. 793*6777b538SAndroid Build Coastguard Worker 794*6777b538SAndroid Build Coastguard Worker Args: 795*6777b538SAndroid Build Coastguard Worker src_xtb_path: Path to source .xtb file to copy from. 796*6777b538SAndroid Build Coastguard Worker dst_xtb_path: Path to destination .xtb file to write to. 797*6777b538SAndroid Build Coastguard Worker dst_locale: Destination locale, the lang attribute in the source file 798*6777b538SAndroid Build Coastguard Worker will be substituted with this value before its lines are written 799*6777b538SAndroid Build Coastguard Worker to the destination file. 800*6777b538SAndroid Build Coastguard Worker """ 801*6777b538SAndroid Build Coastguard Worker with open(src_xtb_path) as f: 802*6777b538SAndroid Build Coastguard Worker src_xtb_lines = f.readlines() 803*6777b538SAndroid Build Coastguard Worker 804*6777b538SAndroid Build Coastguard Worker def replace_xtb_lang_attribute(line): 805*6777b538SAndroid Build Coastguard Worker m = _RE_TRANSLATIONBUNDLE.search(line) 806*6777b538SAndroid Build Coastguard Worker if not m: 807*6777b538SAndroid Build Coastguard Worker return line 808*6777b538SAndroid Build Coastguard Worker return line[:m.start(1)] + dst_locale + line[m.end(1):] 809*6777b538SAndroid Build Coastguard Worker 810*6777b538SAndroid Build Coastguard Worker dst_xtb_lines = [replace_xtb_lang_attribute(line) for line in src_xtb_lines] 811*6777b538SAndroid Build Coastguard Worker with build_utils.AtomicOutput(dst_xtb_path) as tmp: 812*6777b538SAndroid Build Coastguard Worker tmp.writelines(dst_xtb_lines) 813*6777b538SAndroid Build Coastguard Worker 814*6777b538SAndroid Build Coastguard Worker 815*6777b538SAndroid Build Coastguard Workerdef _AddMissingLocalesInGrdTranslations(grd_file, grd_lines, wanted_locales): 816*6777b538SAndroid Build Coastguard Worker """Fix an input .grd line by adding missing Android outputs. 817*6777b538SAndroid Build Coastguard Worker 818*6777b538SAndroid Build Coastguard Worker This also creates fake .xtb files from the one provided for 'en-GB'. 819*6777b538SAndroid Build Coastguard Worker 820*6777b538SAndroid Build Coastguard Worker Args: 821*6777b538SAndroid Build Coastguard Worker grd_file: Input .grd file path. 822*6777b538SAndroid Build Coastguard Worker grd_lines: Input .grd line list. 823*6777b538SAndroid Build Coastguard Worker wanted_locales: set of Chromium locale names. 824*6777b538SAndroid Build Coastguard Worker Returns: 825*6777b538SAndroid Build Coastguard Worker A new list of .grd lines, containing new <output> elements when needed 826*6777b538SAndroid Build Coastguard Worker for locales from |wanted_locales| that were not part of the input. 827*6777b538SAndroid Build Coastguard Worker """ 828*6777b538SAndroid Build Coastguard Worker wanted_locales = wanted_locales - set([_DEFAULT_LOCALE]) 829*6777b538SAndroid Build Coastguard Worker intervals = _BuildIntervalList(grd_lines, _IsTranslationGrdOutputLine) 830*6777b538SAndroid Build Coastguard Worker for start, end in reversed(intervals): 831*6777b538SAndroid Build Coastguard Worker locales = set() 832*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 833*6777b538SAndroid Build Coastguard Worker lang = _GetXmlLangAttribute(grd_lines[pos]) 834*6777b538SAndroid Build Coastguard Worker locale = _FixChromiumLangAttribute(lang) 835*6777b538SAndroid Build Coastguard Worker locales.add(locale) 836*6777b538SAndroid Build Coastguard Worker 837*6777b538SAndroid Build Coastguard Worker missing_locales = wanted_locales.difference(locales) 838*6777b538SAndroid Build Coastguard Worker if not missing_locales: 839*6777b538SAndroid Build Coastguard Worker continue 840*6777b538SAndroid Build Coastguard Worker 841*6777b538SAndroid Build Coastguard Worker src_locale = 'en-GB' 842*6777b538SAndroid Build Coastguard Worker src_lang_attribute = 'lang="%s"' % src_locale 843*6777b538SAndroid Build Coastguard Worker src_line = None 844*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 845*6777b538SAndroid Build Coastguard Worker if src_lang_attribute in grd_lines[pos]: 846*6777b538SAndroid Build Coastguard Worker src_line = grd_lines[pos] 847*6777b538SAndroid Build Coastguard Worker break 848*6777b538SAndroid Build Coastguard Worker 849*6777b538SAndroid Build Coastguard Worker if not src_line: 850*6777b538SAndroid Build Coastguard Worker raise Exception( 851*6777b538SAndroid Build Coastguard Worker 'Cannot find <file> element with "%s" lang attribute' % src_locale) 852*6777b538SAndroid Build Coastguard Worker 853*6777b538SAndroid Build Coastguard Worker src_path = os.path.join( 854*6777b538SAndroid Build Coastguard Worker os.path.dirname(grd_file), 855*6777b538SAndroid Build Coastguard Worker _RE_PATH_ATTRIBUTE.search(src_line).group(1)) 856*6777b538SAndroid Build Coastguard Worker 857*6777b538SAndroid Build Coastguard Worker line_count = end - 1 858*6777b538SAndroid Build Coastguard Worker for locale in missing_locales: 859*6777b538SAndroid Build Coastguard Worker dst_line = src_line.replace( 860*6777b538SAndroid Build Coastguard Worker 'lang="%s"' % src_locale, 'lang="%s"' % locale).replace( 861*6777b538SAndroid Build Coastguard Worker '_%s.xtb' % src_locale, '_%s.xtb' % locale) 862*6777b538SAndroid Build Coastguard Worker grd_lines.insert(line_count, dst_line) 863*6777b538SAndroid Build Coastguard Worker line_count += 1 864*6777b538SAndroid Build Coastguard Worker 865*6777b538SAndroid Build Coastguard Worker dst_path = src_path.replace('_%s.xtb' % src_locale, '_%s.xtb' % locale) 866*6777b538SAndroid Build Coastguard Worker _CreateFakeXtbFileFrom(src_path, dst_path, locale) 867*6777b538SAndroid Build Coastguard Worker 868*6777b538SAndroid Build Coastguard Worker 869*6777b538SAndroid Build Coastguard Worker # Sort the new <output> elements. 870*6777b538SAndroid Build Coastguard Worker return _SortGrdElementsRanges(grd_lines, _IsTranslationGrdOutputLine) 871*6777b538SAndroid Build Coastguard Worker 872*6777b538SAndroid Build Coastguard Worker 873*6777b538SAndroid Build Coastguard Worker########################################################################## 874*6777b538SAndroid Build Coastguard Worker########################################################################## 875*6777b538SAndroid Build Coastguard Worker##### 876*6777b538SAndroid Build Coastguard Worker##### G N A N D R O I D O U T P U T S 877*6777b538SAndroid Build Coastguard Worker##### 878*6777b538SAndroid Build Coastguard Worker########################################################################## 879*6777b538SAndroid Build Coastguard Worker########################################################################## 880*6777b538SAndroid Build Coastguard Worker 881*6777b538SAndroid Build Coastguard Worker_RE_GN_VALUES_LIST_LINE = re.compile( 882*6777b538SAndroid Build Coastguard Worker r'^\s*".*values(\-([A-Za-z0-9-]+))?/.*\.xml",\s*$') 883*6777b538SAndroid Build Coastguard Worker 884*6777b538SAndroid Build Coastguard Workerdef _IsBuildGnInputFile(input_file): 885*6777b538SAndroid Build Coastguard Worker """Returns True iff this is a BUILD.gn file.""" 886*6777b538SAndroid Build Coastguard Worker return os.path.basename(input_file) == 'BUILD.gn' 887*6777b538SAndroid Build Coastguard Worker 888*6777b538SAndroid Build Coastguard Worker 889*6777b538SAndroid Build Coastguard Workerdef _GetAndroidGnOutputLocale(line): 890*6777b538SAndroid Build Coastguard Worker """Check a GN list, and return its Android locale if it is an output .xml""" 891*6777b538SAndroid Build Coastguard Worker m = _RE_GN_VALUES_LIST_LINE.match(line) 892*6777b538SAndroid Build Coastguard Worker if not m: 893*6777b538SAndroid Build Coastguard Worker return None 894*6777b538SAndroid Build Coastguard Worker 895*6777b538SAndroid Build Coastguard Worker if m.group(1): # First group is optional and contains group 2. 896*6777b538SAndroid Build Coastguard Worker return m.group(2) 897*6777b538SAndroid Build Coastguard Worker 898*6777b538SAndroid Build Coastguard Worker return resource_utils.ToAndroidLocaleName(_DEFAULT_LOCALE) 899*6777b538SAndroid Build Coastguard Worker 900*6777b538SAndroid Build Coastguard Worker 901*6777b538SAndroid Build Coastguard Workerdef _IsAndroidGnOutputLine(line): 902*6777b538SAndroid Build Coastguard Worker """Returns True iff this is an Android-specific localized .xml output.""" 903*6777b538SAndroid Build Coastguard Worker return _GetAndroidGnOutputLocale(line) != None 904*6777b538SAndroid Build Coastguard Worker 905*6777b538SAndroid Build Coastguard Worker 906*6777b538SAndroid Build Coastguard Workerdef _CheckGnOutputsRangeForLocalizedStrings(gn_lines, start, end): 907*6777b538SAndroid Build Coastguard Worker """Check that a range of GN lines corresponds to localized strings. 908*6777b538SAndroid Build Coastguard Worker 909*6777b538SAndroid Build Coastguard Worker Special case: Some BUILD.gn files list several non-localized .xml files 910*6777b538SAndroid Build Coastguard Worker that should be ignored by this function, e.g. in 911*6777b538SAndroid Build Coastguard Worker components/cronet/android/BUILD.gn, the following appears: 912*6777b538SAndroid Build Coastguard Worker 913*6777b538SAndroid Build Coastguard Worker inputs = [ 914*6777b538SAndroid Build Coastguard Worker ... 915*6777b538SAndroid Build Coastguard Worker "sample/res/layout/activity_main.xml", 916*6777b538SAndroid Build Coastguard Worker "sample/res/layout/dialog_url.xml", 917*6777b538SAndroid Build Coastguard Worker "sample/res/values/dimens.xml", 918*6777b538SAndroid Build Coastguard Worker "sample/res/values/strings.xml", 919*6777b538SAndroid Build Coastguard Worker ... 920*6777b538SAndroid Build Coastguard Worker ] 921*6777b538SAndroid Build Coastguard Worker 922*6777b538SAndroid Build Coastguard Worker These are non-localized strings, and should be ignored. This function is 923*6777b538SAndroid Build Coastguard Worker used to detect them quickly. 924*6777b538SAndroid Build Coastguard Worker """ 925*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 926*6777b538SAndroid Build Coastguard Worker if not 'values/' in gn_lines[pos]: 927*6777b538SAndroid Build Coastguard Worker return True 928*6777b538SAndroid Build Coastguard Worker return False 929*6777b538SAndroid Build Coastguard Worker 930*6777b538SAndroid Build Coastguard Worker 931*6777b538SAndroid Build Coastguard Workerdef _CheckGnOutputsRange(gn_lines, start, end, wanted_locales): 932*6777b538SAndroid Build Coastguard Worker if not _CheckGnOutputsRangeForLocalizedStrings(gn_lines, start, end): 933*6777b538SAndroid Build Coastguard Worker return [] 934*6777b538SAndroid Build Coastguard Worker 935*6777b538SAndroid Build Coastguard Worker errors = [] 936*6777b538SAndroid Build Coastguard Worker locales = set() 937*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 938*6777b538SAndroid Build Coastguard Worker line = gn_lines[pos] 939*6777b538SAndroid Build Coastguard Worker android_locale = _GetAndroidGnOutputLocale(line) 940*6777b538SAndroid Build Coastguard Worker assert android_locale != None 941*6777b538SAndroid Build Coastguard Worker cr_locale = resource_utils.ToChromiumLocaleName(android_locale) 942*6777b538SAndroid Build Coastguard Worker if cr_locale in locales: 943*6777b538SAndroid Build Coastguard Worker errors.append('%s: Redefinition of output for "%s" locale' % 944*6777b538SAndroid Build Coastguard Worker (pos + 1, android_locale)) 945*6777b538SAndroid Build Coastguard Worker locales.add(cr_locale) 946*6777b538SAndroid Build Coastguard Worker 947*6777b538SAndroid Build Coastguard Worker extra_locales = locales.difference(wanted_locales) 948*6777b538SAndroid Build Coastguard Worker if extra_locales: 949*6777b538SAndroid Build Coastguard Worker errors.append('%d-%d: Extra locales: %s' % (start + 1, end + 1, 950*6777b538SAndroid Build Coastguard Worker sorted(extra_locales))) 951*6777b538SAndroid Build Coastguard Worker 952*6777b538SAndroid Build Coastguard Worker missing_locales = wanted_locales.difference(locales) 953*6777b538SAndroid Build Coastguard Worker if missing_locales: 954*6777b538SAndroid Build Coastguard Worker errors.append('%d-%d: Missing locales: %s' % (start + 1, end + 1, 955*6777b538SAndroid Build Coastguard Worker sorted(missing_locales))) 956*6777b538SAndroid Build Coastguard Worker 957*6777b538SAndroid Build Coastguard Worker return errors 958*6777b538SAndroid Build Coastguard Worker 959*6777b538SAndroid Build Coastguard Worker 960*6777b538SAndroid Build Coastguard Workerdef _CheckGnAndroidOutputs(gn_file, gn_lines, wanted_locales): 961*6777b538SAndroid Build Coastguard Worker intervals = _BuildIntervalList(gn_lines, _IsAndroidGnOutputLine) 962*6777b538SAndroid Build Coastguard Worker errors = [] 963*6777b538SAndroid Build Coastguard Worker for start, end in intervals: 964*6777b538SAndroid Build Coastguard Worker errors += _CheckGnOutputsRange(gn_lines, start, end, wanted_locales) 965*6777b538SAndroid Build Coastguard Worker return errors 966*6777b538SAndroid Build Coastguard Worker 967*6777b538SAndroid Build Coastguard Worker 968*6777b538SAndroid Build Coastguard Workerdef _AddMissingLocalesInGnAndroidOutputs(gn_file, gn_lines, wanted_locales): 969*6777b538SAndroid Build Coastguard Worker intervals = _BuildIntervalList(gn_lines, _IsAndroidGnOutputLine) 970*6777b538SAndroid Build Coastguard Worker # NOTE: Since this may insert new lines to each interval, process the 971*6777b538SAndroid Build Coastguard Worker # list in reverse order to maintain valid (start,end) positions during 972*6777b538SAndroid Build Coastguard Worker # the iteration. 973*6777b538SAndroid Build Coastguard Worker for start, end in reversed(intervals): 974*6777b538SAndroid Build Coastguard Worker if not _CheckGnOutputsRangeForLocalizedStrings(gn_lines, start, end): 975*6777b538SAndroid Build Coastguard Worker continue 976*6777b538SAndroid Build Coastguard Worker 977*6777b538SAndroid Build Coastguard Worker locales = set() 978*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 979*6777b538SAndroid Build Coastguard Worker lang = _GetAndroidGnOutputLocale(gn_lines[pos]) 980*6777b538SAndroid Build Coastguard Worker locale = resource_utils.ToChromiumLocaleName(lang) 981*6777b538SAndroid Build Coastguard Worker locales.add(locale) 982*6777b538SAndroid Build Coastguard Worker 983*6777b538SAndroid Build Coastguard Worker missing_locales = wanted_locales.difference(locales) 984*6777b538SAndroid Build Coastguard Worker if not missing_locales: 985*6777b538SAndroid Build Coastguard Worker continue 986*6777b538SAndroid Build Coastguard Worker 987*6777b538SAndroid Build Coastguard Worker src_locale = 'bg' 988*6777b538SAndroid Build Coastguard Worker src_values = 'values-%s/' % resource_utils.ToAndroidLocaleName(src_locale) 989*6777b538SAndroid Build Coastguard Worker src_line = None 990*6777b538SAndroid Build Coastguard Worker for pos in xrange(start, end): 991*6777b538SAndroid Build Coastguard Worker if src_values in gn_lines[pos]: 992*6777b538SAndroid Build Coastguard Worker src_line = gn_lines[pos] 993*6777b538SAndroid Build Coastguard Worker break 994*6777b538SAndroid Build Coastguard Worker 995*6777b538SAndroid Build Coastguard Worker if not src_line: 996*6777b538SAndroid Build Coastguard Worker raise Exception( 997*6777b538SAndroid Build Coastguard Worker 'Cannot find output list item with "%s" locale' % src_locale) 998*6777b538SAndroid Build Coastguard Worker 999*6777b538SAndroid Build Coastguard Worker line_count = end - 1 1000*6777b538SAndroid Build Coastguard Worker for locale in missing_locales: 1001*6777b538SAndroid Build Coastguard Worker if locale == _DEFAULT_LOCALE: 1002*6777b538SAndroid Build Coastguard Worker dst_line = src_line.replace('values-%s/' % src_locale, 'values/') 1003*6777b538SAndroid Build Coastguard Worker else: 1004*6777b538SAndroid Build Coastguard Worker dst_line = src_line.replace( 1005*6777b538SAndroid Build Coastguard Worker 'values-%s/' % src_locale, 1006*6777b538SAndroid Build Coastguard Worker 'values-%s/' % resource_utils.ToAndroidLocaleName(locale)) 1007*6777b538SAndroid Build Coastguard Worker gn_lines.insert(line_count, dst_line) 1008*6777b538SAndroid Build Coastguard Worker line_count += 1 1009*6777b538SAndroid Build Coastguard Worker 1010*6777b538SAndroid Build Coastguard Worker gn_lines = _SortListSubRange( 1011*6777b538SAndroid Build Coastguard Worker gn_lines, start, line_count, 1012*6777b538SAndroid Build Coastguard Worker lambda line: _RE_GN_VALUES_LIST_LINE.match(line).group(1)) 1013*6777b538SAndroid Build Coastguard Worker 1014*6777b538SAndroid Build Coastguard Worker return gn_lines 1015*6777b538SAndroid Build Coastguard Worker 1016*6777b538SAndroid Build Coastguard Worker 1017*6777b538SAndroid Build Coastguard Worker########################################################################## 1018*6777b538SAndroid Build Coastguard Worker########################################################################## 1019*6777b538SAndroid Build Coastguard Worker##### 1020*6777b538SAndroid Build Coastguard Worker##### T R A N S L A T I O N E X P E C T A T I O N S 1021*6777b538SAndroid Build Coastguard Worker##### 1022*6777b538SAndroid Build Coastguard Worker########################################################################## 1023*6777b538SAndroid Build Coastguard Worker########################################################################## 1024*6777b538SAndroid Build Coastguard Worker 1025*6777b538SAndroid Build Coastguard Worker_EXPECTATIONS_FILENAME = 'translation_expectations.pyl' 1026*6777b538SAndroid Build Coastguard Worker 1027*6777b538SAndroid Build Coastguard Worker# Technical note: the format of translation_expectations.pyl 1028*6777b538SAndroid Build Coastguard Worker# is a 'Python literal', which defines a python dictionary, so should 1029*6777b538SAndroid Build Coastguard Worker# be easy to parse. However, when modifying it, care should be taken 1030*6777b538SAndroid Build Coastguard Worker# to respect the line comments and the order of keys within the text 1031*6777b538SAndroid Build Coastguard Worker# file. 1032*6777b538SAndroid Build Coastguard Worker 1033*6777b538SAndroid Build Coastguard Worker 1034*6777b538SAndroid Build Coastguard Workerdef _ReadPythonLiteralFile(pyl_path): 1035*6777b538SAndroid Build Coastguard Worker """Read a .pyl file into a Python data structure.""" 1036*6777b538SAndroid Build Coastguard Worker with open(pyl_path) as f: 1037*6777b538SAndroid Build Coastguard Worker pyl_content = f.read() 1038*6777b538SAndroid Build Coastguard Worker # Evaluate as a Python data structure, use an empty global 1039*6777b538SAndroid Build Coastguard Worker # and local dictionary. 1040*6777b538SAndroid Build Coastguard Worker return eval(pyl_content, dict(), dict()) 1041*6777b538SAndroid Build Coastguard Worker 1042*6777b538SAndroid Build Coastguard Worker 1043*6777b538SAndroid Build Coastguard Workerdef _UpdateLocalesInExpectationLines(pyl_lines, 1044*6777b538SAndroid Build Coastguard Worker wanted_locales, 1045*6777b538SAndroid Build Coastguard Worker available_width=79): 1046*6777b538SAndroid Build Coastguard Worker """Update the locales list(s) found in an expectations file. 1047*6777b538SAndroid Build Coastguard Worker 1048*6777b538SAndroid Build Coastguard Worker Args: 1049*6777b538SAndroid Build Coastguard Worker pyl_lines: Iterable of input lines from the file. 1050*6777b538SAndroid Build Coastguard Worker wanted_locales: Set or list of new locale names. 1051*6777b538SAndroid Build Coastguard Worker available_width: Optional, number of character colums used 1052*6777b538SAndroid Build Coastguard Worker to word-wrap the new list items. 1053*6777b538SAndroid Build Coastguard Worker Returns: 1054*6777b538SAndroid Build Coastguard Worker New list of updated lines. 1055*6777b538SAndroid Build Coastguard Worker """ 1056*6777b538SAndroid Build Coastguard Worker locales_list = ['"%s"' % loc for loc in sorted(wanted_locales)] 1057*6777b538SAndroid Build Coastguard Worker result = [] 1058*6777b538SAndroid Build Coastguard Worker line_count = len(pyl_lines) 1059*6777b538SAndroid Build Coastguard Worker line_num = 0 1060*6777b538SAndroid Build Coastguard Worker DICT_START = '"languages": [' 1061*6777b538SAndroid Build Coastguard Worker while line_num < line_count: 1062*6777b538SAndroid Build Coastguard Worker line = pyl_lines[line_num] 1063*6777b538SAndroid Build Coastguard Worker line_num += 1 1064*6777b538SAndroid Build Coastguard Worker result.append(line) 1065*6777b538SAndroid Build Coastguard Worker # Look for start of "languages" dictionary. 1066*6777b538SAndroid Build Coastguard Worker pos = line.find(DICT_START) 1067*6777b538SAndroid Build Coastguard Worker if pos < 0: 1068*6777b538SAndroid Build Coastguard Worker continue 1069*6777b538SAndroid Build Coastguard Worker 1070*6777b538SAndroid Build Coastguard Worker start_margin = pos 1071*6777b538SAndroid Build Coastguard Worker start_line = line_num 1072*6777b538SAndroid Build Coastguard Worker # Skip over all lines from the list. 1073*6777b538SAndroid Build Coastguard Worker while (line_num < line_count and 1074*6777b538SAndroid Build Coastguard Worker not pyl_lines[line_num].rstrip().endswith('],')): 1075*6777b538SAndroid Build Coastguard Worker line_num += 1 1076*6777b538SAndroid Build Coastguard Worker continue 1077*6777b538SAndroid Build Coastguard Worker 1078*6777b538SAndroid Build Coastguard Worker if line_num == line_count: 1079*6777b538SAndroid Build Coastguard Worker raise Exception('%d: Missing list termination!' % start_line) 1080*6777b538SAndroid Build Coastguard Worker 1081*6777b538SAndroid Build Coastguard Worker # Format the new list according to the new margin. 1082*6777b538SAndroid Build Coastguard Worker locale_width = available_width - (start_margin + 2) 1083*6777b538SAndroid Build Coastguard Worker locale_lines = _PrettyPrintListAsLines( 1084*6777b538SAndroid Build Coastguard Worker locales_list, locale_width, trailing_comma=True) 1085*6777b538SAndroid Build Coastguard Worker for locale_line in locale_lines: 1086*6777b538SAndroid Build Coastguard Worker result.append(' ' * (start_margin + 2) + locale_line) 1087*6777b538SAndroid Build Coastguard Worker result.append(' ' * start_margin + '],') 1088*6777b538SAndroid Build Coastguard Worker line_num += 1 1089*6777b538SAndroid Build Coastguard Worker 1090*6777b538SAndroid Build Coastguard Worker return result 1091*6777b538SAndroid Build Coastguard Worker 1092*6777b538SAndroid Build Coastguard Worker 1093*6777b538SAndroid Build Coastguard Workerclass _UpdateLocalesInExpectationLinesTest(unittest.TestCase): 1094*6777b538SAndroid Build Coastguard Worker 1095*6777b538SAndroid Build Coastguard Worker def test_simple(self): 1096*6777b538SAndroid Build Coastguard Worker self.maxDiff = 1000 1097*6777b538SAndroid Build Coastguard Worker input_text = r''' 1098*6777b538SAndroid Build Coastguard Worker# This comment should be preserved 1099*6777b538SAndroid Build Coastguard Worker# 23456789012345678901234567890123456789 1100*6777b538SAndroid Build Coastguard Worker{ 1101*6777b538SAndroid Build Coastguard Worker "android_grd": { 1102*6777b538SAndroid Build Coastguard Worker "languages": [ 1103*6777b538SAndroid Build Coastguard Worker "aa", "bb", "cc", "dd", "ee", 1104*6777b538SAndroid Build Coastguard Worker "ff", "gg", "hh", "ii", "jj", 1105*6777b538SAndroid Build Coastguard Worker "kk"], 1106*6777b538SAndroid Build Coastguard Worker }, 1107*6777b538SAndroid Build Coastguard Worker # Example with bad indentation in input. 1108*6777b538SAndroid Build Coastguard Worker "another_grd": { 1109*6777b538SAndroid Build Coastguard Worker "languages": [ 1110*6777b538SAndroid Build Coastguard Worker "aa", "bb", "cc", "dd", "ee", "ff", "gg", "hh", "ii", "jj", "kk", 1111*6777b538SAndroid Build Coastguard Worker ], 1112*6777b538SAndroid Build Coastguard Worker }, 1113*6777b538SAndroid Build Coastguard Worker} 1114*6777b538SAndroid Build Coastguard Worker''' 1115*6777b538SAndroid Build Coastguard Worker expected_text = r''' 1116*6777b538SAndroid Build Coastguard Worker# This comment should be preserved 1117*6777b538SAndroid Build Coastguard Worker# 23456789012345678901234567890123456789 1118*6777b538SAndroid Build Coastguard Worker{ 1119*6777b538SAndroid Build Coastguard Worker "android_grd": { 1120*6777b538SAndroid Build Coastguard Worker "languages": [ 1121*6777b538SAndroid Build Coastguard Worker "A2", "AA", "BB", "CC", "DD", 1122*6777b538SAndroid Build Coastguard Worker "E2", "EE", "FF", "GG", "HH", 1123*6777b538SAndroid Build Coastguard Worker "I2", "II", "JJ", "KK", 1124*6777b538SAndroid Build Coastguard Worker ], 1125*6777b538SAndroid Build Coastguard Worker }, 1126*6777b538SAndroid Build Coastguard Worker # Example with bad indentation in input. 1127*6777b538SAndroid Build Coastguard Worker "another_grd": { 1128*6777b538SAndroid Build Coastguard Worker "languages": [ 1129*6777b538SAndroid Build Coastguard Worker "A2", "AA", "BB", "CC", "DD", 1130*6777b538SAndroid Build Coastguard Worker "E2", "EE", "FF", "GG", "HH", 1131*6777b538SAndroid Build Coastguard Worker "I2", "II", "JJ", "KK", 1132*6777b538SAndroid Build Coastguard Worker ], 1133*6777b538SAndroid Build Coastguard Worker }, 1134*6777b538SAndroid Build Coastguard Worker} 1135*6777b538SAndroid Build Coastguard Worker''' 1136*6777b538SAndroid Build Coastguard Worker input_lines = input_text.splitlines() 1137*6777b538SAndroid Build Coastguard Worker test_locales = ([ 1138*6777b538SAndroid Build Coastguard Worker 'AA', 'BB', 'CC', 'DD', 'EE', 'FF', 'GG', 'HH', 'II', 'JJ', 'KK', 'A2', 1139*6777b538SAndroid Build Coastguard Worker 'E2', 'I2' 1140*6777b538SAndroid Build Coastguard Worker ]) 1141*6777b538SAndroid Build Coastguard Worker expected_lines = expected_text.splitlines() 1142*6777b538SAndroid Build Coastguard Worker self.assertListEqual( 1143*6777b538SAndroid Build Coastguard Worker _UpdateLocalesInExpectationLines(input_lines, test_locales, 40), 1144*6777b538SAndroid Build Coastguard Worker expected_lines) 1145*6777b538SAndroid Build Coastguard Worker 1146*6777b538SAndroid Build Coastguard Worker def test_missing_list_termination(self): 1147*6777b538SAndroid Build Coastguard Worker input_lines = r''' 1148*6777b538SAndroid Build Coastguard Worker "languages": [' 1149*6777b538SAndroid Build Coastguard Worker "aa", "bb", "cc", "dd" 1150*6777b538SAndroid Build Coastguard Worker'''.splitlines() 1151*6777b538SAndroid Build Coastguard Worker with self.assertRaises(Exception) as cm: 1152*6777b538SAndroid Build Coastguard Worker _UpdateLocalesInExpectationLines(input_lines, ['a', 'b'], 40) 1153*6777b538SAndroid Build Coastguard Worker 1154*6777b538SAndroid Build Coastguard Worker self.assertEqual(str(cm.exception), '2: Missing list termination!') 1155*6777b538SAndroid Build Coastguard Worker 1156*6777b538SAndroid Build Coastguard Worker 1157*6777b538SAndroid Build Coastguard Workerdef _UpdateLocalesInExpectationFile(pyl_path, wanted_locales): 1158*6777b538SAndroid Build Coastguard Worker """Update all locales listed in a given expectations file. 1159*6777b538SAndroid Build Coastguard Worker 1160*6777b538SAndroid Build Coastguard Worker Args: 1161*6777b538SAndroid Build Coastguard Worker pyl_path: Path to .pyl file to update. 1162*6777b538SAndroid Build Coastguard Worker wanted_locales: List of locales that need to be written to 1163*6777b538SAndroid Build Coastguard Worker the file. 1164*6777b538SAndroid Build Coastguard Worker """ 1165*6777b538SAndroid Build Coastguard Worker tc_locales = { 1166*6777b538SAndroid Build Coastguard Worker _FixTranslationConsoleLocaleName(locale) 1167*6777b538SAndroid Build Coastguard Worker for locale in set(wanted_locales) - set([_DEFAULT_LOCALE]) 1168*6777b538SAndroid Build Coastguard Worker } 1169*6777b538SAndroid Build Coastguard Worker 1170*6777b538SAndroid Build Coastguard Worker with open(pyl_path) as f: 1171*6777b538SAndroid Build Coastguard Worker input_lines = [l.rstrip() for l in f.readlines()] 1172*6777b538SAndroid Build Coastguard Worker 1173*6777b538SAndroid Build Coastguard Worker updated_lines = _UpdateLocalesInExpectationLines(input_lines, tc_locales) 1174*6777b538SAndroid Build Coastguard Worker with build_utils.AtomicOutput(pyl_path) as f: 1175*6777b538SAndroid Build Coastguard Worker f.writelines('\n'.join(updated_lines) + '\n') 1176*6777b538SAndroid Build Coastguard Worker 1177*6777b538SAndroid Build Coastguard Worker 1178*6777b538SAndroid Build Coastguard Worker########################################################################## 1179*6777b538SAndroid Build Coastguard Worker########################################################################## 1180*6777b538SAndroid Build Coastguard Worker##### 1181*6777b538SAndroid Build Coastguard Worker##### C H E C K E V E R Y T H I N G 1182*6777b538SAndroid Build Coastguard Worker##### 1183*6777b538SAndroid Build Coastguard Worker########################################################################## 1184*6777b538SAndroid Build Coastguard Worker########################################################################## 1185*6777b538SAndroid Build Coastguard Worker 1186*6777b538SAndroid Build Coastguard Worker# pylint: enable=unused-argument 1187*6777b538SAndroid Build Coastguard Worker 1188*6777b538SAndroid Build Coastguard Worker 1189*6777b538SAndroid Build Coastguard Workerdef _IsAllInputFile(input_file): 1190*6777b538SAndroid Build Coastguard Worker return _IsGritInputFile(input_file) or _IsBuildGnInputFile(input_file) 1191*6777b538SAndroid Build Coastguard Worker 1192*6777b538SAndroid Build Coastguard Worker 1193*6777b538SAndroid Build Coastguard Workerdef _CheckAllFiles(input_file, input_lines, wanted_locales): 1194*6777b538SAndroid Build Coastguard Worker errors = [] 1195*6777b538SAndroid Build Coastguard Worker if _IsGritInputFile(input_file): 1196*6777b538SAndroid Build Coastguard Worker errors += _CheckGrdTranslations(input_file, input_lines, wanted_locales) 1197*6777b538SAndroid Build Coastguard Worker errors += _CheckGrdAndroidOutputElements( 1198*6777b538SAndroid Build Coastguard Worker input_file, input_lines, wanted_locales) 1199*6777b538SAndroid Build Coastguard Worker elif _IsBuildGnInputFile(input_file): 1200*6777b538SAndroid Build Coastguard Worker errors += _CheckGnAndroidOutputs(input_file, input_lines, wanted_locales) 1201*6777b538SAndroid Build Coastguard Worker return errors 1202*6777b538SAndroid Build Coastguard Worker 1203*6777b538SAndroid Build Coastguard Worker 1204*6777b538SAndroid Build Coastguard Workerdef _AddMissingLocalesInAllFiles(input_file, input_lines, wanted_locales): 1205*6777b538SAndroid Build Coastguard Worker if _IsGritInputFile(input_file): 1206*6777b538SAndroid Build Coastguard Worker lines = _AddMissingLocalesInGrdTranslations( 1207*6777b538SAndroid Build Coastguard Worker input_file, input_lines, wanted_locales) 1208*6777b538SAndroid Build Coastguard Worker lines = _AddMissingLocalesInGrdAndroidOutputs( 1209*6777b538SAndroid Build Coastguard Worker input_file, lines, wanted_locales) 1210*6777b538SAndroid Build Coastguard Worker elif _IsBuildGnInputFile(input_file): 1211*6777b538SAndroid Build Coastguard Worker lines = _AddMissingLocalesInGnAndroidOutputs( 1212*6777b538SAndroid Build Coastguard Worker input_file, input_lines, wanted_locales) 1213*6777b538SAndroid Build Coastguard Worker return lines 1214*6777b538SAndroid Build Coastguard Worker 1215*6777b538SAndroid Build Coastguard Worker 1216*6777b538SAndroid Build Coastguard Worker########################################################################## 1217*6777b538SAndroid Build Coastguard Worker########################################################################## 1218*6777b538SAndroid Build Coastguard Worker##### 1219*6777b538SAndroid Build Coastguard Worker##### C O M M A N D H A N D L I N G 1220*6777b538SAndroid Build Coastguard Worker##### 1221*6777b538SAndroid Build Coastguard Worker########################################################################## 1222*6777b538SAndroid Build Coastguard Worker########################################################################## 1223*6777b538SAndroid Build Coastguard Worker 1224*6777b538SAndroid Build Coastguard Workerclass _Command(object): 1225*6777b538SAndroid Build Coastguard Worker """A base class for all commands recognized by this script. 1226*6777b538SAndroid Build Coastguard Worker 1227*6777b538SAndroid Build Coastguard Worker Usage is the following: 1228*6777b538SAndroid Build Coastguard Worker 1) Derived classes must re-define the following class-based fields: 1229*6777b538SAndroid Build Coastguard Worker - name: Command name (e.g. 'list-locales') 1230*6777b538SAndroid Build Coastguard Worker - description: Command short description. 1231*6777b538SAndroid Build Coastguard Worker - long_description: Optional. Command long description. 1232*6777b538SAndroid Build Coastguard Worker NOTE: As a convenience, if the first character is a newline, 1233*6777b538SAndroid Build Coastguard Worker it will be omitted in the help output. 1234*6777b538SAndroid Build Coastguard Worker 1235*6777b538SAndroid Build Coastguard Worker 2) Derived classes for commands that take arguments should override 1236*6777b538SAndroid Build Coastguard Worker RegisterExtraArgs(), which receives a corresponding argparse 1237*6777b538SAndroid Build Coastguard Worker sub-parser as argument. 1238*6777b538SAndroid Build Coastguard Worker 1239*6777b538SAndroid Build Coastguard Worker 3) Derived classes should implement a Run() command, which can read 1240*6777b538SAndroid Build Coastguard Worker the current arguments from self.args. 1241*6777b538SAndroid Build Coastguard Worker """ 1242*6777b538SAndroid Build Coastguard Worker name = None 1243*6777b538SAndroid Build Coastguard Worker description = None 1244*6777b538SAndroid Build Coastguard Worker long_description = None 1245*6777b538SAndroid Build Coastguard Worker 1246*6777b538SAndroid Build Coastguard Worker def __init__(self): 1247*6777b538SAndroid Build Coastguard Worker self._parser = None 1248*6777b538SAndroid Build Coastguard Worker self.args = None 1249*6777b538SAndroid Build Coastguard Worker 1250*6777b538SAndroid Build Coastguard Worker def RegisterExtraArgs(self, subparser): 1251*6777b538SAndroid Build Coastguard Worker pass 1252*6777b538SAndroid Build Coastguard Worker 1253*6777b538SAndroid Build Coastguard Worker def RegisterArgs(self, parser): 1254*6777b538SAndroid Build Coastguard Worker subp = parser.add_parser( 1255*6777b538SAndroid Build Coastguard Worker self.name, help=self.description, 1256*6777b538SAndroid Build Coastguard Worker description=self.long_description or self.description, 1257*6777b538SAndroid Build Coastguard Worker formatter_class=argparse.RawDescriptionHelpFormatter) 1258*6777b538SAndroid Build Coastguard Worker self._parser = subp 1259*6777b538SAndroid Build Coastguard Worker subp.set_defaults(command=self) 1260*6777b538SAndroid Build Coastguard Worker group = subp.add_argument_group('%s arguments' % self.name) 1261*6777b538SAndroid Build Coastguard Worker self.RegisterExtraArgs(group) 1262*6777b538SAndroid Build Coastguard Worker 1263*6777b538SAndroid Build Coastguard Worker def ProcessArgs(self, args): 1264*6777b538SAndroid Build Coastguard Worker self.args = args 1265*6777b538SAndroid Build Coastguard Worker 1266*6777b538SAndroid Build Coastguard Worker 1267*6777b538SAndroid Build Coastguard Workerclass _ListLocalesCommand(_Command): 1268*6777b538SAndroid Build Coastguard Worker """Implement the 'list-locales' command to list locale lists of interest.""" 1269*6777b538SAndroid Build Coastguard Worker name = 'list-locales' 1270*6777b538SAndroid Build Coastguard Worker description = 'List supported Chrome locales' 1271*6777b538SAndroid Build Coastguard Worker long_description = r''' 1272*6777b538SAndroid Build Coastguard WorkerList locales of interest, by default this prints all locales supported by 1273*6777b538SAndroid Build Coastguard WorkerChrome, but `--type=ios_unsupported` can be used for the list of locales 1274*6777b538SAndroid Build Coastguard Workerunsupported on iOS. 1275*6777b538SAndroid Build Coastguard Worker 1276*6777b538SAndroid Build Coastguard WorkerThese values are extracted directly from build/config/locales.gni. 1277*6777b538SAndroid Build Coastguard Worker 1278*6777b538SAndroid Build Coastguard WorkerAdditionally, use the --as-json argument to print the list as a JSON list, 1279*6777b538SAndroid Build Coastguard Workerinstead of the default format (which is a space-separated list of locale names). 1280*6777b538SAndroid Build Coastguard Worker''' 1281*6777b538SAndroid Build Coastguard Worker 1282*6777b538SAndroid Build Coastguard Worker # Maps type argument to a function returning the corresponding locales list. 1283*6777b538SAndroid Build Coastguard Worker TYPE_MAP = { 1284*6777b538SAndroid Build Coastguard Worker 'all': ChromeLocales, 1285*6777b538SAndroid Build Coastguard Worker 'ios_unsupported': IosUnsupportedLocales, 1286*6777b538SAndroid Build Coastguard Worker } 1287*6777b538SAndroid Build Coastguard Worker 1288*6777b538SAndroid Build Coastguard Worker def RegisterExtraArgs(self, group): 1289*6777b538SAndroid Build Coastguard Worker group.add_argument( 1290*6777b538SAndroid Build Coastguard Worker '--as-json', 1291*6777b538SAndroid Build Coastguard Worker action='store_true', 1292*6777b538SAndroid Build Coastguard Worker help='Output as JSON list.') 1293*6777b538SAndroid Build Coastguard Worker group.add_argument( 1294*6777b538SAndroid Build Coastguard Worker '--type', 1295*6777b538SAndroid Build Coastguard Worker choices=tuple(self.TYPE_MAP.viewkeys()), 1296*6777b538SAndroid Build Coastguard Worker default='all', 1297*6777b538SAndroid Build Coastguard Worker help='Select type of locale list to print.') 1298*6777b538SAndroid Build Coastguard Worker 1299*6777b538SAndroid Build Coastguard Worker def Run(self): 1300*6777b538SAndroid Build Coastguard Worker locale_list = self.TYPE_MAP[self.args.type]() 1301*6777b538SAndroid Build Coastguard Worker if self.args.as_json: 1302*6777b538SAndroid Build Coastguard Worker print('[%s]' % ", ".join("'%s'" % loc for loc in locale_list)) 1303*6777b538SAndroid Build Coastguard Worker else: 1304*6777b538SAndroid Build Coastguard Worker print(' '.join(locale_list)) 1305*6777b538SAndroid Build Coastguard Worker 1306*6777b538SAndroid Build Coastguard Worker 1307*6777b538SAndroid Build Coastguard Workerclass _CheckInputFileBaseCommand(_Command): 1308*6777b538SAndroid Build Coastguard Worker """Used as a base for other _Command subclasses that check input files. 1309*6777b538SAndroid Build Coastguard Worker 1310*6777b538SAndroid Build Coastguard Worker Subclasses should also define the following class-level variables: 1311*6777b538SAndroid Build Coastguard Worker 1312*6777b538SAndroid Build Coastguard Worker - select_file_func: 1313*6777b538SAndroid Build Coastguard Worker A predicate that receives a file name (not path) and return True if it 1314*6777b538SAndroid Build Coastguard Worker should be selected for inspection. Used when scanning directories with 1315*6777b538SAndroid Build Coastguard Worker '--scan-dir <dir>'. 1316*6777b538SAndroid Build Coastguard Worker 1317*6777b538SAndroid Build Coastguard Worker - check_func: 1318*6777b538SAndroid Build Coastguard Worker - fix_func: 1319*6777b538SAndroid Build Coastguard Worker Two functions passed as parameters to _ProcessFile(), see relevant 1320*6777b538SAndroid Build Coastguard Worker documentation in this function's definition. 1321*6777b538SAndroid Build Coastguard Worker """ 1322*6777b538SAndroid Build Coastguard Worker select_file_func = None 1323*6777b538SAndroid Build Coastguard Worker check_func = None 1324*6777b538SAndroid Build Coastguard Worker fix_func = None 1325*6777b538SAndroid Build Coastguard Worker 1326*6777b538SAndroid Build Coastguard Worker def RegisterExtraArgs(self, group): 1327*6777b538SAndroid Build Coastguard Worker group.add_argument( 1328*6777b538SAndroid Build Coastguard Worker '--scan-dir', 1329*6777b538SAndroid Build Coastguard Worker action='append', 1330*6777b538SAndroid Build Coastguard Worker help='Optional directory to scan for input files recursively.') 1331*6777b538SAndroid Build Coastguard Worker group.add_argument( 1332*6777b538SAndroid Build Coastguard Worker 'input', 1333*6777b538SAndroid Build Coastguard Worker nargs='*', 1334*6777b538SAndroid Build Coastguard Worker help='Input file(s) to check.') 1335*6777b538SAndroid Build Coastguard Worker group.add_argument( 1336*6777b538SAndroid Build Coastguard Worker '--fix-inplace', 1337*6777b538SAndroid Build Coastguard Worker action='store_true', 1338*6777b538SAndroid Build Coastguard Worker help='Try to fix the files in-place too.') 1339*6777b538SAndroid Build Coastguard Worker group.add_argument( 1340*6777b538SAndroid Build Coastguard Worker '--add-locales', 1341*6777b538SAndroid Build Coastguard Worker help='Space-separated list of additional locales to use') 1342*6777b538SAndroid Build Coastguard Worker 1343*6777b538SAndroid Build Coastguard Worker def Run(self): 1344*6777b538SAndroid Build Coastguard Worker args = self.args 1345*6777b538SAndroid Build Coastguard Worker input_files = [] 1346*6777b538SAndroid Build Coastguard Worker if args.input: 1347*6777b538SAndroid Build Coastguard Worker input_files = args.input 1348*6777b538SAndroid Build Coastguard Worker if args.scan_dir: 1349*6777b538SAndroid Build Coastguard Worker input_files.extend(_ScanDirectoriesForFiles( 1350*6777b538SAndroid Build Coastguard Worker args.scan_dir, self.select_file_func.__func__)) 1351*6777b538SAndroid Build Coastguard Worker locales = ChromeLocales() 1352*6777b538SAndroid Build Coastguard Worker if args.add_locales: 1353*6777b538SAndroid Build Coastguard Worker locales.extend(args.add_locales.split(' ')) 1354*6777b538SAndroid Build Coastguard Worker 1355*6777b538SAndroid Build Coastguard Worker locales = set(locales) 1356*6777b538SAndroid Build Coastguard Worker 1357*6777b538SAndroid Build Coastguard Worker for input_file in input_files: 1358*6777b538SAndroid Build Coastguard Worker _ProcessFile(input_file, 1359*6777b538SAndroid Build Coastguard Worker locales, 1360*6777b538SAndroid Build Coastguard Worker self.check_func.__func__, 1361*6777b538SAndroid Build Coastguard Worker self.fix_func.__func__ if args.fix_inplace else None) 1362*6777b538SAndroid Build Coastguard Worker print('%sDone.' % (_CONSOLE_START_LINE)) 1363*6777b538SAndroid Build Coastguard Worker 1364*6777b538SAndroid Build Coastguard Worker 1365*6777b538SAndroid Build Coastguard Workerclass _CheckGrdAndroidOutputsCommand(_CheckInputFileBaseCommand): 1366*6777b538SAndroid Build Coastguard Worker name = 'check-grd-android-outputs' 1367*6777b538SAndroid Build Coastguard Worker description = ( 1368*6777b538SAndroid Build Coastguard Worker 'Check the Android resource (.xml) files outputs in GRIT input files.') 1369*6777b538SAndroid Build Coastguard Worker long_description = r''' 1370*6777b538SAndroid Build Coastguard WorkerCheck the Android .xml files outputs in one or more input GRIT (.grd) files 1371*6777b538SAndroid Build Coastguard Workerfor the following conditions: 1372*6777b538SAndroid Build Coastguard Worker 1373*6777b538SAndroid Build Coastguard Worker - Each item has a correct 'lang' attribute. 1374*6777b538SAndroid Build Coastguard Worker - There are no duplicated lines for the same 'lang' attribute. 1375*6777b538SAndroid Build Coastguard Worker - That there are no extra locales that Chromium doesn't want. 1376*6777b538SAndroid Build Coastguard Worker - That no wanted locale is missing. 1377*6777b538SAndroid Build Coastguard Worker - Filenames exist for each listed locale. 1378*6777b538SAndroid Build Coastguard Worker - Filenames are well-formed. 1379*6777b538SAndroid Build Coastguard Worker''' 1380*6777b538SAndroid Build Coastguard Worker select_file_func = _IsGritInputFile 1381*6777b538SAndroid Build Coastguard Worker check_func = _CheckGrdAndroidOutputElements 1382*6777b538SAndroid Build Coastguard Worker fix_func = _AddMissingLocalesInGrdAndroidOutputs 1383*6777b538SAndroid Build Coastguard Worker 1384*6777b538SAndroid Build Coastguard Worker 1385*6777b538SAndroid Build Coastguard Workerclass _CheckGrdTranslationsCommand(_CheckInputFileBaseCommand): 1386*6777b538SAndroid Build Coastguard Worker name = 'check-grd-translations' 1387*6777b538SAndroid Build Coastguard Worker description = ( 1388*6777b538SAndroid Build Coastguard Worker 'Check the translation (.xtb) files outputted by .grd input files.') 1389*6777b538SAndroid Build Coastguard Worker long_description = r''' 1390*6777b538SAndroid Build Coastguard WorkerCheck the translation (.xtb) file outputs in one or more input GRIT (.grd) files 1391*6777b538SAndroid Build Coastguard Workerfor the following conditions: 1392*6777b538SAndroid Build Coastguard Worker 1393*6777b538SAndroid Build Coastguard Worker - Each item has a correct 'lang' attribute. 1394*6777b538SAndroid Build Coastguard Worker - There are no duplicated lines for the same 'lang' attribute. 1395*6777b538SAndroid Build Coastguard Worker - That there are no extra locales that Chromium doesn't want. 1396*6777b538SAndroid Build Coastguard Worker - That no wanted locale is missing. 1397*6777b538SAndroid Build Coastguard Worker - Each item has a 'path' attribute. 1398*6777b538SAndroid Build Coastguard Worker - Each such path value ends up with '.xtb'. 1399*6777b538SAndroid Build Coastguard Worker''' 1400*6777b538SAndroid Build Coastguard Worker select_file_func = _IsGritInputFile 1401*6777b538SAndroid Build Coastguard Worker check_func = _CheckGrdTranslations 1402*6777b538SAndroid Build Coastguard Worker fix_func = _AddMissingLocalesInGrdTranslations 1403*6777b538SAndroid Build Coastguard Worker 1404*6777b538SAndroid Build Coastguard Worker 1405*6777b538SAndroid Build Coastguard Workerclass _CheckGnAndroidOutputsCommand(_CheckInputFileBaseCommand): 1406*6777b538SAndroid Build Coastguard Worker name = 'check-gn-android-outputs' 1407*6777b538SAndroid Build Coastguard Worker description = 'Check the Android .xml file lists in GN build files.' 1408*6777b538SAndroid Build Coastguard Worker long_description = r''' 1409*6777b538SAndroid Build Coastguard WorkerCheck one or more BUILD.gn file, looking for lists of Android resource .xml 1410*6777b538SAndroid Build Coastguard Workerfiles, and checking that: 1411*6777b538SAndroid Build Coastguard Worker 1412*6777b538SAndroid Build Coastguard Worker - There are no duplicated output files in the list. 1413*6777b538SAndroid Build Coastguard Worker - Each output file belongs to a wanted Chromium locale. 1414*6777b538SAndroid Build Coastguard Worker - There are no output files for unwanted Chromium locales. 1415*6777b538SAndroid Build Coastguard Worker''' 1416*6777b538SAndroid Build Coastguard Worker select_file_func = _IsBuildGnInputFile 1417*6777b538SAndroid Build Coastguard Worker check_func = _CheckGnAndroidOutputs 1418*6777b538SAndroid Build Coastguard Worker fix_func = _AddMissingLocalesInGnAndroidOutputs 1419*6777b538SAndroid Build Coastguard Worker 1420*6777b538SAndroid Build Coastguard Worker 1421*6777b538SAndroid Build Coastguard Workerclass _CheckAllCommand(_CheckInputFileBaseCommand): 1422*6777b538SAndroid Build Coastguard Worker name = 'check-all' 1423*6777b538SAndroid Build Coastguard Worker description = 'Check everything.' 1424*6777b538SAndroid Build Coastguard Worker long_description = 'Equivalent to calling all other check-xxx commands.' 1425*6777b538SAndroid Build Coastguard Worker select_file_func = _IsAllInputFile 1426*6777b538SAndroid Build Coastguard Worker check_func = _CheckAllFiles 1427*6777b538SAndroid Build Coastguard Worker fix_func = _AddMissingLocalesInAllFiles 1428*6777b538SAndroid Build Coastguard Worker 1429*6777b538SAndroid Build Coastguard Worker 1430*6777b538SAndroid Build Coastguard Workerclass _UpdateExpectationsCommand(_Command): 1431*6777b538SAndroid Build Coastguard Worker name = 'update-expectations' 1432*6777b538SAndroid Build Coastguard Worker description = 'Update translation expectations file.' 1433*6777b538SAndroid Build Coastguard Worker long_description = r''' 1434*6777b538SAndroid Build Coastguard WorkerUpdate %s files to match the current list of locales supported by Chromium. 1435*6777b538SAndroid Build Coastguard WorkerThis is especially useful to add new locales before updating any GRIT or GN 1436*6777b538SAndroid Build Coastguard Workerinput file with the --add-locales option. 1437*6777b538SAndroid Build Coastguard Worker''' % _EXPECTATIONS_FILENAME 1438*6777b538SAndroid Build Coastguard Worker 1439*6777b538SAndroid Build Coastguard Worker def RegisterExtraArgs(self, group): 1440*6777b538SAndroid Build Coastguard Worker group.add_argument( 1441*6777b538SAndroid Build Coastguard Worker '--add-locales', 1442*6777b538SAndroid Build Coastguard Worker help='Space-separated list of additional locales to use.') 1443*6777b538SAndroid Build Coastguard Worker 1444*6777b538SAndroid Build Coastguard Worker def Run(self): 1445*6777b538SAndroid Build Coastguard Worker locales = ChromeLocales() 1446*6777b538SAndroid Build Coastguard Worker add_locales = self.args.add_locales 1447*6777b538SAndroid Build Coastguard Worker if add_locales: 1448*6777b538SAndroid Build Coastguard Worker locales.extend(add_locales.split(' ')) 1449*6777b538SAndroid Build Coastguard Worker 1450*6777b538SAndroid Build Coastguard Worker expectation_paths = [ 1451*6777b538SAndroid Build Coastguard Worker 'tools/gritsettings/translation_expectations.pyl', 1452*6777b538SAndroid Build Coastguard Worker 'clank/tools/translation_expectations.pyl', 1453*6777b538SAndroid Build Coastguard Worker ] 1454*6777b538SAndroid Build Coastguard Worker missing_expectation_files = [] 1455*6777b538SAndroid Build Coastguard Worker for path in enumerate(expectation_paths): 1456*6777b538SAndroid Build Coastguard Worker file_path = os.path.join(_TOP_SRC_DIR, path) 1457*6777b538SAndroid Build Coastguard Worker if not os.path.exists(file_path): 1458*6777b538SAndroid Build Coastguard Worker missing_expectation_files.append(file_path) 1459*6777b538SAndroid Build Coastguard Worker continue 1460*6777b538SAndroid Build Coastguard Worker _UpdateLocalesInExpectationFile(file_path, locales) 1461*6777b538SAndroid Build Coastguard Worker 1462*6777b538SAndroid Build Coastguard Worker if missing_expectation_files: 1463*6777b538SAndroid Build Coastguard Worker sys.stderr.write('WARNING: Missing file(s): %s\n' % 1464*6777b538SAndroid Build Coastguard Worker (', '.join(missing_expectation_files))) 1465*6777b538SAndroid Build Coastguard Worker 1466*6777b538SAndroid Build Coastguard Worker 1467*6777b538SAndroid Build Coastguard Workerclass _UnitTestsCommand(_Command): 1468*6777b538SAndroid Build Coastguard Worker name = 'unit-tests' 1469*6777b538SAndroid Build Coastguard Worker description = 'Run internal unit-tests for this script' 1470*6777b538SAndroid Build Coastguard Worker 1471*6777b538SAndroid Build Coastguard Worker def RegisterExtraArgs(self, group): 1472*6777b538SAndroid Build Coastguard Worker group.add_argument( 1473*6777b538SAndroid Build Coastguard Worker '-v', '--verbose', action='count', help='Increase test verbosity.') 1474*6777b538SAndroid Build Coastguard Worker group.add_argument('args', nargs=argparse.REMAINDER) 1475*6777b538SAndroid Build Coastguard Worker 1476*6777b538SAndroid Build Coastguard Worker def Run(self): 1477*6777b538SAndroid Build Coastguard Worker argv = [_SCRIPT_NAME] + self.args.args 1478*6777b538SAndroid Build Coastguard Worker unittest.main(argv=argv, verbosity=self.args.verbose) 1479*6777b538SAndroid Build Coastguard Worker 1480*6777b538SAndroid Build Coastguard Worker 1481*6777b538SAndroid Build Coastguard Worker# List of all commands supported by this script. 1482*6777b538SAndroid Build Coastguard Worker_COMMANDS = [ 1483*6777b538SAndroid Build Coastguard Worker _ListLocalesCommand, 1484*6777b538SAndroid Build Coastguard Worker _CheckGrdAndroidOutputsCommand, 1485*6777b538SAndroid Build Coastguard Worker _CheckGrdTranslationsCommand, 1486*6777b538SAndroid Build Coastguard Worker _CheckGnAndroidOutputsCommand, 1487*6777b538SAndroid Build Coastguard Worker _CheckAllCommand, 1488*6777b538SAndroid Build Coastguard Worker _UpdateExpectationsCommand, 1489*6777b538SAndroid Build Coastguard Worker _UnitTestsCommand, 1490*6777b538SAndroid Build Coastguard Worker] 1491*6777b538SAndroid Build Coastguard Worker 1492*6777b538SAndroid Build Coastguard Worker 1493*6777b538SAndroid Build Coastguard Workerdef main(argv): 1494*6777b538SAndroid Build Coastguard Worker parser = argparse.ArgumentParser( 1495*6777b538SAndroid Build Coastguard Worker description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 1496*6777b538SAndroid Build Coastguard Worker 1497*6777b538SAndroid Build Coastguard Worker subparsers = parser.add_subparsers() 1498*6777b538SAndroid Build Coastguard Worker commands = [clazz() for clazz in _COMMANDS] 1499*6777b538SAndroid Build Coastguard Worker for command in commands: 1500*6777b538SAndroid Build Coastguard Worker command.RegisterArgs(subparsers) 1501*6777b538SAndroid Build Coastguard Worker 1502*6777b538SAndroid Build Coastguard Worker if not argv: 1503*6777b538SAndroid Build Coastguard Worker argv = ['--help'] 1504*6777b538SAndroid Build Coastguard Worker 1505*6777b538SAndroid Build Coastguard Worker args = parser.parse_args(argv) 1506*6777b538SAndroid Build Coastguard Worker args.command.ProcessArgs(args) 1507*6777b538SAndroid Build Coastguard Worker args.command.Run() 1508*6777b538SAndroid Build Coastguard Worker 1509*6777b538SAndroid Build Coastguard Worker 1510*6777b538SAndroid Build Coastguard Workerif __name__ == "__main__": 1511*6777b538SAndroid Build Coastguard Worker main(sys.argv[1:]) 1512