python: add type hints

This commit is contained in:
Max Kellermann 2023-09-16 23:23:18 +02:00
parent dd89ea4505
commit 3f2016e552
9 changed files with 68 additions and 54 deletions

View File

@ -1,15 +1,17 @@
import os.path, subprocess, sys
from typing import Collection, Iterable, Optional
from build.makeproject import MakeProject
class AutotoolsProject(MakeProject):
def __init__(self, url, md5, installed, configure_args=[],
autogen=False,
autoreconf=False,
cppflags='',
ldflags='',
libs='',
subdirs=None,
def __init__(self, url: str, md5: str, installed: str,
configure_args: Iterable[str]=[],
autogen: bool=False,
autoreconf: bool=False,
cppflags: str='',
ldflags: str='',
libs: str='',
subdirs: Optional[Collection[str]]=None,
**kwargs):
MakeProject.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args
@ -20,7 +22,7 @@ class AutotoolsProject(MakeProject):
self.libs = libs
self.subdirs = subdirs
def configure(self, toolchain):
def configure(self, toolchain) -> str:
src = self.unpack(toolchain)
if self.autogen:
if sys.platform == 'darwin':
@ -68,7 +70,7 @@ class AutotoolsProject(MakeProject):
return build
def _build(self, toolchain):
def _build(self, toolchain) -> None:
build = self.configure(toolchain)
if self.subdirs is not None:
for subdir in self.subdirs:

View File

@ -1,17 +1,19 @@
import os
import re
import subprocess
from typing import Optional, TextIO
from collections.abc import Mapping
from build.project import Project
def __write_cmake_compiler(f, language, compiler):
def __write_cmake_compiler(f: TextIO, language: str, compiler: str) -> None:
s = compiler.split(' ', 1)
if len(s) == 2:
print(f'set(CMAKE_{language}_COMPILER_LAUNCHER {s[0]})', file=f)
compiler = s[1]
print(f'set(CMAKE_{language}_COMPILER {compiler})', file=f)
def __write_cmake_toolchain_file(f, toolchain):
def __write_cmake_toolchain_file(f: TextIO, toolchain) -> None:
if '-darwin' in toolchain.actual_arch:
cmake_system_name = 'Darwin'
elif toolchain.is_windows:
@ -52,7 +54,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
""")
def configure(toolchain, src, build, args=(), env=None):
def configure(toolchain, src: str, build: str, args: list[str]=[], env: Optional[Mapping[str, str]]=None) -> None:
cross_args = []
if toolchain.is_windows:
@ -91,16 +93,17 @@ def configure(toolchain, src, build, args=(), env=None):
subprocess.check_call(configure, env=env, cwd=build)
class CmakeProject(Project):
def __init__(self, url, md5, installed, configure_args=[],
windows_configure_args=[],
env=None,
def __init__(self, url: str, md5: str, installed: str,
configure_args: list[str]=[],
windows_configure_args: list[str]=[],
env: Optional[Mapping[str, str]]=None,
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args
self.windows_configure_args = windows_configure_args
self.env = env
def configure(self, toolchain):
def configure(self, toolchain) -> str:
src = self.unpack(toolchain)
build = self.make_build_path(toolchain)
configure_args = self.configure_args
@ -109,7 +112,7 @@ class CmakeProject(Project):
configure(toolchain, src, build, configure_args, self.env)
return build
def _build(self, toolchain):
def _build(self, toolchain) -> None:
build = self.configure(toolchain)
subprocess.check_call(['ninja', '-v', 'install'],
cwd=build, env=toolchain.env)

View File

@ -1,15 +1,16 @@
import subprocess, multiprocessing
from typing import Optional
from build.project import Project
class MakeProject(Project):
def __init__(self, url, md5, installed,
install_target='install',
def __init__(self, url: str, md5: str, installed: str,
install_target: str='install',
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.install_target = install_target
def get_simultaneous_jobs(self):
def get_simultaneous_jobs(self) -> int:
try:
# use twice as many simultaneous jobs as we have CPU cores
return multiprocessing.cpu_count() * 2
@ -17,17 +18,17 @@ class MakeProject(Project):
# default to 12, if multiprocessing.cpu_count() is not implemented
return 12
def get_make_args(self, toolchain):
def get_make_args(self, toolchain) -> list[str]:
return ['--quiet', '-j' + str(self.get_simultaneous_jobs())]
def get_make_install_args(self, toolchain):
def get_make_install_args(self, toolchain) -> list[str]:
return ['--quiet', self.install_target]
def make(self, toolchain, wd, args):
def make(self, toolchain, wd: str, args: list[str]) -> None:
subprocess.check_call(['make'] + args,
cwd=wd, env=toolchain.env)
def build_make(self, toolchain, wd, install=True):
def build_make(self, toolchain, wd: str, install: bool=True) -> None:
self.make(toolchain, wd, self.get_make_args(toolchain))
if install:
self.make(toolchain, wd, self.get_make_install_args(toolchain))

View File

@ -1,10 +1,11 @@
import os
import subprocess
import platform
from typing import Optional
from build.project import Project
def make_cross_file(toolchain):
def make_cross_file(toolchain) -> str:
if toolchain.is_windows:
system = 'windows'
windres = "windres = '%s'" % toolchain.windres
@ -80,7 +81,7 @@ endian = '{endian}'
""")
return path
def configure(toolchain, src, build, args=()):
def configure(toolchain, src: str, build: str, args: list[str]=[]) -> None:
cross_file = make_cross_file(toolchain)
configure = [
'meson', 'setup',
@ -103,18 +104,19 @@ def configure(toolchain, src, build, args=()):
subprocess.check_call(configure, env=env)
class MesonProject(Project):
def __init__(self, url, md5, installed, configure_args=[],
def __init__(self, url: str, md5: str, installed: str,
configure_args: list[str]=[],
**kwargs):
Project.__init__(self, url, md5, installed, **kwargs)
self.configure_args = configure_args
def configure(self, toolchain):
def configure(self, toolchain) -> str:
src = self.unpack(toolchain)
build = self.make_build_path(toolchain)
configure(toolchain, src, build, self.configure_args)
return build
def _build(self, toolchain):
def _build(self, toolchain) -> None:
build = self.configure(toolchain)
subprocess.check_call(['ninja', '-v', 'install'],
cwd=build, env=toolchain.env)

View File

@ -1,13 +1,14 @@
import subprocess
from typing import Optional
from build.makeproject import MakeProject
class OpenSSLProject(MakeProject):
def __init__(self, url, md5, installed,
def __init__(self, url: str, md5: str, installed: str,
**kwargs):
MakeProject.__init__(self, url, md5, installed, install_target='install_dev', **kwargs)
def get_make_args(self, toolchain):
def get_make_args(self, toolchain) -> list[str]:
return MakeProject.get_make_args(self, toolchain) + [
'CC=' + toolchain.cc,
'CFLAGS=' + toolchain.cflags,
@ -17,13 +18,13 @@ class OpenSSLProject(MakeProject):
'build_libs',
]
def get_make_install_args(self, toolchain):
def get_make_install_args(self, toolchain) -> list[str]:
# OpenSSL's Makefile runs "ranlib" during installation
return MakeProject.get_make_install_args(self, toolchain) + [
'RANLIB=' + toolchain.ranlib,
]
def _build(self, toolchain):
def _build(self, toolchain) -> None:
src = self.unpack(toolchain, out_of_tree=False)
# OpenSSL has a weird target architecture scheme with lots of

View File

@ -1,16 +1,18 @@
import os, shutil
import re
from typing import cast, BinaryIO, Optional
from build.download import download_and_verify
from build.tar import untar
from build.quilt import push_all
class Project:
def __init__(self, url, md5, installed, name=None, version=None,
base=None,
patches=None,
def __init__(self, url: str, md5: str, installed: str,
name: Optional[str]=None, version: Optional[str]=None,
base: Optional[str]=None,
patches: Optional[str]=None,
edits=None,
use_cxx=False):
use_cxx: bool=False):
if base is None:
basename = os.path.basename(url)
m = re.match(r'^(.+)\.(tar(\.(gz|bz2|xz|lzma))?|zip)$', basename)
@ -39,10 +41,10 @@ class Project:
self.edits = edits
self.use_cxx = use_cxx
def download(self, toolchain):
def download(self, toolchain) -> str:
return download_and_verify(self.url, self.md5, toolchain.tarball_path)
def is_installed(self, toolchain):
def is_installed(self, toolchain) -> bool:
tarball = self.download(toolchain)
installed = os.path.join(toolchain.install_prefix, self.installed)
tarball_mtime = os.path.getmtime(tarball)
@ -51,7 +53,7 @@ class Project:
except FileNotFoundError:
return False
def unpack(self, toolchain, out_of_tree=True):
def unpack(self, toolchain, out_of_tree: bool=True) -> str:
if out_of_tree:
parent_path = toolchain.src_path
else:
@ -72,7 +74,7 @@ class Project:
return path
def make_build_path(self, toolchain, lazy=False):
def make_build_path(self, toolchain, lazy: bool=False) -> str:
path = os.path.join(toolchain.build_path, self.base)
if lazy and os.path.isdir(path):
return path
@ -83,5 +85,5 @@ class Project:
os.makedirs(path, exist_ok=True)
return path
def build(self, toolchain):
def build(self, toolchain) -> None:
self._build(toolchain)

View File

@ -1,9 +1,10 @@
import subprocess
from typing import Union
def run_quilt(toolchain, cwd, patches_path, *args):
def run_quilt(toolchain, cwd: str, patches_path: str, *args: str) -> None:
env = dict(toolchain.env)
env['QUILT_PATCHES'] = patches_path
subprocess.check_call(['quilt'] + list(args), cwd=cwd, env=env)
def push_all(toolchain, src_path, patches_path):
def push_all(toolchain, src_path: str, patches_path: str) -> None:
run_quilt(toolchain, src_path, patches_path, 'push', '-a')

View File

@ -1,6 +1,7 @@
import hashlib
from typing import cast, Any, BinaryIO
def feed_file(h, f):
def feed_file(h: Any, f: BinaryIO) -> None:
"""Feed data read from an open file into the hashlib instance."""
while True:
@ -10,20 +11,20 @@ def feed_file(h, f):
break
h.update(data)
def feed_file_path(h, path):
def feed_file_path(h: Any, path: str) -> None:
"""Feed data read from a file (to be opened by this function) into the hashlib instance."""
with open(path, 'rb') as f:
feed_file(h, f)
def file_digest(algorithm, path):
def file_digest(algorithm: Any, path: str) -> str:
"""Calculate the digest of a file and return it in hexadecimal notation."""
h = algorithm()
feed_file_path(h, path)
return h.hexdigest()
return cast(str, h.hexdigest())
def guess_digest_algorithm(digest):
def guess_digest_algorithm(digest: str) -> Any:
l = len(digest)
if l == 32:
return hashlib.md5
@ -36,7 +37,7 @@ def guess_digest_algorithm(digest):
else:
return None
def verify_file_digest(path, expected_digest):
def verify_file_digest(path: str, expected_digest: str) -> bool:
"""Verify the digest of a file, and return True if the digest matches with the given expected digest."""
algorithm = guess_digest_algorithm(expected_digest)

View File

@ -1,13 +1,14 @@
import subprocess
from typing import Optional
from build.makeproject import MakeProject
class ZlibProject(MakeProject):
def __init__(self, url, md5, installed,
def __init__(self, url: str, md5: str, installed: str,
**kwargs):
MakeProject.__init__(self, url, md5, installed, **kwargs)
def get_make_args(self, toolchain):
def get_make_args(self, toolchain) -> list[str]:
return MakeProject.get_make_args(self, toolchain) + [
'CC=' + toolchain.cc + ' ' + toolchain.cppflags + ' ' + toolchain.cflags,
'CPP=' + toolchain.cc + ' -E ' + toolchain.cppflags,
@ -18,13 +19,13 @@ class ZlibProject(MakeProject):
'libz.a'
]
def get_make_install_args(self, toolchain):
def get_make_install_args(self, toolchain) -> list[str]:
return [
'RANLIB=' + toolchain.ranlib,
self.install_target
]
def _build(self, toolchain):
def _build(self, toolchain) -> None:
src = self.unpack(toolchain, out_of_tree=False)
subprocess.check_call(['./configure', '--prefix=' + toolchain.install_prefix, '--static'],