1*6777b538SAndroid Build Coastguard Worker# Copyright 2023 The Chromium Authors 2*6777b538SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 3*6777b538SAndroid Build Coastguard Worker# found in the LICENSE file. 4*6777b538SAndroid Build Coastguard Worker"""Common logic needed by other modules.""" 5*6777b538SAndroid Build Coastguard Worker 6*6777b538SAndroid Build Coastguard Workerimport contextlib 7*6777b538SAndroid Build Coastguard Workerimport filecmp 8*6777b538SAndroid Build Coastguard Workerimport os 9*6777b538SAndroid Build Coastguard Workerimport shutil 10*6777b538SAndroid Build Coastguard Workerimport tempfile 11*6777b538SAndroid Build Coastguard Workerimport pathlib 12*6777b538SAndroid Build Coastguard Workerimport zipfile 13*6777b538SAndroid Build Coastguard Worker 14*6777b538SAndroid Build Coastguard Worker 15*6777b538SAndroid Build Coastguard Worker# Only some methods respect line length, so this is more of a best-effort 16*6777b538SAndroid Build Coastguard Worker# limit. 17*6777b538SAndroid Build Coastguard Worker_TARGET_LINE_LENGTH = 80 18*6777b538SAndroid Build Coastguard Worker 19*6777b538SAndroid Build Coastguard Worker 20*6777b538SAndroid Build Coastguard Workerclass StringBuilder: 21*6777b538SAndroid Build Coastguard Worker 22*6777b538SAndroid Build Coastguard Worker def __init__(self): 23*6777b538SAndroid Build Coastguard Worker self._sb = [] 24*6777b538SAndroid Build Coastguard Worker self._indent = 0 25*6777b538SAndroid Build Coastguard Worker self._start_of_line = True 26*6777b538SAndroid Build Coastguard Worker 27*6777b538SAndroid Build Coastguard Worker def __call__(self, value): 28*6777b538SAndroid Build Coastguard Worker lines = value.splitlines(keepends=True) 29*6777b538SAndroid Build Coastguard Worker for line in lines: 30*6777b538SAndroid Build Coastguard Worker if self._start_of_line and line != '\n': 31*6777b538SAndroid Build Coastguard Worker self._sb.append(' ' * self._indent) 32*6777b538SAndroid Build Coastguard Worker self._sb.append(line) 33*6777b538SAndroid Build Coastguard Worker self._start_of_line = line[-1] == '\n' 34*6777b538SAndroid Build Coastguard Worker 35*6777b538SAndroid Build Coastguard Worker def _cur_line_length(self): 36*6777b538SAndroid Build Coastguard Worker ret = 0 37*6777b538SAndroid Build Coastguard Worker for l in reversed(self._sb): 38*6777b538SAndroid Build Coastguard Worker if l.endswith('\n'): 39*6777b538SAndroid Build Coastguard Worker break 40*6777b538SAndroid Build Coastguard Worker ret += len(l) 41*6777b538SAndroid Build Coastguard Worker return ret 42*6777b538SAndroid Build Coastguard Worker 43*6777b538SAndroid Build Coastguard Worker @contextlib.contextmanager 44*6777b538SAndroid Build Coastguard Worker def _param_list_generator(self): 45*6777b538SAndroid Build Coastguard Worker values = [] 46*6777b538SAndroid Build Coastguard Worker yield values 47*6777b538SAndroid Build Coastguard Worker self.param_list(values) 48*6777b538SAndroid Build Coastguard Worker 49*6777b538SAndroid Build Coastguard Worker def param_list(self, values=None): 50*6777b538SAndroid Build Coastguard Worker if values is None: 51*6777b538SAndroid Build Coastguard Worker return self._param_list_generator() 52*6777b538SAndroid Build Coastguard Worker 53*6777b538SAndroid Build Coastguard Worker self('(') 54*6777b538SAndroid Build Coastguard Worker punctuation_size = 2 * len(values) # punctuation: ", ()" 55*6777b538SAndroid Build Coastguard Worker single_line_size = sum(len(v) for v in values) + punctuation_size 56*6777b538SAndroid Build Coastguard Worker if self._cur_line_length() + single_line_size < _TARGET_LINE_LENGTH: 57*6777b538SAndroid Build Coastguard Worker self(', '.join(values)) 58*6777b538SAndroid Build Coastguard Worker else: 59*6777b538SAndroid Build Coastguard Worker self('\n') 60*6777b538SAndroid Build Coastguard Worker with self.indent(4): 61*6777b538SAndroid Build Coastguard Worker self(',\n'.join(values)) 62*6777b538SAndroid Build Coastguard Worker self(')') 63*6777b538SAndroid Build Coastguard Worker 64*6777b538SAndroid Build Coastguard Worker @contextlib.contextmanager 65*6777b538SAndroid Build Coastguard Worker def statement(self): 66*6777b538SAndroid Build Coastguard Worker yield 67*6777b538SAndroid Build Coastguard Worker self(';\n') 68*6777b538SAndroid Build Coastguard Worker 69*6777b538SAndroid Build Coastguard Worker @contextlib.contextmanager 70*6777b538SAndroid Build Coastguard Worker def block(self): 71*6777b538SAndroid Build Coastguard Worker self(' {\n') 72*6777b538SAndroid Build Coastguard Worker with self.indent(2): 73*6777b538SAndroid Build Coastguard Worker yield 74*6777b538SAndroid Build Coastguard Worker self('}\n') 75*6777b538SAndroid Build Coastguard Worker 76*6777b538SAndroid Build Coastguard Worker @contextlib.contextmanager 77*6777b538SAndroid Build Coastguard Worker def indent(self, amount): 78*6777b538SAndroid Build Coastguard Worker self._indent += amount 79*6777b538SAndroid Build Coastguard Worker yield 80*6777b538SAndroid Build Coastguard Worker self._indent -= amount 81*6777b538SAndroid Build Coastguard Worker 82*6777b538SAndroid Build Coastguard Worker def to_string(self): 83*6777b538SAndroid Build Coastguard Worker return ''.join(self._sb) 84*6777b538SAndroid Build Coastguard Worker 85*6777b538SAndroid Build Coastguard Worker 86*6777b538SAndroid Build Coastguard Workerdef capitalize(value): 87*6777b538SAndroid Build Coastguard Worker return value[0].upper() + value[1:] 88*6777b538SAndroid Build Coastguard Worker 89*6777b538SAndroid Build Coastguard Worker 90*6777b538SAndroid Build Coastguard Workerdef escape_class_name(fully_qualified_class): 91*6777b538SAndroid Build Coastguard Worker """Returns an escaped string concatenating the Java package and class.""" 92*6777b538SAndroid Build Coastguard Worker escaped = fully_qualified_class.replace('_', '_1') 93*6777b538SAndroid Build Coastguard Worker return escaped.replace('/', '_').replace('$', '_00024') 94*6777b538SAndroid Build Coastguard Worker 95*6777b538SAndroid Build Coastguard Worker 96*6777b538SAndroid Build Coastguard Worker@contextlib.contextmanager 97*6777b538SAndroid Build Coastguard Workerdef atomic_output(path, mode='w+b'): 98*6777b538SAndroid Build Coastguard Worker with tempfile.NamedTemporaryFile(mode, delete=False) as f: 99*6777b538SAndroid Build Coastguard Worker try: 100*6777b538SAndroid Build Coastguard Worker yield f 101*6777b538SAndroid Build Coastguard Worker finally: 102*6777b538SAndroid Build Coastguard Worker f.close() 103*6777b538SAndroid Build Coastguard Worker 104*6777b538SAndroid Build Coastguard Worker if not (os.path.exists(path) and filecmp.cmp(f.name, path)): 105*6777b538SAndroid Build Coastguard Worker pathlib.Path(path).parents[0].mkdir(parents=True, exist_ok=True) 106*6777b538SAndroid Build Coastguard Worker shutil.move(f.name, path) 107*6777b538SAndroid Build Coastguard Worker if os.path.exists(f.name): 108*6777b538SAndroid Build Coastguard Worker os.unlink(f.name) 109*6777b538SAndroid Build Coastguard Worker 110*6777b538SAndroid Build Coastguard Worker 111*6777b538SAndroid Build Coastguard Workerdef add_to_zip_hermetic(zip_file, zip_path, data=None): 112*6777b538SAndroid Build Coastguard Worker zipinfo = zipfile.ZipInfo(filename=zip_path) 113*6777b538SAndroid Build Coastguard Worker zipinfo.external_attr = 0o644 << 16 114*6777b538SAndroid Build Coastguard Worker zipinfo.date_time = (2001, 1, 1, 0, 0, 0) 115*6777b538SAndroid Build Coastguard Worker zip_file.writestr(zipinfo, data, zipfile.ZIP_STORED) 116