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