diff options
author | Daniel Mensinger <daniel@mensinger-ka.de> | 2020-08-29 21:23:43 +0200 |
---|---|---|
committer | Daniel Mensinger <daniel@mensinger-ka.de> | 2020-09-08 20:15:56 +0200 |
commit | a4f4379c44c7f13bc9e44bc01504077af1f3a338 (patch) | |
tree | 6f969b023a4311c7bad7b1dbdd61fa845cadfef3 /mesonbuild/scripts | |
parent | 0d57e307b2fea541a9ee368873431fe224e5c982 (diff) | |
download | meson-a4f4379c44c7f13bc9e44bc01504077af1f3a338.zip meson-a4f4379c44c7f13bc9e44bc01504077af1f3a338.tar.gz meson-a4f4379c44c7f13bc9e44bc01504077af1f3a338.tar.bz2 |
typing: fully annotate scripts
Diffstat (limited to 'mesonbuild/scripts')
-rw-r--r-- | mesonbuild/scripts/__init__.py | 3 | ||||
-rw-r--r-- | mesonbuild/scripts/clangformat.py | 5 | ||||
-rw-r--r-- | mesonbuild/scripts/clangtidy.py | 8 | ||||
-rw-r--r-- | mesonbuild/scripts/cleantrees.py | 5 | ||||
-rwxr-xr-x | mesonbuild/scripts/cmake_run_ctgt.py | 44 | ||||
-rw-r--r-- | mesonbuild/scripts/commandrunner.py | 15 | ||||
-rw-r--r-- | mesonbuild/scripts/coverage.py | 5 | ||||
-rw-r--r-- | mesonbuild/scripts/delwithsuffix.py | 3 | ||||
-rw-r--r-- | mesonbuild/scripts/depfixer.py | 78 | ||||
-rw-r--r-- | mesonbuild/scripts/dirchanger.py | 3 | ||||
-rw-r--r-- | mesonbuild/scripts/gettext.py | 14 | ||||
-rw-r--r-- | mesonbuild/scripts/gtkdochelper.py | 19 | ||||
-rw-r--r-- | mesonbuild/scripts/hotdochelper.py | 6 | ||||
-rw-r--r-- | mesonbuild/scripts/meson_exe.py | 7 | ||||
-rw-r--r-- | mesonbuild/scripts/msgfmthelper.py | 3 | ||||
-rw-r--r-- | mesonbuild/scripts/regen_checker.py | 20 | ||||
-rw-r--r-- | mesonbuild/scripts/scanbuild.py | 23 | ||||
-rw-r--r-- | mesonbuild/scripts/symbolextractor.py | 34 | ||||
-rw-r--r-- | mesonbuild/scripts/tags.py | 14 | ||||
-rw-r--r-- | mesonbuild/scripts/uninstall.py | 5 | ||||
-rw-r--r-- | mesonbuild/scripts/vcstagger.py | 6 | ||||
-rw-r--r-- | mesonbuild/scripts/yelphelper.py | 14 |
22 files changed, 182 insertions, 152 deletions
diff --git a/mesonbuild/scripts/__init__.py b/mesonbuild/scripts/__init__.py index af6bedc..2edbe88 100644 --- a/mesonbuild/scripts/__init__.py +++ b/mesonbuild/scripts/__init__.py @@ -12,7 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -def destdir_join(d1, d2): +# TODO: consider switching to pathlib for this +def destdir_join(d1: str, d2: str) -> str: # c:\destdir + c:\prefix must produce c:\destdir\prefix if len(d1) > 1 and d1[1] == ':' \ and len(d2) > 1 and d2[1] == ':': diff --git a/mesonbuild/scripts/clangformat.py b/mesonbuild/scripts/clangformat.py index 4b441de..19ea5ec 100644 --- a/mesonbuild/scripts/clangformat.py +++ b/mesonbuild/scripts/clangformat.py @@ -18,8 +18,9 @@ from concurrent.futures import ThreadPoolExecutor from ..environment import detect_clangformat from ..compilers import lang_suffixes +import typing as T -def clangformat(exelist, srcdir_name, builddir_name): +def clangformat(exelist: T.List[str], srcdir_name: str, builddir_name: str) -> int: srcdir = pathlib.Path(srcdir_name) suffixes = set(lang_suffixes['c']).union(set(lang_suffixes['cpp'])) suffixes.add('h') @@ -33,7 +34,7 @@ def clangformat(exelist, srcdir_name, builddir_name): [x.result() for x in futures] return 0 -def run(args): +def run(args: T.List[str]) -> int: srcdir_name = args[0] builddir_name = args[1] diff --git a/mesonbuild/scripts/clangtidy.py b/mesonbuild/scripts/clangtidy.py index 0452086..11174ea 100644 --- a/mesonbuild/scripts/clangtidy.py +++ b/mesonbuild/scripts/clangtidy.py @@ -16,10 +16,11 @@ import pathlib import subprocess import shutil from concurrent.futures import ThreadPoolExecutor +import typing as T from ..compilers import lang_suffixes -def manual_clangformat(srcdir_name, builddir_name): +def manual_clangformat(srcdir_name: str, builddir_name: str) -> int: srcdir = pathlib.Path(srcdir_name) suffixes = set(lang_suffixes['c']).union(set(lang_suffixes['cpp'])) suffixes.add('h') @@ -34,7 +35,7 @@ def manual_clangformat(srcdir_name, builddir_name): [max(returncode, x.result().returncode) for x in futures] return returncode -def clangformat(srcdir_name, builddir_name): +def clangformat(srcdir_name: str, builddir_name: str) -> int: run_clang_tidy = None for rct in ('run-clang-tidy', 'run-clang-tidy.py'): if shutil.which(rct): @@ -45,8 +46,9 @@ def clangformat(srcdir_name, builddir_name): else: print('Could not find run-clang-tidy, running checks manually.') manual_clangformat(srcdir_name, builddir_name) + return 0 -def run(args): +def run(args: T.List[str]) -> int: srcdir_name = args[0] builddir_name = args[1] return clangformat(srcdir_name, builddir_name) diff --git a/mesonbuild/scripts/cleantrees.py b/mesonbuild/scripts/cleantrees.py index 0af8dd0..6feb9a7 100644 --- a/mesonbuild/scripts/cleantrees.py +++ b/mesonbuild/scripts/cleantrees.py @@ -16,8 +16,9 @@ import os import sys import shutil import pickle +import typing as T -def rmtrees(build_dir, trees): +def rmtrees(build_dir: str, trees: T.List[str]) -> None: for t in trees: # Never delete trees outside of the builddir if os.path.isabs(t): @@ -28,7 +29,7 @@ def rmtrees(build_dir, trees): if os.path.isdir(bt): shutil.rmtree(bt, ignore_errors=True) -def run(args): +def run(args: T.List[str]) -> int: if len(args) != 1: print('Cleaner script for Meson. Do not run on your own please.') print('cleantrees.py <data-file>') diff --git a/mesonbuild/scripts/cmake_run_ctgt.py b/mesonbuild/scripts/cmake_run_ctgt.py index 5c0b31f..2d9aeca 100755 --- a/mesonbuild/scripts/cmake_run_ctgt.py +++ b/mesonbuild/scripts/cmake_run_ctgt.py @@ -3,12 +3,12 @@ import argparse import subprocess import shutil -import os import sys from pathlib import Path +import typing as T -def run(argsv): - commands = [[]] +def run(argsv: T.List[str]) -> int: + commands = [[]] # type: T.List[T.List[str]] SEPARATOR = ';;;' # Generate CMD parameters @@ -20,13 +20,14 @@ def run(argsv): # Parse args = parser.parse_args(argsv) + directory = Path(args.directory) dummy_target = None if len(args.outputs) == 1 and len(args.original_outputs) == 0: - dummy_target = args.outputs[0] + dummy_target = Path(args.outputs[0]) elif len(args.outputs) != len(args.original_outputs): print('Length of output list and original output list differ') - sys.exit(1) + return 1 for i in args.commands: if i == SEPARATOR: @@ -62,39 +63,40 @@ def run(argsv): cmd += [j] try: - os.makedirs(args.directory, exist_ok=True) + directory.mkdir(parents=True, exist_ok=True) - res = subprocess.run(cmd, stdout=stdout, stderr=stderr, cwd=args.directory, check=True) + res = subprocess.run(cmd, stdout=stdout, stderr=stderr, cwd=str(directory), check=True) if capture_file: - out_file = Path(args.directory) / capture_file + out_file = directory / capture_file out_file.write_bytes(res.stdout) except subprocess.CalledProcessError: - sys.exit(1) + return 1 if dummy_target: - with open(dummy_target, 'a'): - os.utime(dummy_target, None) - sys.exit(0) + dummy_target.touch() + return 0 # Copy outputs - zipped_outputs = zip(args.outputs, args.original_outputs) + zipped_outputs = zip([Path(x) for x in args.outputs], [Path(x) for x in args.original_outputs]) for expected, generated in zipped_outputs: do_copy = False - if not os.path.exists(expected): - if not os.path.exists(generated): + if not expected.exists(): + if not generated.exists(): print('Unable to find generated file. This can cause the build to fail:') print(generated) do_copy = False else: do_copy = True - elif os.path.exists(generated): - if os.path.getmtime(generated) > os.path.getmtime(expected): + elif generated.exists(): + if generated.stat().st_mtime > expected.stat().st_mtime: do_copy = True if do_copy: - if os.path.exists(expected): - os.remove(expected) - shutil.copyfile(generated, expected) + if expected.exists(): + expected.unlink() + shutil.copyfile(str(generated), str(expected)) + + return 0 if __name__ == '__main__': - sys.run(sys.argv[1:]) + sys.exit(run(sys.argv[1:])) diff --git a/mesonbuild/scripts/commandrunner.py b/mesonbuild/scripts/commandrunner.py index 22da417..aeeaa3b 100644 --- a/mesonbuild/scripts/commandrunner.py +++ b/mesonbuild/scripts/commandrunner.py @@ -17,8 +17,9 @@ what to run, sets up the environment and executes the command.""" import sys, os, subprocess, shutil, shlex import re +import typing as T -def run_command(source_dir, build_dir, subdir, meson_command, command, arguments): +def run_command(source_dir: str, build_dir: str, subdir: str, meson_command: T.List[str], command: str, arguments: T.List[str]) -> subprocess.Popen: env = {'MESON_SOURCE_ROOT': source_dir, 'MESON_BUILD_ROOT': build_dir, 'MESON_SUBDIR': subdir, @@ -50,24 +51,24 @@ def run_command(source_dir, build_dir, subdir, meson_command, command, arguments print('Could not execute command "{}": {}'.format(command, err)) sys.exit(1) -def is_python_command(cmdname): +def is_python_command(cmdname: str) -> bool: end_py_regex = r'python(3|3\.\d+)?(\.exe)?$' return re.search(end_py_regex, cmdname) is not None -def run(args): +def run(args: T.List[str]) -> int: if len(args) < 4: print('commandrunner.py <source dir> <build dir> <subdir> <command> [arguments]') return 1 src_dir = args[0] build_dir = args[1] subdir = args[2] - meson_command = args[3] - if is_python_command(meson_command): - meson_command = [meson_command, args[4]] + meson_bin = args[3] + if is_python_command(meson_bin): + meson_command = [meson_bin, args[4]] command = args[5] arguments = args[6:] else: - meson_command = [meson_command] + meson_command = [meson_bin] command = args[4] arguments = args[5:] pc = run_command(src_dir, build_dir, subdir, meson_command, command, arguments) diff --git a/mesonbuild/scripts/coverage.py b/mesonbuild/scripts/coverage.py index 7231972..80e9052 100644 --- a/mesonbuild/scripts/coverage.py +++ b/mesonbuild/scripts/coverage.py @@ -15,8 +15,9 @@ from mesonbuild import environment, mesonlib import argparse, sys, os, subprocess, pathlib, stat +import typing as T -def coverage(outputs, source_root, subproject_root, build_root, log_dir, use_llvm_cov): +def coverage(outputs: T.List[str], source_root: str, subproject_root: str, build_root: str, log_dir: str, use_llvm_cov: bool) -> int: outfiles = [] exitcode = 0 @@ -146,7 +147,7 @@ def coverage(outputs, source_root, subproject_root, build_root, log_dir, use_llv return exitcode -def run(args): +def run(args: T.List[str]) -> int: if not os.path.isfile('build.ninja'): print('Coverage currently only works with the Ninja backend.') return 1 diff --git a/mesonbuild/scripts/delwithsuffix.py b/mesonbuild/scripts/delwithsuffix.py index 0d410ae..873db0d 100644 --- a/mesonbuild/scripts/delwithsuffix.py +++ b/mesonbuild/scripts/delwithsuffix.py @@ -13,8 +13,9 @@ # limitations under the License. import os, sys +import typing as T -def run(args): +def run(args: T.List[str]) -> int: if len(args) != 2: print('delwithsuffix.py <root of subdir to process> <suffix to delete>') sys.exit(1) diff --git a/mesonbuild/scripts/depfixer.py b/mesonbuild/scripts/depfixer.py index f927693..a64caca 100644 --- a/mesonbuild/scripts/depfixer.py +++ b/mesonbuild/scripts/depfixer.py @@ -15,6 +15,7 @@ import sys, struct import shutil, subprocess +import typing as T from ..mesonlib import OrderedSet @@ -30,7 +31,7 @@ DT_MIPS_RLD_MAP_REL = 1879048245 INSTALL_NAME_TOOL = False class DataSizes: - def __init__(self, ptrsize, is_le): + def __init__(self, ptrsize: int, is_le: bool) -> None: if is_le: p = '<' else: @@ -57,7 +58,7 @@ class DataSizes: self.OffSize = 4 class DynamicEntry(DataSizes): - def __init__(self, ifile, ptrsize, is_le): + def __init__(self, ifile: T.BinaryIO, ptrsize: int, is_le: bool) -> None: super().__init__(ptrsize, is_le) self.ptrsize = ptrsize if ptrsize == 64: @@ -67,7 +68,7 @@ class DynamicEntry(DataSizes): self.d_tag = struct.unpack(self.Sword, ifile.read(self.SwordSize))[0] self.val = struct.unpack(self.Word, ifile.read(self.WordSize))[0] - def write(self, ofile): + def write(self, ofile: T.BinaryIO) -> None: if self.ptrsize == 64: ofile.write(struct.pack(self.Sxword, self.d_tag)) ofile.write(struct.pack(self.XWord, self.val)) @@ -76,7 +77,7 @@ class DynamicEntry(DataSizes): ofile.write(struct.pack(self.Word, self.val)) class SectionHeader(DataSizes): - def __init__(self, ifile, ptrsize, is_le): + def __init__(self, ifile: T.BinaryIO, ptrsize: int, is_le: bool) -> None: super().__init__(ptrsize, is_le) if ptrsize == 64: is_64 = True @@ -116,10 +117,12 @@ class SectionHeader(DataSizes): self.sh_entsize = struct.unpack(self.Word, ifile.read(self.WordSize))[0] class Elf(DataSizes): - def __init__(self, bfile, verbose=True): + def __init__(self, bfile: str, verbose: bool = True) -> None: self.bfile = bfile self.verbose = verbose self.bf = open(bfile, 'r+b') + self.sections = [] # type: T.List[SectionHeader] + self.dynamic = [] # type: T.List[DynamicEntry] try: (self.ptrsize, self.is_le) = self.detect_elf_type() super().__init__(self.ptrsize, self.is_le) @@ -130,18 +133,18 @@ class Elf(DataSizes): self.bf.close() raise - def __enter__(self): + def __enter__(self) -> 'Elf': return self - def __del__(self): + def __del__(self) -> None: if self.bf: self.bf.close() - def __exit__(self, exc_type, exc_value, traceback): + def __exit__(self, exc_type: T.Any, exc_value: T.Any, traceback: T.Any) -> None: self.bf.close() self.bf = None - def detect_elf_type(self): + def detect_elf_type(self) -> T.Tuple[int, bool]: data = self.bf.read(6) if data[1:4] != b'ELF': # This script gets called to non-elf targets too @@ -163,7 +166,7 @@ class Elf(DataSizes): sys.exit('File "%s" has unknown ELF endianness.' % self.bfile) return ptrsize, is_le - def parse_header(self): + def parse_header(self) -> None: self.bf.seek(0) self.e_ident = struct.unpack('16s', self.bf.read(16))[0] self.e_type = struct.unpack(self.Half, self.bf.read(self.HalfSize))[0] @@ -180,13 +183,12 @@ class Elf(DataSizes): self.e_shnum = struct.unpack(self.Half, self.bf.read(self.HalfSize))[0] self.e_shstrndx = struct.unpack(self.Half, self.bf.read(self.HalfSize))[0] - def parse_sections(self): + def parse_sections(self) -> None: self.bf.seek(self.e_shoff) - self.sections = [] for _ in range(self.e_shnum): self.sections.append(SectionHeader(self.bf, self.ptrsize, self.is_le)) - def read_str(self): + def read_str(self) -> bytes: arr = [] x = self.bf.read(1) while x != b'\0': @@ -196,17 +198,17 @@ class Elf(DataSizes): raise RuntimeError('Tried to read past the end of the file') return b''.join(arr) - def find_section(self, target_name): + def find_section(self, target_name: bytes) -> T.Optional[SectionHeader]: section_names = self.sections[self.e_shstrndx] for i in self.sections: self.bf.seek(section_names.sh_offset + i.sh_name) name = self.read_str() if name == target_name: return i + return None - def parse_dynamic(self): + def parse_dynamic(self) -> None: sec = self.find_section(b'.dynamic') - self.dynamic = [] if sec is None: return self.bf.seek(sec.sh_offset) @@ -216,14 +218,14 @@ class Elf(DataSizes): if e.d_tag == 0: break - def print_section_names(self): + def print_section_names(self) -> None: section_names = self.sections[self.e_shstrndx] for i in self.sections: self.bf.seek(section_names.sh_offset + i.sh_name) name = self.read_str() print(name.decode()) - def print_soname(self): + def print_soname(self) -> None: soname = None strtab = None for i in self.dynamic: @@ -237,14 +239,16 @@ class Elf(DataSizes): self.bf.seek(strtab.val + soname.val) print(self.read_str()) - def get_entry_offset(self, entrynum): + def get_entry_offset(self, entrynum: int) -> T.Optional[int]: sec = self.find_section(b'.dynstr') for i in self.dynamic: if i.d_tag == entrynum: - return sec.sh_offset + i.val + res = sec.sh_offset + i.val + assert isinstance(res, int) + return res return None - def print_rpath(self): + def print_rpath(self) -> None: offset = self.get_entry_offset(DT_RPATH) if offset is None: print("This file does not have an rpath.") @@ -252,7 +256,7 @@ class Elf(DataSizes): self.bf.seek(offset) print(self.read_str()) - def print_runpath(self): + def print_runpath(self) -> None: offset = self.get_entry_offset(DT_RUNPATH) if offset is None: print("This file does not have a runpath.") @@ -260,7 +264,7 @@ class Elf(DataSizes): self.bf.seek(offset) print(self.read_str()) - def print_deps(self): + def print_deps(self) -> None: sec = self.find_section(b'.dynstr') deps = [] for i in self.dynamic: @@ -272,7 +276,7 @@ class Elf(DataSizes): name = self.read_str() print(name) - def fix_deps(self, prefix): + def fix_deps(self, prefix: bytes) -> None: sec = self.find_section(b'.dynstr') deps = [] for i in self.dynamic: @@ -290,15 +294,13 @@ class Elf(DataSizes): self.bf.seek(offset) self.bf.write(newname) - def fix_rpath(self, rpath_dirs_to_remove, new_rpath): + def fix_rpath(self, rpath_dirs_to_remove: T.List[bytes], new_rpath: bytes) -> None: # The path to search for can be either rpath or runpath. # Fix both of them to be sure. self.fix_rpathtype_entry(rpath_dirs_to_remove, new_rpath, DT_RPATH) self.fix_rpathtype_entry(rpath_dirs_to_remove, new_rpath, DT_RUNPATH) - def fix_rpathtype_entry(self, rpath_dirs_to_remove, new_rpath, entrynum): - if isinstance(new_rpath, str): - new_rpath = new_rpath.encode('utf8') + def fix_rpathtype_entry(self, rpath_dirs_to_remove: T.List[bytes], new_rpath: bytes, entrynum: int) -> None: rp_off = self.get_entry_offset(entrynum) if rp_off is None: if self.verbose: @@ -326,7 +328,7 @@ class Elf(DataSizes): new_rpath = b':'.join(new_rpaths) if len(old_rpath) < len(new_rpath): - msg = "New rpath must not be longer than the old one.\n Old: {}\n New: {}".format(old_rpath, new_rpath) + msg = "New rpath must not be longer than the old one.\n Old: {!r}\n New: {!r}".format(old_rpath, new_rpath) sys.exit(msg) # The linker does read-only string deduplication. If there is a # string that shares a suffix with the rpath, they might get @@ -343,7 +345,7 @@ class Elf(DataSizes): self.bf.write(new_rpath) self.bf.write(b'\0') - def remove_rpath_entry(self, entrynum): + def remove_rpath_entry(self, entrynum: int) -> None: sec = self.find_section(b'.dynamic') if sec is None: return None @@ -363,7 +365,7 @@ class Elf(DataSizes): entry.write(self.bf) return None -def fix_elf(fname, rpath_dirs_to_remove, new_rpath, verbose=True): +def fix_elf(fname: str, rpath_dirs_to_remove: T.List[bytes], new_rpath: T.Optional[bytes], verbose: bool = True) -> None: with Elf(fname, verbose) as e: if new_rpath is None: e.print_rpath() @@ -371,7 +373,7 @@ def fix_elf(fname, rpath_dirs_to_remove, new_rpath, verbose=True): else: e.fix_rpath(rpath_dirs_to_remove, new_rpath) -def get_darwin_rpaths_to_remove(fname): +def get_darwin_rpaths_to_remove(fname: str) -> T.List[str]: out = subprocess.check_output(['otool', '-l', fname], universal_newlines=True, stderr=subprocess.DEVNULL) @@ -389,7 +391,7 @@ def get_darwin_rpaths_to_remove(fname): result.append(rp) return result -def fix_darwin(fname, new_rpath, final_path, install_name_mappings): +def fix_darwin(fname: str, new_rpath: str, final_path: str, install_name_mappings: T.Dict[str, str]) -> None: try: rpaths = get_darwin_rpaths_to_remove(fname) except subprocess.CalledProcessError: @@ -439,7 +441,7 @@ def fix_darwin(fname, new_rpath, final_path, install_name_mappings): except Exception as err: raise SystemExit(err) -def fix_jar(fname): +def fix_jar(fname: str) -> None: subprocess.check_call(['jar', 'xfv', fname, 'META-INF/MANIFEST.MF']) with open('META-INF/MANIFEST.MF', 'r+') as f: lines = f.readlines() @@ -450,7 +452,7 @@ def fix_jar(fname): f.truncate() subprocess.check_call(['jar', 'ufm', fname, 'META-INF/MANIFEST.MF']) -def fix_rpath(fname, rpath_dirs_to_remove, new_rpath, final_path, install_name_mappings, verbose=True): +def fix_rpath(fname: str, rpath_dirs_to_remove: T.List[bytes], new_rpath: T.Union[str, bytes], final_path: str, install_name_mappings: T.Dict[str, str], verbose: bool = True) -> None: global INSTALL_NAME_TOOL # Static libraries, import libraries, debug information, headers, etc # never have rpaths @@ -461,6 +463,8 @@ def fix_rpath(fname, rpath_dirs_to_remove, new_rpath, final_path, install_name_m if fname.endswith('.jar'): fix_jar(fname) return + if isinstance(new_rpath, str): + new_rpath = new_rpath.encode('utf8') fix_elf(fname, rpath_dirs_to_remove, new_rpath, verbose) return except SystemExit as e: @@ -473,6 +477,8 @@ def fix_rpath(fname, rpath_dirs_to_remove, new_rpath, final_path, install_name_m # (upto 30ms), which is significant with --only-changed. For details, see: # https://github.com/mesonbuild/meson/pull/6612#discussion_r378581401 if INSTALL_NAME_TOOL is False: - INSTALL_NAME_TOOL = shutil.which('install_name_tool') + INSTALL_NAME_TOOL = bool(shutil.which('install_name_tool')) if INSTALL_NAME_TOOL: + if isinstance(new_rpath, bytes): + new_rpath = new_rpath.decode('utf8') fix_darwin(fname, new_rpath, final_path, install_name_mappings) diff --git a/mesonbuild/scripts/dirchanger.py b/mesonbuild/scripts/dirchanger.py index 3d7f4e2..21632cd 100644 --- a/mesonbuild/scripts/dirchanger.py +++ b/mesonbuild/scripts/dirchanger.py @@ -16,8 +16,9 @@ the command given in the rest of the arguments.''' import os, subprocess, sys +import typing as T -def run(args): +def run(args: T.List[str]) -> int: dirname = args[0] command = args[1:] diff --git a/mesonbuild/scripts/gettext.py b/mesonbuild/scripts/gettext.py index 7042863..547d14f 100644 --- a/mesonbuild/scripts/gettext.py +++ b/mesonbuild/scripts/gettext.py @@ -17,6 +17,7 @@ import shutil import argparse import subprocess from . import destdir_join +import typing as T parser = argparse.ArgumentParser() parser.add_argument('command') @@ -27,7 +28,7 @@ parser.add_argument('--localedir', default='') parser.add_argument('--subdir', default='') parser.add_argument('--extra-args', default='') -def read_linguas(src_sub): +def read_linguas(src_sub: str) -> T.List[str]: # Syntax of this file is documented here: # https://www.gnu.org/software/gettext/manual/html_node/po_002fLINGUAS.html linguas = os.path.join(src_sub, 'LINGUAS') @@ -43,7 +44,7 @@ def read_linguas(src_sub): print('Could not find file LINGUAS in {}'.format(src_sub)) return [] -def run_potgen(src_sub, pkgname, datadirs, args): +def run_potgen(src_sub: str, pkgname: str, datadirs: str, args: T.List[str]) -> int: listfile = os.path.join(src_sub, 'POTFILES.in') if not os.path.exists(listfile): listfile = os.path.join(src_sub, 'POTFILES') @@ -60,13 +61,13 @@ def run_potgen(src_sub, pkgname, datadirs, args): '-D', os.environ['MESON_SOURCE_ROOT'], '-k_', '-o', ofile] + args, env=child_env) -def gen_gmo(src_sub, bld_sub, langs): +def gen_gmo(src_sub: str, bld_sub: str, langs: T.List[str]) -> int: for l in langs: subprocess.check_call(['msgfmt', os.path.join(src_sub, l + '.po'), '-o', os.path.join(bld_sub, l + '.gmo')]) return 0 -def update_po(src_sub, pkgname, langs): +def update_po(src_sub: str, pkgname: str, langs: T.List[str]) -> int: potfile = os.path.join(src_sub, pkgname + '.pot') for l in langs: pofile = os.path.join(src_sub, l + '.po') @@ -76,7 +77,7 @@ def update_po(src_sub, pkgname, langs): subprocess.check_call(['msginit', '--input', potfile, '--output-file', pofile, '--locale', l, '--no-translator']) return 0 -def do_install(src_sub, bld_sub, dest, pkgname, langs): +def do_install(src_sub: str, bld_sub: str, dest: str, pkgname: str, langs: T.List[str]) -> int: for l in langs: srcfile = os.path.join(bld_sub, l + '.gmo') outfile = os.path.join(dest, l, 'LC_MESSAGES', @@ -88,7 +89,7 @@ def do_install(src_sub, bld_sub, dest, pkgname, langs): print('Installing %s to %s' % (srcfile, outfile)) return 0 -def run(args): +def run(args: T.List[str]) -> int: options = parser.parse_args(args) subcmd = options.command langs = options.langs.split('@@') if options.langs else None @@ -120,3 +121,4 @@ def run(args): else: print('Unknown subcommand.') return 1 + return 0 diff --git a/mesonbuild/scripts/gtkdochelper.py b/mesonbuild/scripts/gtkdochelper.py index 812604a..86949e5 100644 --- a/mesonbuild/scripts/gtkdochelper.py +++ b/mesonbuild/scripts/gtkdochelper.py @@ -18,6 +18,7 @@ import shutil import argparse from ..mesonlib import MesonException, Popen_safe, is_windows, is_cygwin, split_args from . import destdir_join +import typing as T parser = argparse.ArgumentParser() @@ -50,7 +51,7 @@ for tool in ['scan', 'scangobj', 'mkdb', 'mkhtml', 'fixxref']: program_name = 'gtkdoc-' + tool parser.add_argument('--' + program_name, dest=program_name.replace('-', '_')) -def gtkdoc_run_check(cmd, cwd, library_paths=None): +def gtkdoc_run_check(cmd: T.List[str], cwd: str, library_paths: T.Optional[T.List[str]] = None) -> None: if library_paths is None: library_paths = [] @@ -85,12 +86,12 @@ def gtkdoc_run_check(cmd, cwd, library_paths=None): except UnicodeEncodeError: pass -def build_gtkdoc(source_root, build_root, doc_subdir, src_subdirs, - main_file, module, module_version, - html_args, scan_args, fixxref_args, mkdb_args, - gobject_typesfile, scanobjs_args, run, ld, cc, ldflags, cflags, - html_assets, content_files, ignore_headers, namespace, - expand_content_files, mode, options): +def build_gtkdoc(source_root: str, build_root: str, doc_subdir: str, src_subdirs: T.List[str], + main_file: str, module: str, module_version: str, + html_args: T.List[str], scan_args: T.List[str], fixxref_args: T.List[str], mkdb_args: T.List[str], + gobject_typesfile: str, scanobjs_args: T.List[str], run: str, ld: str, cc: str, ldflags: str, cflags: str, + html_assets: T.List[str], content_files: T.List[str], ignore_headers: T.List[str], namespace: str, + expand_content_files: T.List[str], mode: str, options: argparse.Namespace) -> None: print("Building documentation for %s" % module) src_dir_args = [] @@ -217,13 +218,13 @@ def build_gtkdoc(source_root, build_root, doc_subdir, src_subdirs, shutil.move(os.path.join(htmldir, '{}.devhelp2'.format(module)), os.path.join(htmldir, '{}-{}.devhelp2'.format(module, module_version))) -def install_gtkdoc(build_root, doc_subdir, install_prefix, datadir, module): +def install_gtkdoc(build_root: str, doc_subdir: str, install_prefix: str, datadir: str, module: str) -> None: source = os.path.join(build_root, doc_subdir, 'html') final_destination = os.path.join(install_prefix, datadir, module) shutil.rmtree(final_destination, ignore_errors=True) shutil.copytree(source, final_destination) -def run(args): +def run(args: T.List[str]) -> int: options = parser.parse_args(args) if options.htmlargs: htmlargs = options.htmlargs.split('@@') diff --git a/mesonbuild/scripts/hotdochelper.py b/mesonbuild/scripts/hotdochelper.py index 826745d..a96a34a 100644 --- a/mesonbuild/scripts/hotdochelper.py +++ b/mesonbuild/scripts/hotdochelper.py @@ -5,6 +5,7 @@ import subprocess from . import destdir_join import argparse +import typing as T parser = argparse.ArgumentParser() parser.add_argument('--install') @@ -14,7 +15,7 @@ parser.add_argument('--builddir') parser.add_argument('--project-version') -def run(argv): +def run(argv: T.List[str]) -> int: options, args = parser.parse_known_args(argv) subenv = os.environ.copy() @@ -23,7 +24,7 @@ def run(argv): res = subprocess.call(args, cwd=options.builddir, env=subenv) if res != 0: - exit(res) + return res if options.install: source_dir = os.path.join(options.builddir, options.install) @@ -34,3 +35,4 @@ def run(argv): shutil.rmtree(installdir, ignore_errors=True) shutil.copytree(source_dir, installdir) + return 0 diff --git a/mesonbuild/scripts/meson_exe.py b/mesonbuild/scripts/meson_exe.py index f1b0299..68ba120 100644 --- a/mesonbuild/scripts/meson_exe.py +++ b/mesonbuild/scripts/meson_exe.py @@ -17,19 +17,20 @@ import sys import argparse import pickle import subprocess +import typing as T from .. import mesonlib from ..backend.backends import ExecutableSerialisation options = None -def buildparser(): +def buildparser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser(description='Custom executable wrapper for Meson. Do not run on your own, mmm\'kay?') parser.add_argument('--unpickle') parser.add_argument('--capture') return parser -def run_exe(exe): +def run_exe(exe: ExecutableSerialisation) -> int: if exe.exe_runner: if not exe.exe_runner.found(): raise AssertionError('BUG: Can\'t run cross-compiled exe {!r} with not-found ' @@ -74,7 +75,7 @@ def run_exe(exe): sys.stderr.buffer.write(stderr) return p.returncode -def run(args): +def run(args: T.List[str]) -> int: global options parser = buildparser() options, cmd_args = parser.parse_known_args(args) diff --git a/mesonbuild/scripts/msgfmthelper.py b/mesonbuild/scripts/msgfmthelper.py index 737f1bc..3ddc9e6 100644 --- a/mesonbuild/scripts/msgfmthelper.py +++ b/mesonbuild/scripts/msgfmthelper.py @@ -15,6 +15,7 @@ import argparse import subprocess import os +import typing as T parser = argparse.ArgumentParser() parser.add_argument('input') @@ -25,7 +26,7 @@ parser.add_argument('--datadirs', default='') parser.add_argument('args', default=[], metavar='extra msgfmt argument', nargs='*') -def run(args): +def run(args: T.List[str]) -> int: options = parser.parse_args(args) env = None if options.datadirs: diff --git a/mesonbuild/scripts/regen_checker.py b/mesonbuild/scripts/regen_checker.py index 80d9242..84f7d77 100644 --- a/mesonbuild/scripts/regen_checker.py +++ b/mesonbuild/scripts/regen_checker.py @@ -14,10 +14,14 @@ import sys, os import pickle, subprocess +import typing as T + +if T.TYPE_CHECKING: + from ..backend.vs2010backend import RegenInfo # This could also be used for XCode. -def need_regen(regeninfo, regen_timestamp): +def need_regen(regeninfo: 'RegenInfo', regen_timestamp: float) -> bool: for i in regeninfo.depfiles: curfile = os.path.join(regeninfo.build_dir, i) curtime = os.stat(curfile).st_mtime @@ -31,7 +35,7 @@ def need_regen(regeninfo, regen_timestamp): Vs2010Backend.touch_regen_timestamp(regeninfo.build_dir) return False -def regen(regeninfo, meson_command, backend): +def regen(regeninfo: 'RegenInfo', meson_command: T.List[str], backend: str) -> None: cmd = meson_command + ['--internal', 'regenerate', regeninfo.build_dir, @@ -39,19 +43,19 @@ def regen(regeninfo, meson_command, backend): '--backend=' + backend] subprocess.check_call(cmd) -def run(args): +def run(args: T.List[str]) -> int: private_dir = args[0] dumpfile = os.path.join(private_dir, 'regeninfo.dump') - coredata = os.path.join(private_dir, 'coredata.dat') + coredata_file = os.path.join(private_dir, 'coredata.dat') with open(dumpfile, 'rb') as f: - regeninfo = pickle.load(f) - with open(coredata, 'rb') as f: + regeninfo = T.cast('RegenInfo', pickle.load(f)) + with open(coredata_file, 'rb') as f: coredata = pickle.load(f) backend = coredata.get_builtin_option('backend') regen_timestamp = os.stat(dumpfile).st_mtime if need_regen(regeninfo, regen_timestamp): regen(regeninfo, coredata.meson_command, backend) - sys.exit(0) + return 0 if __name__ == '__main__': - run(sys.argv[1:]) + sys.exit(run(sys.argv[1:])) diff --git a/mesonbuild/scripts/scanbuild.py b/mesonbuild/scripts/scanbuild.py index 0190067..3e5b30f 100644 --- a/mesonbuild/scripts/scanbuild.py +++ b/mesonbuild/scripts/scanbuild.py @@ -12,30 +12,31 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os import subprocess import shutil import tempfile from ..environment import detect_ninja, detect_scanbuild +from pathlib import Path +import typing as T -def scanbuild(exelist, srcdir, blddir, privdir, logdir, args): - with tempfile.TemporaryDirectory(dir=privdir) as scandir: +def scanbuild(exelist: T.List[str], srcdir: Path, blddir: Path, privdir: Path, logdir: Path, args: T.List[str]) -> int: + with tempfile.TemporaryDirectory(dir=str(privdir)) as scandir: meson_cmd = exelist + args - build_cmd = exelist + ['-o', logdir] + detect_ninja() + ['-C', scandir] - rc = subprocess.call(meson_cmd + [srcdir, scandir]) + build_cmd = exelist + ['-o', str(logdir)] + detect_ninja() + ['-C', scandir] + rc = subprocess.call(meson_cmd + [str(srcdir), scandir]) if rc != 0: return rc return subprocess.call(build_cmd) -def run(args): - srcdir = args[0] - blddir = args[1] +def run(args: T.List[str]) -> int: + srcdir = Path(args[0]) + blddir = Path(args[1]) meson_cmd = args[2:] - privdir = os.path.join(blddir, 'meson-private') - logdir = os.path.join(blddir, 'meson-logs/scanbuild') - shutil.rmtree(logdir, ignore_errors=True) + privdir = blddir / 'meson-private' + logdir = blddir / 'meson-logs' / 'scanbuild' + shutil.rmtree(str(logdir), ignore_errors=True) exelist = detect_scanbuild() if not exelist: diff --git a/mesonbuild/scripts/symbolextractor.py b/mesonbuild/scripts/symbolextractor.py index cf486af..e607315 100644 --- a/mesonbuild/scripts/symbolextractor.py +++ b/mesonbuild/scripts/symbolextractor.py @@ -36,12 +36,12 @@ parser.add_argument('args', nargs='+') TOOL_WARNING_FILE = None RELINKING_WARNING = 'Relinking will always happen on source changes.' -def dummy_syms(outfilename: str): +def dummy_syms(outfilename: str) -> None: """Just touch it so relinking happens always.""" with open(outfilename, 'w'): pass -def write_if_changed(text: str, outfilename: str): +def write_if_changed(text: str, outfilename: str) -> None: try: with open(outfilename, 'r') as f: oldtext = f.read() @@ -52,13 +52,11 @@ def write_if_changed(text: str, outfilename: str): with open(outfilename, 'w') as f: f.write(text) -def print_tool_warning(tool: list, msg: str, stderr: str = None): +def print_tool_warning(tools: T.List[str], msg: str, stderr: T.Optional[str] = None) -> None: global TOOL_WARNING_FILE if os.path.exists(TOOL_WARNING_FILE): return - if len(tool) == 1: - tool = tool[0] - m = '{!r} {}. {}'.format(tool, msg, RELINKING_WARNING) + m = '{!r} {}. {}'.format(tools, msg, RELINKING_WARNING) if stderr: m += '\n' + stderr mlog.warning(m) @@ -73,7 +71,7 @@ def get_tool(name: str) -> T.List[str]: return shlex.split(os.environ[evar]) return [name] -def call_tool(name: str, args: T.List[str], **kwargs) -> str: +def call_tool(name: str, args: T.List[str], **kwargs: T.Any) -> str: tool = get_tool(name) try: p, output, e = Popen_safe(tool + args, **kwargs) @@ -88,7 +86,7 @@ def call_tool(name: str, args: T.List[str], **kwargs) -> str: return None return output -def call_tool_nowarn(tool: T.List[str], **kwargs) -> T.Tuple[str, str]: +def call_tool_nowarn(tool: T.List[str], **kwargs: T.Any) -> T.Tuple[str, str]: try: p, output, e = Popen_safe(tool, **kwargs) except FileNotFoundError: @@ -99,7 +97,7 @@ def call_tool_nowarn(tool: T.List[str], **kwargs) -> T.Tuple[str, str]: return None, e return output, None -def gnu_syms(libfilename: str, outfilename: str): +def gnu_syms(libfilename: str, outfilename: str) -> None: # Get the name of the library output = call_tool('readelf', ['-d', libfilename]) if not output: @@ -126,7 +124,7 @@ def gnu_syms(libfilename: str, outfilename: str): result += [' '.join(entry)] write_if_changed('\n'.join(result) + '\n', outfilename) -def solaris_syms(libfilename: str, outfilename: str): +def solaris_syms(libfilename: str, outfilename: str) -> None: # gnu_syms() works with GNU nm & readelf, not Solaris nm & elfdump origpath = os.environ['PATH'] try: @@ -135,7 +133,7 @@ def solaris_syms(libfilename: str, outfilename: str): finally: os.environ['PATH'] = origpath -def osx_syms(libfilename: str, outfilename: str): +def osx_syms(libfilename: str, outfilename: str) -> None: # Get the name of the library output = call_tool('otool', ['-l', libfilename]) if not output: @@ -156,7 +154,7 @@ def osx_syms(libfilename: str, outfilename: str): result += [' '.join(x.split()[0:2]) for x in output.split('\n')] write_if_changed('\n'.join(result) + '\n', outfilename) -def openbsd_syms(libfilename: str, outfilename: str): +def openbsd_syms(libfilename: str, outfilename: str) -> None: # Get the name of the library output = call_tool('readelf', ['-d', libfilename]) if not output: @@ -173,7 +171,7 @@ def openbsd_syms(libfilename: str, outfilename: str): result += [' '.join(x.split()[0:2]) for x in output.split('\n') if x and not x.endswith('U ')] write_if_changed('\n'.join(result) + '\n', outfilename) -def cygwin_syms(impfilename: str, outfilename: str): +def cygwin_syms(impfilename: str, outfilename: str) -> None: # Get the name of the library output = call_tool('dlltool', ['-I', impfilename]) if not output: @@ -242,23 +240,23 @@ def _get_implib_exports(impfilename: str) -> T.Tuple[T.List[str], str]: all_stderr += e return ([], all_stderr) -def windows_syms(impfilename: str, outfilename: str): +def windows_syms(impfilename: str, outfilename: str) -> None: # Get the name of the library result, e = _get_implib_dllname(impfilename) if not result: - print_tool_warning('lib, llvm-lib, dlltool', 'do not work or were not found', e) + print_tool_warning(['lib', 'llvm-lib', 'dlltool'], 'do not work or were not found', e) dummy_syms(outfilename) return # Get a list of all symbols exported symbols, e = _get_implib_exports(impfilename) if not symbols: - print_tool_warning('dumpbin, llvm-nm, nm', 'do not work or were not found', e) + print_tool_warning(['dumpbin', 'llvm-nm', 'nm'], 'do not work or were not found', e) dummy_syms(outfilename) return result += symbols write_if_changed('\n'.join(result) + '\n', outfilename) -def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host: str): +def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host: str) -> None: if cross_host is not None: # In case of cross builds just always relink. In theory we could # determine the correct toolset, but we would need to use the correct @@ -295,7 +293,7 @@ def gen_symbols(libfilename: str, impfilename: str, outfilename: str, cross_host pass dummy_syms(outfilename) -def run(args): +def run(args: T.List[str]) -> int: global TOOL_WARNING_FILE options = parser.parse_args(args) if len(options.args) != 4: diff --git a/mesonbuild/scripts/tags.py b/mesonbuild/scripts/tags.py index 431bb5f..bb85831 100644 --- a/mesonbuild/scripts/tags.py +++ b/mesonbuild/scripts/tags.py @@ -15,9 +15,9 @@ import os import subprocess from pathlib import Path +import typing as T - -def ls_as_bytestream(): +def ls_as_bytestream() -> bytes: if os.path.exists('.git'): return subprocess.run(['git', 'ls-tree', '-r', '--name-only', 'HEAD'], stdout=subprocess.PIPE).stdout @@ -28,24 +28,24 @@ def ls_as_bytestream(): return '\n'.join(files).encode() -def cscope(): +def cscope() -> int: ls = b'\n'.join([b'"%s"' % f for f in ls_as_bytestream().split()]) return subprocess.run(['cscope', '-v', '-b', '-i-'], input=ls).returncode -def ctags(): +def ctags() -> int: ls = ls_as_bytestream() return subprocess.run(['ctags', '-L-'], input=ls).returncode -def etags(): +def etags() -> int: ls = ls_as_bytestream() return subprocess.run(['etags', '-'], input=ls).returncode -def run(args): +def run(args: T.List[str]) -> int: tool_name = args[0] srcdir_name = args[1] os.chdir(srcdir_name) assert tool_name in ['cscope', 'ctags', 'etags'] - return globals()[tool_name]() + return T.cast(int, globals()[tool_name]()) diff --git a/mesonbuild/scripts/uninstall.py b/mesonbuild/scripts/uninstall.py index bdc036b..b648de4 100644 --- a/mesonbuild/scripts/uninstall.py +++ b/mesonbuild/scripts/uninstall.py @@ -13,10 +13,11 @@ # limitations under the License. import os +import typing as T logfile = 'meson-logs/install-log.txt' -def do_uninstall(log): +def do_uninstall(log: str) -> None: failures = 0 successes = 0 for line in open(log): @@ -38,7 +39,7 @@ def do_uninstall(log): print('Failed:', failures) print('\nRemember that files created by custom scripts have not been removed.') -def run(args): +def run(args: T.List[str]) -> int: if args: print('Weird error.') return 1 diff --git a/mesonbuild/scripts/vcstagger.py b/mesonbuild/scripts/vcstagger.py index 16dd4d1..64985f6 100644 --- a/mesonbuild/scripts/vcstagger.py +++ b/mesonbuild/scripts/vcstagger.py @@ -13,9 +13,9 @@ # limitations under the License. import sys, os, subprocess, re +import typing as T - -def config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_selector, cmd): +def config_vcs_tag(infile: str, outfile: str, fallback: str, source_dir: str, replace_string: str, regex_selector: str, cmd: T.List[str]) -> None: try: output = subprocess.check_output(cmd, cwd=source_dir) new_string = re.search(regex_selector, output.decode()).group(1).strip() @@ -34,7 +34,7 @@ def config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_ f.write(new_data) -def run(args): +def run(args: T.List[str]) -> int: infile, outfile, fallback, source_dir, replace_string, regex_selector = args[0:6] command = args[6:] config_vcs_tag(infile, outfile, fallback, source_dir, replace_string, regex_selector, command) diff --git a/mesonbuild/scripts/yelphelper.py b/mesonbuild/scripts/yelphelper.py index 6bf0673..0355d9f 100644 --- a/mesonbuild/scripts/yelphelper.py +++ b/mesonbuild/scripts/yelphelper.py @@ -20,6 +20,7 @@ from .. import mlog from ..mesonlib import has_path_sep from . import destdir_join from .gettext import read_linguas +import typing as T parser = argparse.ArgumentParser() parser.add_argument('command') @@ -31,19 +32,19 @@ parser.add_argument('--media', dest='media', default='') parser.add_argument('--langs', dest='langs', default='') parser.add_argument('--symlinks', type=bool, dest='symlinks', default=False) -def build_pot(srcdir, project_id, sources): +def build_pot(srcdir: str, project_id: str, sources: T.List[str]) -> None: # Must be relative paths sources = [os.path.join('C', source) for source in sources] outfile = os.path.join(srcdir, project_id + '.pot') subprocess.call(['itstool', '-o', outfile] + sources) -def update_po(srcdir, project_id, langs): +def update_po(srcdir: str, project_id: str, langs: T.List[str]) -> None: potfile = os.path.join(srcdir, project_id + '.pot') for lang in langs: pofile = os.path.join(srcdir, lang, lang + '.po') subprocess.call(['msgmerge', '-q', '-o', pofile, pofile, potfile]) -def build_translations(srcdir, blddir, langs): +def build_translations(srcdir: str, blddir: str, langs: T.List[str]) -> None: for lang in langs: outdir = os.path.join(blddir, lang) os.makedirs(outdir, exist_ok=True) @@ -52,14 +53,14 @@ def build_translations(srcdir, blddir, langs): '-o', os.path.join(outdir, lang + '.gmo') ]) -def merge_translations(blddir, sources, langs): +def merge_translations(blddir: str, sources: T.List[str], langs: T.List[str]) -> None: for lang in langs: subprocess.call([ 'itstool', '-m', os.path.join(blddir, lang, lang + '.gmo'), '-o', os.path.join(blddir, lang) ] + sources) -def install_help(srcdir, blddir, sources, media, langs, install_dir, destdir, project_id, symlinks): +def install_help(srcdir: str, blddir: str, sources: T.List[str], media: T.List[str], langs: T.List[str], install_dir: str, destdir: str, project_id: str, symlinks: bool) -> None: c_install_dir = os.path.join(install_dir, 'C', project_id) for lang in langs + ['C']: indir = destdir_join(destdir, os.path.join(install_dir, lang, project_id)) @@ -101,7 +102,7 @@ def install_help(srcdir, blddir, sources, media, langs, install_dir, destdir, pr shutil.copyfile(infile, outfile) shutil.copystat(infile, outfile) -def run(args): +def run(args: T.List[str]) -> int: options = parser.parse_args(args) langs = options.langs.split('@@') if options.langs else [] media = options.media.split('@@') if options.media else [] @@ -129,3 +130,4 @@ def run(args): merge_translations(build_subdir, abs_sources, langs) install_help(src_subdir, build_subdir, sources, media, langs, install_dir, destdir, options.project_id, options.symlinks) + return 0 |