Commit 56079b9c authored by Pyre Bot Jr's avatar Pyre Bot Jr Committed by Facebook GitHub Bot

Add annotations to `opensource/fbcode_builder`

Reviewed By: shannonzhu

Differential Revision: D34224272

fbshipit-source-id: 52e19886ab3d4fb015a557244660dd4357a35c17
parent 126c1959
......@@ -29,7 +29,7 @@ class BuilderBase(object):
inst_dir,
env=None,
final_install_prefix=None,
):
) -> None:
self.env = Env()
if env:
self.env.update(env)
......@@ -57,7 +57,14 @@ class BuilderBase(object):
return [vcvarsall, "amd64", "&&"]
return []
def _run_cmd(self, cmd, cwd=None, env=None, use_cmd_prefix=True, allow_fail=False):
def _run_cmd(
self,
cmd,
cwd=None,
env=None,
use_cmd_prefix: bool = True,
allow_fail: bool = False,
):
if env:
e = self.env.copy()
e.update(env)
......@@ -79,7 +86,7 @@ class BuilderBase(object):
allow_fail=allow_fail,
)
def build(self, install_dirs, reconfigure):
def build(self, install_dirs, reconfigure: bool) -> None:
print("Building %s..." % self.manifest.name)
if self.build_dir is not None:
......@@ -97,6 +104,7 @@ class BuilderBase(object):
script_path = self.get_dev_run_script_path()
dep_munger = create_dyn_dep_munger(self.build_opts, install_dirs)
dep_dirs = self.get_dev_run_extra_path_dirs(install_dirs, dep_munger)
# pyre-fixme[16]: Optional type has no attribute `emit_dev_run_script`.
dep_munger.emit_dev_run_script(script_path, dep_dirs)
@property
......@@ -122,12 +130,12 @@ class BuilderBase(object):
def run_tests(
self, install_dirs, schedule_type, owner, test_filter, retry, no_testpilot
):
) -> None:
"""Execute any tests that we know how to run. If they fail,
raise an exception."""
pass
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
"""Perform the build.
install_dirs contains the list of installation directories for
the dependencies of this project.
......@@ -166,7 +174,7 @@ class MakeBuilder(BuilderBase):
build_args,
install_args,
test_args,
):
) -> None:
super(MakeBuilder, self).__init__(
build_opts, ctx, manifest, src_dir, build_dir, inst_dir
)
......@@ -181,7 +189,7 @@ class MakeBuilder(BuilderBase):
def _get_prefix(self):
return ["PREFIX=" + self.inst_dir, "prefix=" + self.inst_dir]
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
env = self._compute_env(install_dirs)
......@@ -208,7 +216,7 @@ class MakeBuilder(BuilderBase):
def run_tests(
self, install_dirs, schedule_type, owner, test_filter, retry, no_testpilot
):
) -> None:
if not self.test_args:
return
......@@ -219,7 +227,7 @@ class MakeBuilder(BuilderBase):
class CMakeBootStrapBuilder(MakeBuilder):
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
self._run_cmd(
[
"./bootstrap",
......@@ -241,7 +249,7 @@ class AutoconfBuilder(BuilderBase):
inst_dir,
args,
conf_env_args,
):
) -> None:
super(AutoconfBuilder, self).__init__(
build_opts, ctx, manifest, src_dir, build_dir, inst_dir
)
......@@ -252,7 +260,7 @@ class AutoconfBuilder(BuilderBase):
def _make_binary(self):
return self.manifest.get("build", "make_binary", "make", ctx=self.ctx)
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
configure_path = os.path.join(self.src_dir, "configure")
autogen_path = os.path.join(self.src_dir, "autogen.sh")
......@@ -295,12 +303,12 @@ class Iproute2Builder(BuilderBase):
# Thus, explicitly copy sources from src_dir to build_dir, bulid,
# and then install to inst_dir using DESTDIR
# lastly, also copy include from build_dir to inst_dir
def __init__(self, build_opts, ctx, manifest, src_dir, build_dir, inst_dir):
def __init__(self, build_opts, ctx, manifest, src_dir, build_dir, inst_dir) -> None:
super(Iproute2Builder, self).__init__(
build_opts, ctx, manifest, src_dir, build_dir, inst_dir
)
def _patch(self):
def _patch(self) -> None:
# FBOSS build currently depends on an old version of iproute2 (commit
# 7ca63aef7d1b0c808da0040c6b366ef7a61f38c1). This is missing a commit
# (ae717baf15fb4d30749ada3948d9445892bac239) needed to build iproute2
......@@ -313,7 +321,7 @@ class Iproute2Builder(BuilderBase):
f.write("#include <stdint.h>\n")
f.write(data)
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
configure_path = os.path.join(self.src_dir, "configure")
env = self.env.copy()
......@@ -334,7 +342,7 @@ class Iproute2Builder(BuilderBase):
class BistroBuilder(BuilderBase):
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
p = os.path.join(self.src_dir, "bistro", "bistro")
env = self._compute_env(install_dirs)
env["PATH"] = env["PATH"] + ":" + os.path.join(p, "bin")
......@@ -361,7 +369,7 @@ class BistroBuilder(BuilderBase):
def run_tests(
self, install_dirs, schedule_type, owner, test_filter, retry, no_testpilot
):
) -> None:
env = self._compute_env(install_dirs)
build_dir = os.path.join(self.src_dir, "bistro", "bistro", "cmake", "Release")
NUM_RETRIES = 5
......@@ -508,7 +516,7 @@ if __name__ == "__main__":
loader=None,
final_install_prefix=None,
extra_cmake_defines=None,
):
) -> None:
super(CMakeBuilder, self).__init__(
build_opts,
ctx,
......@@ -533,7 +541,7 @@ if __name__ == "__main__":
if build_opts.shared_libs:
self.defines["BUILD_SHARED_LIBS"] = "ON"
def _invalidate_cache(self):
def _invalidate_cache(self) -> None:
for name in [
"CMakeCache.txt",
"CMakeFiles/CMakeError.log",
......@@ -545,14 +553,14 @@ if __name__ == "__main__":
elif os.path.exists(name):
os.unlink(name)
def _needs_reconfigure(self):
def _needs_reconfigure(self) -> bool:
for name in ["CMakeCache.txt", "build.ninja"]:
name = os.path.join(self.build_dir, name)
if not os.path.exists(name):
return True
return False
def _write_build_script(self, **kwargs):
def _write_build_script(self, **kwargs) -> None:
env_lines = [" {!r}: {!r},".format(k, v) for k, v in kwargs["env"].items()]
kwargs["env_str"] = "\n".join(["{"] + env_lines + ["}"])
......@@ -668,7 +676,7 @@ if __name__ == "__main__":
return define_args
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
reconfigure = reconfigure or self._needs_reconfigure()
env = self._compute_env(install_dirs)
......@@ -713,8 +721,8 @@ if __name__ == "__main__":
)
def run_tests(
self, install_dirs, schedule_type, owner, test_filter, retry, no_testpilot
):
self, install_dirs, schedule_type, owner, test_filter, retry: int, no_testpilot
) -> None:
env = self._compute_env(install_dirs)
ctest = path_search(env, "ctest")
cmake = path_search(env, "cmake")
......@@ -917,19 +925,21 @@ if __name__ == "__main__":
# Only add this option in the second run.
args += ["--rerun-failed"]
count += 1
# pyre-fixme[61]: `retcode` is undefined, or not always defined.
if retcode != 0:
# Allow except clause in getdeps.main to catch and exit gracefully
# This allows non-testpilot runs to fail through the same logic as failed testpilot runs, which may become handy in case if post test processing is needed in the future
# pyre-fixme[61]: `retcode` is undefined, or not always defined.
raise subprocess.CalledProcessError(retcode, args)
class NinjaBootstrap(BuilderBase):
def __init__(self, build_opts, ctx, manifest, build_dir, src_dir, inst_dir):
def __init__(self, build_opts, ctx, manifest, build_dir, src_dir, inst_dir) -> None:
super(NinjaBootstrap, self).__init__(
build_opts, ctx, manifest, src_dir, build_dir, inst_dir
)
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
self._run_cmd([sys.executable, "configure.py", "--bootstrap"], cwd=self.src_dir)
src_ninja = os.path.join(self.src_dir, "ninja")
dest_ninja = os.path.join(self.inst_dir, "bin/ninja")
......@@ -941,12 +951,12 @@ class NinjaBootstrap(BuilderBase):
class OpenSSLBuilder(BuilderBase):
def __init__(self, build_opts, ctx, manifest, build_dir, src_dir, inst_dir):
def __init__(self, build_opts, ctx, manifest, build_dir, src_dir, inst_dir) -> None:
super(OpenSSLBuilder, self).__init__(
build_opts, ctx, manifest, src_dir, build_dir, inst_dir
)
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
configure = os.path.join(self.src_dir, "Configure")
# prefer to resolve the perl that we installed from
......@@ -1001,7 +1011,7 @@ class OpenSSLBuilder(BuilderBase):
class Boost(BuilderBase):
def __init__(
self, build_opts, ctx, manifest, src_dir, build_dir, inst_dir, b2_args
):
) -> None:
children = os.listdir(src_dir)
assert len(children) == 1, "expected a single directory entry: %r" % (children,)
boost_src = children[0]
......@@ -1012,7 +1022,7 @@ class Boost(BuilderBase):
)
self.b2_args = b2_args
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
env = self._compute_env(install_dirs)
linkage = ["static"]
if self.build_opts.is_windows() or self.build_opts.shared_libs:
......@@ -1068,12 +1078,12 @@ class Boost(BuilderBase):
class NopBuilder(BuilderBase):
def __init__(self, build_opts, ctx, manifest, src_dir, inst_dir):
def __init__(self, build_opts, ctx, manifest, src_dir, inst_dir) -> None:
super(NopBuilder, self).__init__(
build_opts, ctx, manifest, src_dir, None, inst_dir
)
def build(self, install_dirs, reconfigure):
def build(self, install_dirs, reconfigure) -> None:
print("Installing %s -> %s" % (self.src_dir, self.inst_dir))
parent = os.path.dirname(self.inst_dir)
if not os.path.exists(parent):
......@@ -1116,12 +1126,12 @@ class OpenNSABuilder(NopBuilder):
# In future, if more builders require git-lfs, we would consider installing
# git-lfs as part of the sandcastle infra as against repeating similar
# logic for each builder that requires git-lfs.
def __init__(self, build_opts, ctx, manifest, src_dir, inst_dir):
def __init__(self, build_opts, ctx, manifest, src_dir, inst_dir) -> None:
super(OpenNSABuilder, self).__init__(
build_opts, ctx, manifest, src_dir, inst_dir
)
def build(self, install_dirs, reconfigure):
def build(self, install_dirs, reconfigure) -> None:
env = self._compute_env(install_dirs)
self._run_cmd(["git", "lfs", "install", "--local"], cwd=self.src_dir, env=env)
self._run_cmd(["git", "lfs", "pull"], cwd=self.src_dir, env=env)
......@@ -1130,12 +1140,12 @@ class OpenNSABuilder(NopBuilder):
class SqliteBuilder(BuilderBase):
def __init__(self, build_opts, ctx, manifest, src_dir, build_dir, inst_dir):
def __init__(self, build_opts, ctx, manifest, src_dir, build_dir, inst_dir) -> None:
super(SqliteBuilder, self).__init__(
build_opts, ctx, manifest, src_dir, build_dir, inst_dir
)
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
for f in ["sqlite3.c", "sqlite3.h", "sqlite3ext.h"]:
src = os.path.join(self.src_dir, f)
dest = os.path.join(self.build_dir, f)
......
......@@ -44,14 +44,14 @@ class BuildOptions(object):
scratch_dir,
host_type,
install_dir=None,
num_jobs=0,
use_shipit=False,
num_jobs: int = 0,
use_shipit: bool = False,
vcvars_path=None,
allow_system_packages=False,
allow_system_packages: bool = False,
lfs_path=None,
shared_libs=False,
shared_libs: bool = False,
facebook_internal=None,
):
) -> None:
"""fbcode_builder_dir - the path to either the in-fbsource fbcode_builder dir,
or for shipit-transformed repos, the build dir that
has been mapped into that dir.
......@@ -176,7 +176,7 @@ class BuildOptions(object):
def is_freebsd(self):
return self.host_type.is_freebsd()
def get_num_jobs(self, job_weight):
def get_num_jobs(self, job_weight: int):
"""Given an estimated job_weight in MiB, compute a reasonable concurrency limit."""
if self.specified_num_jobs:
return self.specified_num_jobs
......@@ -324,8 +324,8 @@ class BuildOptions(object):
return False
def add_prefix_to_env(
self, d, env, append=True, add_library_path=False
): # noqa: C901
self, d, env, append: bool = True, add_library_path: bool = False
) -> bool: # noqa: C901
bindir = os.path.join(d, "bin")
found = False
pkgconfig = os.path.join(d, "lib", "pkgconfig")
......@@ -472,7 +472,7 @@ def find_unused_drive_letter():
return available[-1]
def create_subst_path(path):
def create_subst_path(path) -> str:
for _attempt in range(0, 24):
drive = find_existing_win32_subst_for_path(
path, subst_mapping=list_win32_subst_letters()
......@@ -512,7 +512,7 @@ def _check_host_type(args, host_type):
return host_type
def setup_build_options(args, host_type=None):
def setup_build_options(args, host_type=None) -> BuildOptions:
"""Create a BuildOptions object based on the arguments"""
fbcode_builder_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
......
......@@ -10,7 +10,7 @@ class ArtifactCache(object):
The primary use case is for storing the build products on CI
systems to accelerate the build"""
def download_to_file(self, name, dest_file_name):
def download_to_file(self, name, dest_file_name) -> bool:
"""If `name` exists in the cache, download it and place it
in the specified `dest_file_name` location on the filesystem.
If a transient issue was encountered a TransientFailure shall
......@@ -21,7 +21,7 @@ class ArtifactCache(object):
All other conditions shall raise an appropriate exception."""
return False
def upload_from_file(self, name, source_file_name):
def upload_from_file(self, name, source_file_name) -> None:
"""Causes `name` to be populated in the cache by uploading
the contents of `source_file_name` to the storage system.
If a transient issue was encountered a TransientFailure shall
......@@ -31,7 +31,7 @@ class ArtifactCache(object):
pass
def create_cache():
def create_cache() -> None:
"""This function is monkey patchable to provide an actual
implementation"""
return None
......@@ -23,7 +23,7 @@ class CargoBuilder(BuilderBase):
workspace_dir,
manifests_to_build,
loader,
):
) -> None:
super(CargoBuilder, self).__init__(
build_opts, ctx, manifest, src_dir, build_dir, inst_dir
)
......@@ -32,7 +32,7 @@ class CargoBuilder(BuilderBase):
self.manifests_to_build = manifests_to_build and manifests_to_build.split(",")
self.loader = loader
def run_cargo(self, install_dirs, operation, args=None):
def run_cargo(self, install_dirs, operation, args=None) -> None:
args = args or []
env = self._compute_env(install_dirs)
# Enable using nightly features with stable compiler
......@@ -55,12 +55,12 @@ class CargoBuilder(BuilderBase):
def manifest_dir(self, manifest):
return os.path.join(self.build_source_dir(), manifest)
def recreate_dir(self, src, dst):
def recreate_dir(self, src, dst) -> None:
if os.path.isdir(dst):
shutil.rmtree(dst)
shutil.copytree(src, dst)
def _build(self, install_dirs, reconfigure):
def _build(self, install_dirs, reconfigure) -> None:
build_source_dir = self.build_source_dir()
self.recreate_dir(self.src_dir, build_source_dir)
......@@ -121,7 +121,7 @@ incremental = false
def run_tests(
self, install_dirs, schedule_type, owner, test_filter, retry, no_testpilot
):
) -> None:
if test_filter:
args = ["--", test_filter]
else:
......@@ -138,7 +138,7 @@ incremental = false
if self.build_doc:
self.run_cargo(install_dirs, "doc", ["--no-deps"] + margs)
def _patchup_workspace(self):
def _patchup_workspace(self) -> None:
"""
This method makes some assumptions about the state of the project and
its cargo dependendies:
......@@ -184,7 +184,7 @@ incremental = false
f.write("\n")
f.write(config)
def _resolve_config(self):
def _resolve_config(self) -> str:
"""
Returns a configuration to be put inside root Cargo.toml file which
patches the dependencies git code with local getdeps versions.
......
......@@ -45,7 +45,7 @@ def find_eden_root(dirpath):
return None
def prefetch_dir_if_eden(dirpath):
def prefetch_dir_if_eden(dirpath) -> None:
"""After an amend/rebase, Eden may need to fetch a large number
of trees from the servers. The simplistic single threaded walk
performed by copytree makes this more expensive than is desirable
......@@ -65,7 +65,8 @@ def prefetch_dir_if_eden(dirpath):
PREFETCHED_DIRS.add(dirpath)
def copytree(src_dir, dest_dir, ignore=None):
# pyre-fixme[9]: ignore has type `bool`; used as `None`.
def copytree(src_dir, dest_dir, ignore: bool = None):
"""Recursively copy the src_dir to the dest_dir, filtering
out entries using the ignore lambda. The behavior of the
ignore lambda must match that described by `shutil.copytree`.
......@@ -75,4 +76,7 @@ def copytree(src_dir, dest_dir, ignore=None):
uses watchman to mirror src_dir into dest_dir.
"""
prefetch_dir_if_eden(src_dir)
# pyre-fixme[6]: For 3rd param expected
# `Union[typing.Callable[[Union[PathLike[str], str], List[str]], Iterable[str]],
# typing.Callable[[str, List[str]], Iterable[str]], None]` but got `bool`.
return shutil.copytree(src_dir, dest_dir, ignore=ignore)
......@@ -20,13 +20,13 @@ from .envfuncs import path_search
OBJECT_SUBDIRS = ("bin", "lib", "lib64")
def copyfile(src, dest):
def copyfile(src, dest) -> None:
shutil.copyfile(src, dest)
shutil.copymode(src, dest)
class DepBase(object):
def __init__(self, buildopts, install_dirs, strip):
def __init__(self, buildopts, install_dirs, strip) -> None:
self.buildopts = buildopts
self.env = buildopts.compute_env_for_install_dirs(install_dirs)
self.install_dirs = install_dirs
......@@ -36,7 +36,7 @@ class DepBase(object):
def list_dynamic_deps(self, objfile):
raise RuntimeError("list_dynamic_deps not implemented")
def interesting_dep(self, d):
def interesting_dep(self, d) -> bool:
return True
# final_install_prefix must be the equivalent path to `destdir` on the
......@@ -44,11 +44,12 @@ class DepBase(object):
# is intended to map to `/usr/local` in the install image, then
# final_install_prefix='/usr/local'.
# If left unspecified, destdir will be used.
def process_deps(self, destdir, final_install_prefix=None):
def process_deps(self, destdir, final_install_prefix=None) -> None:
if self.buildopts.is_windows():
lib_dir = "bin"
else:
lib_dir = "lib"
# pyre-fixme[16]: `DepBase` has no attribute `munged_lib_dir`.
self.munged_lib_dir = os.path.join(destdir, lib_dir)
final_lib_dir = os.path.join(final_install_prefix or destdir, lib_dir)
......@@ -92,7 +93,7 @@ class DepBase(object):
return dep_paths
def munge_in_place(self, objfile, final_lib_dir):
def munge_in_place(self, objfile, final_lib_dir) -> None:
print("Munging %s" % objfile)
for d in self.list_dynamic_deps(objfile):
if not self.interesting_dep(d):
......@@ -103,6 +104,7 @@ class DepBase(object):
dep = self.resolve_loader_path(d)
print("dep: %s -> %s" % (d, dep))
if dep:
# pyre-fixme[16]: `DepBase` has no attribute `munged_lib_dir`.
dest_dep = os.path.join(self.munged_lib_dir, os.path.basename(dep))
if dep not in self.processed_deps:
self.processed_deps.add(dep)
......@@ -128,7 +130,7 @@ class DepBase(object):
return candidate
return None
def list_objs_in_dir(self, dir, recurse=False, output_prefix=""):
def list_objs_in_dir(self, dir, recurse: bool = False, output_prefix: str = ""):
for entry in os.listdir(dir):
entry_path = os.path.join(dir, entry)
st = os.lstat(entry_path)
......@@ -143,21 +145,21 @@ class DepBase(object):
):
yield result
def is_objfile(self, objfile):
def is_objfile(self, objfile) -> bool:
return True
def strip_debug_info(self, objfile):
def strip_debug_info(self, objfile) -> None:
"""override this to define how to remove debug information
from an object file"""
pass
class WinDeps(DepBase):
def __init__(self, buildopts, install_dirs, strip):
def __init__(self, buildopts, install_dirs, strip) -> None:
super(WinDeps, self).__init__(buildopts, install_dirs, strip)
self.dumpbin = self.find_dumpbin()
def find_dumpbin(self):
def find_dumpbin(self) -> str:
# Looking for dumpbin in the following hardcoded paths.
# The registry option to find the install dir doesn't work anymore.
globs = [
......@@ -196,7 +198,7 @@ class WinDeps(DepBase):
return deps
def rewrite_dep(self, objfile, depname, old_dep, new_dep, final_lib_dir):
def rewrite_dep(self, objfile, depname, old_dep, new_dep, final_lib_dir) -> None:
# We can't rewrite on windows, but we will
# place the deps alongside the exe so that
# they end up in the search path
......@@ -217,21 +219,21 @@ class WinDeps(DepBase):
]
)
def interesting_dep(self, d):
def interesting_dep(self, d) -> bool:
if "api-ms-win-crt" in d:
return False
if d in self.SYSTEM_DLLS:
return False
return True
def is_objfile(self, objfile):
def is_objfile(self, objfile) -> bool:
if not os.path.isfile(objfile):
return False
if objfile.lower().endswith(".exe"):
return True
return False
def emit_dev_run_script(self, script_path, dep_dirs):
def emit_dev_run_script(self, script_path, dep_dirs) -> None:
"""Emit a script that can be used to run build artifacts directly from the
build directory, without installing them.
......@@ -297,7 +299,7 @@ class WinDeps(DepBase):
return dep_dirs
def _get_dev_run_script_contents(self, path_dirs):
def _get_dev_run_script_contents(self, path_dirs) -> str:
path_entries = ["$env:PATH"] + path_dirs
path_str = ";".join(path_entries)
return """\
......@@ -316,7 +318,7 @@ try {{
class ElfDeps(DepBase):
def __init__(self, buildopts, install_dirs, strip):
def __init__(self, buildopts, install_dirs, strip) -> None:
super(ElfDeps, self).__init__(buildopts, install_dirs, strip)
# We need patchelf to rewrite deps, so ensure that it is built...
......@@ -342,15 +344,17 @@ class ElfDeps(DepBase):
lines = out.split("\n")
return lines
def rewrite_dep(self, objfile, depname, old_dep, new_dep, final_lib_dir):
def rewrite_dep(self, objfile, depname, old_dep, new_dep, final_lib_dir) -> None:
final_dep = os.path.join(
final_lib_dir, os.path.relpath(new_dep, self.munged_lib_dir)
final_lib_dir,
# pyre-fixme[16]: `ElfDeps` has no attribute `munged_lib_dir`.
os.path.relpath(new_dep, self.munged_lib_dir),
)
subprocess.check_call(
[self.patchelf, "--replace-needed", depname, final_dep, objfile]
)
def is_objfile(self, objfile):
def is_objfile(self, objfile) -> bool:
if not os.path.isfile(objfile):
return False
with open(objfile, "rb") as f:
......@@ -358,7 +362,7 @@ class ElfDeps(DepBase):
magic = f.read(4)
return magic == b"\x7fELF"
def strip_debug_info(self, objfile):
def strip_debug_info(self, objfile) -> None:
subprocess.check_call(["strip", objfile])
......@@ -367,7 +371,7 @@ MACH_MAGIC = 0xFEEDFACF
class MachDeps(DepBase):
def interesting_dep(self, d):
def interesting_dep(self, d) -> bool:
if d.startswith("/usr/lib/") or d.startswith("/System/"):
return False
return True
......@@ -403,7 +407,7 @@ class MachDeps(DepBase):
deps.append(os.path.normcase(m.group(1)))
return deps
def rewrite_dep(self, objfile, depname, old_dep, new_dep, final_lib_dir):
def rewrite_dep(self, objfile, depname, old_dep, new_dep, final_lib_dir) -> None:
if objfile.endswith(".dylib"):
# Erase the original location from the id of the shared
# object. It doesn't appear to hurt to retain it, but
......@@ -412,7 +416,9 @@ class MachDeps(DepBase):
["install_name_tool", "-id", os.path.basename(objfile), objfile]
)
final_dep = os.path.join(
final_lib_dir, os.path.relpath(new_dep, self.munged_lib_dir)
final_lib_dir,
# pyre-fixme[16]: `MachDeps` has no attribute `munged_lib_dir`.
os.path.relpath(new_dep, self.munged_lib_dir),
)
subprocess.check_call(
......@@ -420,7 +426,9 @@ class MachDeps(DepBase):
)
def create_dyn_dep_munger(buildopts, install_dirs, strip=False) -> Optional[DepBase]:
def create_dyn_dep_munger(
buildopts, install_dirs, strip: bool = False
) -> Optional[DepBase]:
if buildopts.is_linux():
return ElfDeps(buildopts, install_dirs, strip)
if buildopts.is_darwin():
......
......@@ -9,18 +9,18 @@ import sys
class Env(object):
def __init__(self, src=None):
def __init__(self, src=None) -> None:
self._dict = {}
if src is None:
self.update(os.environ)
else:
self.update(src)
def update(self, src):
def update(self, src) -> None:
for k, v in src.items():
self.set(k, v)
def copy(self):
def copy(self) -> "Env":
return Env(self._dict)
def _key(self, key):
......@@ -64,7 +64,7 @@ class Env(object):
raise KeyError(key)
return val
def unset(self, key):
def unset(self, key) -> None:
if key is None:
raise KeyError("attempting to unset env[None]")
......@@ -72,13 +72,13 @@ class Env(object):
if key:
del self._dict[key]
def __delitem__(self, key):
def __delitem__(self, key) -> None:
self.unset(key)
def __repr__(self):
return repr(self._dict)
def set(self, key, value):
def set(self, key, value) -> None:
if key is None:
raise KeyError("attempting to assign env[None] = %r" % value)
......@@ -99,13 +99,13 @@ class Env(object):
self.unset(key)
self._dict[key] = value
def __setitem__(self, key, value):
def __setitem__(self, key, value) -> None:
self.set(key, value)
def __iter__(self):
return self._dict.__iter__()
def __len__(self):
def __len__(self) -> int:
return len(self._dict)
def keys(self):
......@@ -118,7 +118,9 @@ class Env(object):
return self._dict.items()
def add_path_entry(env, name, item, append=True, separator=os.pathsep):
def add_path_entry(
env, name, item, append: bool = True, separator: str = os.pathsep
) -> None:
"""Cause `item` to be added to the path style env var named
`name` held in the `env` dict. `append` specifies whether
the item is added to the end (the default) or should be
......@@ -135,7 +137,7 @@ def add_path_entry(env, name, item, append=True, separator=os.pathsep):
env.set(name, separator.join(val))
def add_flag(env, name, flag, append=True):
def add_flag(env, name, flag, append: bool = True) -> None:
"""Cause `flag` to be added to the CXXFLAGS-style env var named
`name` held in the `env` dict. `append` specifies whether the
flag is added to the end (the default) or should be prepended if
......@@ -152,7 +154,7 @@ _path_search_cache = {}
_not_found = object()
def tpx_path():
def tpx_path() -> str:
return "xplat/testinfra/tpx/ctp.tpx"
......
......@@ -13,5 +13,5 @@ class TransientFailure(Exception):
class ManifestNotFound(Exception):
def __init__(self, manifest_name):
def __init__(self, manifest_name) -> None:
super(Exception, self).__init__("Unable to find manifest '%s'" % manifest_name)
......@@ -37,40 +37,40 @@ def parse_expr(expr_text, valid_variables):
class ExprNode(object):
def eval(self, ctx):
def eval(self, ctx) -> bool:
return False
class TrueExpr(ExprNode):
def eval(self, ctx):
def eval(self, ctx) -> bool:
return True
def __str__(self):
def __str__(self) -> str:
return "true"
class NotExpr(ExprNode):
def __init__(self, node):
def __init__(self, node) -> None:
self._node = node
def eval(self, ctx):
def eval(self, ctx) -> bool:
return not self._node.eval(ctx)
def __str__(self):
def __str__(self) -> str:
return "not(%s)" % self._node
class AllExpr(ExprNode):
def __init__(self, nodes):
def __init__(self, nodes) -> None:
self._nodes = nodes
def eval(self, ctx):
def eval(self, ctx) -> bool:
for node in self._nodes:
if not node.eval(ctx):
return False
return True
def __str__(self):
def __str__(self) -> str:
items = []
for node in self._nodes:
items.append(str(node))
......@@ -78,16 +78,16 @@ class AllExpr(ExprNode):
class AnyExpr(ExprNode):
def __init__(self, nodes):
def __init__(self, nodes) -> None:
self._nodes = nodes
def eval(self, ctx):
def eval(self, ctx) -> bool:
for node in self._nodes:
if node.eval(ctx):
return True
return False
def __str__(self):
def __str__(self) -> str:
items = []
for node in self._nodes:
items.append(str(node))
......@@ -95,19 +95,19 @@ class AnyExpr(ExprNode):
class EqualExpr(ExprNode):
def __init__(self, key, value):
def __init__(self, key, value) -> None:
self._key = key
self._value = value
def eval(self, ctx):
return ctx.get(self._key) == self._value
def __str__(self):
def __str__(self) -> str:
return "%s=%s" % (self._key, self._value)
class Parser(object):
def __init__(self, text, valid_variables):
def __init__(self, text, valid_variables) -> None:
self.text = text
self.lex = shlex.shlex(text)
self.valid_variables = valid_variables
......@@ -147,13 +147,13 @@ class Parser(object):
"Unexpected token sequence '%s %s' in %s" % (name, op, self.text)
)
def ident(self):
def ident(self) -> str:
ident = self.lex.get_token()
if not re.match("[a-zA-Z]+", ident):
raise Exception("expected identifier found %s" % ident)
return ident
def parse_not(self):
def parse_not(self) -> NotExpr:
node = self.top()
expr = NotExpr(node)
tok = self.lex.get_token()
......@@ -161,7 +161,7 @@ class Parser(object):
raise Exception("expected ')' found %s" % tok)
return expr
def parse_any(self):
def parse_any(self) -> AnyExpr:
nodes = []
while True:
nodes.append(self.top())
......@@ -172,7 +172,7 @@ class Parser(object):
raise Exception("expected ',' or ')' but found %s" % tok)
return AnyExpr(nodes)
def parse_all(self):
def parse_all(self) -> AllExpr:
nodes = []
while True:
nodes.append(self.top())
......
......@@ -50,7 +50,7 @@ class ChangeStatus(object):
and whether we might need to reconfigure the build system.
"""
def __init__(self, all_changed=False):
def __init__(self, all_changed: bool = False) -> None:
"""Construct a ChangeStatus object. The default is to create
a status that indicates no changes, but passing all_changed=True
will create one that indicates that everything changed"""
......@@ -61,7 +61,7 @@ class ChangeStatus(object):
self.source_files = 0
self.make_files = 0
def record_change(self, file_name):
def record_change(self, file_name) -> None:
"""Used by the shipit fetcher to record changes as it updates
files in the destination. If the file name might be one used
in the cmake build system that we use for 1st party code, then
......@@ -81,14 +81,14 @@ class ChangeStatus(object):
elif "/fbcode_builder/" not in file_name:
self.source_files += 1
def sources_changed(self):
def sources_changed(self) -> bool:
"""Returns true if any source files were changed during
an update operation. This will typically be used to decide
that the build system to be run on the source dir in an
incremental mode"""
return self.source_files > 0
def build_changed(self):
def build_changed(self) -> bool:
"""Returns true if any build files were changed during
an update operation. This will typically be used to decidfe
that the build system should be reconfigured and re-run
......@@ -102,7 +102,7 @@ class Fetcher(object):
extracted data resides and reports this to the consumer via
its `get_src_dir` method."""
def update(self):
def update(self) -> ChangeStatus:
"""Brings the src dir up to date, ideally minimizing
changes so that a subsequent build doesn't over-build.
Returns a ChangeStatus object that helps the caller to
......@@ -110,12 +110,12 @@ class Fetcher(object):
the update."""
return ChangeStatus()
def clean(self):
def clean(self) -> None:
"""Reverts any changes that might have been made to
the src dir"""
pass
def hash(self):
def hash(self) -> None:
"""Returns a hash that identifies the version of the code in the
working copy. For a git repo this is commit hash for the working
copy. For other Fetchers this should relate to the version of
......@@ -128,7 +128,7 @@ class Fetcher(object):
"""
pass
def get_src_dir(self):
def get_src_dir(self) -> None:
"""Returns the source directory that the project was
extracted into"""
pass
......@@ -141,13 +141,13 @@ class LocalDirFetcher(object):
This fetcher cannot update or track changes. It always reports that the
project has changed, forcing it to always be built."""
def __init__(self, path):
def __init__(self, path) -> None:
self.path = os.path.realpath(path)
def update(self):
def update(self) -> ChangeStatus:
return ChangeStatus(all_changed=True)
def hash(self):
def hash(self) -> str:
return "0" * 40
def get_src_dir(self):
......@@ -155,7 +155,7 @@ class LocalDirFetcher(object):
class SystemPackageFetcher(object):
def __init__(self, build_options, packages):
def __init__(self, build_options, packages) -> None:
self.manager = build_options.host_type.get_package_manager()
self.packages = packages.get(self.manager)
self.host_type = build_options.host_type
......@@ -190,29 +190,29 @@ class SystemPackageFetcher(object):
return bool(self.installed)
def update(self):
def update(self) -> ChangeStatus:
assert self.installed
return ChangeStatus(all_changed=False)
def hash(self):
def hash(self) -> str:
if self.packages_are_installed():
return hashlib.sha256(self.installed).hexdigest()
else:
return "0" * 40
def get_src_dir(self):
def get_src_dir(self) -> None:
return None
class PreinstalledNopFetcher(SystemPackageFetcher):
def __init__(self):
def __init__(self) -> None:
self.installed = True
class GitFetcher(Fetcher):
DEFAULT_DEPTH = 1
def __init__(self, build_options, manifest, repo_url, rev, depth):
def __init__(self, build_options, manifest, repo_url, rev, depth) -> None:
# Extract the host/path portions of the URL and generate a flattened
# directory name. eg:
# github.com/facebook/folly.git -> github.com-facebook-folly.git
......@@ -246,7 +246,7 @@ class GitFetcher(Fetcher):
self.manifest = manifest
self.depth = depth if depth else GitFetcher.DEFAULT_DEPTH
def _update(self):
def _update(self) -> ChangeStatus:
current_hash = (
subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=self.repo_dir)
.strip()
......@@ -281,7 +281,7 @@ class GitFetcher(Fetcher):
self._clone()
return ChangeStatus(True)
def _clone(self):
def _clone(self) -> None:
print("Cloning %s..." % self.origin_repo)
# The basename/dirname stuff allows us to dance around issues where
# eg: this python process is native win32, but the git.exe is cygwin
......@@ -300,7 +300,7 @@ class GitFetcher(Fetcher):
)
self._update()
def clean(self):
def clean(self) -> None:
if os.path.exists(self.repo_dir):
run_cmd(["git", "clean", "-fxd"], cwd=self.repo_dir)
......@@ -343,7 +343,7 @@ def does_file_need_update(src_name, src_st, dest_name):
return False
def copy_if_different(src_name, dest_name):
def copy_if_different(src_name, dest_name) -> bool:
"""Copy src_name -> dest_name, but only touch dest_name
if src_name is different from dest_name, making this a
more build system friendly way to copy."""
......@@ -380,22 +380,22 @@ def list_files_under_dir_newer_than_timestamp(dir_to_scan, ts):
class ShipitPathMap(object):
def __init__(self):
def __init__(self) -> None:
self.roots = []
self.mapping = []
self.exclusion = []
def add_mapping(self, fbsource_dir, target_dir):
def add_mapping(self, fbsource_dir, target_dir) -> None:
"""Add a posix path or pattern. We cannot normpath the input
here because that would change the paths from posix to windows
form and break the logic throughout this class."""
self.roots.append(fbsource_dir)
self.mapping.append((fbsource_dir, target_dir))
def add_exclusion(self, pattern):
def add_exclusion(self, pattern) -> None:
self.exclusion.append(re.compile(pattern))
def _minimize_roots(self):
def _minimize_roots(self) -> None:
"""compute the de-duplicated set of roots within fbsource.
We take the shortest common directory prefix to make this
determination"""
......@@ -413,7 +413,7 @@ class ShipitPathMap(object):
self.roots = minimized
def _sort_mapping(self):
def _sort_mapping(self) -> None:
self.mapping.sort(reverse=True, key=lambda x: len(x[0]))
def _map_name(self, norm_name, dest_root):
......@@ -442,7 +442,7 @@ class ShipitPathMap(object):
raise Exception("%s did not match any rules" % norm_name)
def mirror(self, fbsource_root, dest_root):
def mirror(self, fbsource_root, dest_root) -> ChangeStatus:
self._minimize_roots()
self._sort_mapping()
......@@ -516,7 +516,7 @@ class FbsourceRepoData(NamedTuple):
FBSOURCE_REPO_DATA: Dict[str, FbsourceRepoData] = {}
def get_fbsource_repo_data(build_options):
def get_fbsource_repo_data(build_options) -> FbsourceRepoData:
"""Returns the commit metadata for the fbsource repo.
Since we may have multiple first party projects to
hash, and because we don't mutate the repo, we cache
......@@ -546,13 +546,13 @@ def get_fbsource_repo_data(build_options):
class SimpleShipitTransformerFetcher(Fetcher):
def __init__(self, build_options, manifest, ctx):
def __init__(self, build_options, manifest, ctx) -> None:
self.build_options = build_options
self.manifest = manifest
self.repo_dir = os.path.join(build_options.scratch_dir, "shipit", manifest.name)
self.ctx = ctx
def clean(self):
def clean(self) -> None:
if os.path.exists(self.repo_dir):
shutil.rmtree(self.repo_dir)
......@@ -571,7 +571,8 @@ class SimpleShipitTransformerFetcher(Fetcher):
return mapping.mirror(self.build_options.fbsource_dir, self.repo_dir)
def hash(self):
# pyre-fixme[15]: `hash` overrides method defined in `Fetcher` inconsistently.
def hash(self) -> str:
# We return a fixed non-hash string for in-fbsource builds.
# We're relying on the `update` logic to correctly invalidate
# the build in the case that files have changed.
......@@ -584,18 +585,18 @@ class SimpleShipitTransformerFetcher(Fetcher):
class ShipitTransformerFetcher(Fetcher):
SHIPIT = "/var/www/scripts/opensource/shipit/run_shipit.php"
def __init__(self, build_options, project_name):
def __init__(self, build_options, project_name) -> None:
self.build_options = build_options
self.project_name = project_name
self.repo_dir = os.path.join(build_options.scratch_dir, "shipit", project_name)
def update(self):
def update(self) -> ChangeStatus:
if os.path.exists(self.repo_dir):
return ChangeStatus()
self.run_shipit()
return ChangeStatus(True)
def clean(self):
def clean(self) -> None:
if os.path.exists(self.repo_dir):
shutil.rmtree(self.repo_dir)
......@@ -603,7 +604,7 @@ class ShipitTransformerFetcher(Fetcher):
def available(cls):
return os.path.exists(cls.SHIPIT)
def run_shipit(self):
def run_shipit(self) -> None:
tmp_path = self.repo_dir + ".new"
try:
if os.path.exists(tmp_path):
......@@ -640,7 +641,8 @@ class ShipitTransformerFetcher(Fetcher):
self.clean()
raise
def hash(self):
# pyre-fixme[15]: `hash` overrides method defined in `Fetcher` inconsistently.
def hash(self) -> str:
# We return a fixed non-hash string for in-fbsource builds.
return "fbsource"
......@@ -648,7 +650,7 @@ class ShipitTransformerFetcher(Fetcher):
return self.repo_dir
def download_url_to_file_with_progress(url, file_name):
def download_url_to_file_with_progress(url: str, file_name) -> None:
print("Download %s -> %s ..." % (url, file_name))
class Progress(object):
......@@ -686,7 +688,7 @@ def download_url_to_file_with_progress(url, file_name):
class ArchiveFetcher(Fetcher):
def __init__(self, build_options, manifest, url, sha256):
def __init__(self, build_options, manifest, url, sha256) -> None:
self.manifest = manifest
self.url = url
self.sha256 = sha256
......@@ -698,7 +700,7 @@ class ArchiveFetcher(Fetcher):
self.src_dir = os.path.join(build_options.scratch_dir, "extracted", basename)
self.hash_file = self.src_dir + ".hash"
def _verify_hash(self):
def _verify_hash(self) -> None:
h = hashlib.sha256()
with open(self.file_name, "rb") as f:
while True:
......@@ -720,16 +722,16 @@ class ArchiveFetcher(Fetcher):
os.makedirs(download_dir)
return download_dir
def _download(self):
def _download(self) -> None:
self._download_dir()
download_url_to_file_with_progress(self.url, self.file_name)
self._verify_hash()
def clean(self):
def clean(self) -> None:
if os.path.exists(self.src_dir):
shutil.rmtree(self.src_dir)
def update(self):
def update(self) -> ChangeStatus:
try:
with open(self.hash_file, "r") as f:
saved_hash = f.read().strip()
......
......@@ -56,7 +56,7 @@ class Loader(object):
class ResourceLoader(Loader):
def __init__(self, namespace, manifests_dir):
def __init__(self, namespace, manifests_dir) -> None:
self.namespace = namespace
self.manifests_dir = manifests_dir
......@@ -82,7 +82,7 @@ class ResourceLoader(Loader):
raise ManifestNotFound(project_name)
def _load_manifest(self, path):
def _load_manifest(self, path: str):
import pkg_resources
contents = pkg_resources.resource_string(self.namespace, path).decode("utf8")
......@@ -96,7 +96,7 @@ class ResourceLoader(Loader):
LOADER = Loader()
def patch_loader(namespace, manifests_dir="manifests"):
def patch_loader(namespace, manifests_dir: str = "manifests") -> None:
global LOADER
LOADER = ResourceLoader(namespace, manifests_dir)
......@@ -119,7 +119,7 @@ class ManifestLoader(object):
relationships and project hash values for this build configuration.
"""
def __init__(self, build_opts, ctx_gen=None):
def __init__(self, build_opts, ctx_gen=None) -> None:
self._loader = LOADER
self.build_opts = build_opts
if ctx_gen is None:
......@@ -231,16 +231,16 @@ class ManifestLoader(object):
return dep_order
def set_project_src_dir(self, project_name, path):
def set_project_src_dir(self, project_name, path) -> None:
self._fetcher_overrides[project_name] = fetcher.LocalDirFetcher(path)
def set_project_build_dir(self, project_name, path):
def set_project_build_dir(self, project_name, path) -> None:
self._build_dir_overrides[project_name] = path
def set_project_install_dir(self, project_name, path):
def set_project_install_dir(self, project_name, path) -> None:
self._install_dir_overrides[project_name] = path
def set_project_install_prefix(self, project_name, path):
def set_project_install_prefix(self, project_name, path) -> None:
self._install_prefix_overrides[project_name] = path
def create_fetcher(self, manifest):
......@@ -258,7 +258,7 @@ class ManifestLoader(object):
self._project_hashes[manifest.name] = h
return h
def _compute_project_hash(self, manifest):
def _compute_project_hash(self, manifest) -> str:
"""This recursive function computes a hash for a given manifest.
The hash takes into account some environmental factors on the
host machine and includes the hashes of its dependencies.
......
......@@ -9,7 +9,7 @@ import shlex
import sys
def is_windows():
def is_windows() -> bool:
"""Returns true if the system we are currently running on
is a Windows system"""
return sys.platform.startswith("win")
......@@ -52,7 +52,7 @@ def get_linux_type():
# but getdeps can't take third-party dependencies.
def _get_available_ram_linux():
def _get_available_ram_linux() -> int:
# TODO: Ideally, this function would inspect the current cgroup for any
# limits, rather than solely relying on system RAM.
with open("/proc/meminfo") as f:
......@@ -175,7 +175,7 @@ def get_available_ram() -> int:
class HostType(object):
def __init__(self, ostype=None, distro=None, distrovers=None):
def __init__(self, ostype=None, distro=None, distrovers=None) -> None:
if ostype is None:
distro = None
distrovers = None
......@@ -185,6 +185,7 @@ class HostType(object):
ostype = "darwin"
elif is_windows():
ostype = "windows"
# pyre-fixme[16]: Module `sys` has no attribute `getwindowsversion`.
distrovers = str(sys.getwindowsversion().major)
elif sys.platform.startswith("freebsd"):
ostype = "freebsd"
......@@ -218,7 +219,7 @@ class HostType(object):
def is_freebsd(self):
return self.ostype == "freebsd"
def as_tuple_string(self):
def as_tuple_string(self) -> str:
return "%s-%s-%s" % (
self.ostype,
self.distro or "none",
......@@ -237,7 +238,7 @@ class HostType(object):
return None
@staticmethod
def from_tuple_string(s):
def from_tuple_string(s) -> "HostType":
ostype, distro, distrovers = s.split("-")
return HostType(ostype=ostype, distro=distro, distrovers=distrovers)
......
......@@ -212,7 +212,7 @@ class PythonWheelBuilder(BuilderBase):
f.write(CMAKE_FOOTER.format(**self.template_format_dict))
def _write_cmake_config_template(self):
def _write_cmake_config_template(self) -> None:
config_path_name = self.manifest.name + "-config.cmake.in"
output_path = os.path.join(self.build_dir, config_path_name)
......
......@@ -22,7 +22,7 @@ class RunCommandError(Exception):
pass
def _print_env_diff(env, log_fn):
def _print_env_diff(env, log_fn) -> None:
current_keys = set(os.environ.keys())
wanted_env = set(env.keys())
......@@ -44,7 +44,7 @@ def _print_env_diff(env, log_fn):
log_fn("+ %s=%s \\\n" % (k, shellquote(env[k])))
def run_cmd(cmd, env=None, cwd=None, allow_fail=False, log_file=None):
def run_cmd(cmd, env=None, cwd=None, allow_fail: bool = False, log_file=None):
def log_to_stdout(msg):
sys.stdout.buffer.write(msg.encode(errors="surrogateescape"))
......@@ -64,7 +64,7 @@ def run_cmd(cmd, env=None, cwd=None, allow_fail=False, log_file=None):
)
def _run_cmd(cmd, env, cwd, allow_fail, log_fn):
def _run_cmd(cmd, env, cwd, allow_fail, log_fn) -> int:
log_fn("---\n")
try:
cmd_str = " \\\n+ ".join(shellquote(arg) for arg in cmd)
......
......@@ -8,11 +8,11 @@ class SubCmd(object):
NAME = None
HELP = None
def run(self, args):
def run(self, args) -> int:
"""perform the command"""
return 0
def setup_parser(self, parser):
def setup_parser(self, parser) -> None:
# Subclasses should override setup_parser() if they have any
# command line options or arguments.
pass
......@@ -21,7 +21,7 @@ class SubCmd(object):
CmdTable = []
def add_subcommands(parser, common_args, cmd_table=CmdTable):
def add_subcommands(parser, common_args, cmd_table=CmdTable) -> None:
"""Register parsers for the defined commands with the provided parser"""
for cls in cmd_table:
command = cls()
......
......@@ -10,37 +10,37 @@ from ..expr import parse_expr
class ExprTest(unittest.TestCase):
def test_equal(self):
def test_equal(self) -> None:
valid_variables = {"foo", "some_var", "another_var"}
e = parse_expr("foo=bar", valid_variables)
self.assertTrue(e.eval({"foo": "bar"}))
self.assertFalse(e.eval({"foo": "not-bar"}))
self.assertFalse(e.eval({"not-foo": "bar"}))
def test_not_equal(self):
def test_not_equal(self) -> None:
valid_variables = {"foo"}
e = parse_expr("not(foo=bar)", valid_variables)
self.assertFalse(e.eval({"foo": "bar"}))
self.assertTrue(e.eval({"foo": "not-bar"}))
def test_bad_not(self):
def test_bad_not(self) -> None:
valid_variables = {"foo"}
with self.assertRaises(Exception):
parse_expr("foo=not(bar)", valid_variables)
def test_bad_variable(self):
def test_bad_variable(self) -> None:
valid_variables = {"bar"}
with self.assertRaises(Exception):
parse_expr("foo=bar", valid_variables)
def test_all(self):
def test_all(self) -> None:
valid_variables = {"foo", "baz"}
e = parse_expr("all(foo = bar, baz = qux)", valid_variables)
self.assertTrue(e.eval({"foo": "bar", "baz": "qux"}))
self.assertFalse(e.eval({"foo": "bar", "baz": "nope"}))
self.assertFalse(e.eval({"foo": "nope", "baz": "nope"}))
def test_any(self):
def test_any(self) -> None:
valid_variables = {"foo", "baz"}
e = parse_expr("any(foo = bar, baz = qux)", valid_variables)
self.assertTrue(e.eval({"foo": "bar", "baz": "qux"}))
......
......@@ -12,13 +12,13 @@ from ..manifest import ManifestParser
class ManifestTest(unittest.TestCase):
def test_missing_section(self):
def test_missing_section(self) -> None:
with self.assertRaisesRegex(
Exception, "manifest file test is missing required section manifest"
):
ManifestParser("test", "")
def test_missing_name(self):
def test_missing_name(self) -> None:
with self.assertRaisesRegex(
Exception,
"manifest file test section 'manifest' is missing required field 'name'",
......@@ -30,7 +30,7 @@ class ManifestTest(unittest.TestCase):
""",
)
def test_minimal(self):
def test_minimal(self) -> None:
p = ManifestParser(
"test",
"""
......@@ -41,7 +41,7 @@ name = test
self.assertEqual(p.name, "test")
self.assertEqual(p.fbsource_path, None)
def test_minimal_with_fbsource_path(self):
def test_minimal_with_fbsource_path(self) -> None:
p = ManifestParser(
"test",
"""
......@@ -53,7 +53,7 @@ fbsource_path = fbcode/wat
self.assertEqual(p.name, "test")
self.assertEqual(p.fbsource_path, "fbcode/wat")
def test_unknown_field(self):
def test_unknown_field(self) -> None:
with self.assertRaisesRegex(
Exception,
(
......@@ -70,7 +70,7 @@ invalid.field = woot
""",
)
def test_invalid_section_name(self):
def test_invalid_section_name(self) -> None:
with self.assertRaisesRegex(
Exception, "manifest file test contains unknown section 'invalid.section'"
):
......@@ -85,7 +85,7 @@ foo = bar
""",
)
def test_value_in_dependencies_section(self):
def test_value_in_dependencies_section(self) -> None:
with self.assertRaisesRegex(
Exception,
(
......@@ -105,7 +105,7 @@ foo = bar
""",
)
def test_invalid_conditional_section_name(self):
def test_invalid_conditional_section_name(self) -> None:
with self.assertRaisesRegex(
Exception,
(
......@@ -124,7 +124,7 @@ name = test
""",
)
def test_section_as_args(self):
def test_section_as_args(self) -> None:
p = ManifestParser(
"test",
"""
......@@ -164,7 +164,7 @@ name = test
p2.get_section_as_args("autoconf.args"), ["--prefix=/foo", "--with-woot"]
)
def test_section_as_dict(self):
def test_section_as_dict(self) -> None:
p = ManifestParser(
"test",
"""
......@@ -202,12 +202,12 @@ foo = bar
msg="sections cascade in the order they appear in the manifest",
)
def test_parse_common_manifests(self):
def test_parse_common_manifests(self) -> None:
patch_loader(__name__)
manifests = load_all_manifests(None)
self.assertNotEqual(0, len(manifests), msg="parsed some number of manifests")
def test_mismatch_name(self):
def test_mismatch_name(self) -> None:
with self.assertRaisesRegex(
Exception,
"filename of the manifest 'foo' does not match the manifest name 'bar'",
......@@ -220,7 +220,7 @@ name = bar
""",
)
def test_duplicate_manifest(self):
def test_duplicate_manifest(self) -> None:
patch_loader(__name__, "fixtures/duplicate")
with self.assertRaisesRegex(Exception, "found duplicate manifest 'foo'"):
......
......@@ -10,7 +10,7 @@ from ..platform import HostType
class PlatformTest(unittest.TestCase):
def test_create(self):
def test_create(self) -> None:
p = HostType()
self.assertNotEqual(p.ostype, None, msg="probed and returned something")
......@@ -18,11 +18,11 @@ class PlatformTest(unittest.TestCase):
round_trip = HostType.from_tuple_string(tuple_string)
self.assertEqual(round_trip, p)
def test_rendering_of_none(self):
def test_rendering_of_none(self) -> None:
p = HostType(ostype="foo")
self.assertEqual(p.as_tuple_string(), "foo-none-none")
def test_is_methods(self):
def test_is_methods(self) -> None:
p = HostType(ostype="windows")
self.assertTrue(p.is_windows())
self.assertFalse(p.is_darwin())
......
......@@ -10,7 +10,7 @@ from ..buildopts import find_existing_win32_subst_for_path
class Win32SubstTest(unittest.TestCase):
def test_no_existing_subst(self):
def test_no_existing_subst(self) -> None:
self.assertIsNone(
find_existing_win32_subst_for_path(
r"C:\users\alice\appdata\local\temp\fbcode_builder_getdeps",
......@@ -24,7 +24,7 @@ class Win32SubstTest(unittest.TestCase):
)
)
def test_exact_match_returns_drive_path(self):
def test_exact_match_returns_drive_path(self) -> None:
self.assertEqual(
find_existing_win32_subst_for_path(
r"C:\temp\fbcode_builder_getdeps",
......@@ -40,7 +40,7 @@ class Win32SubstTest(unittest.TestCase):
"X:\\",
)
def test_multiple_exact_matches_returns_arbitrary_drive_path(self):
def test_multiple_exact_matches_returns_arbitrary_drive_path(self) -> None:
self.assertIn(
find_existing_win32_subst_for_path(
r"C:\temp\fbcode_builder_getdeps",
......@@ -53,7 +53,7 @@ class Win32SubstTest(unittest.TestCase):
("X:\\", "Y:\\", "Z:\\"),
)
def test_drive_letter_is_case_insensitive(self):
def test_drive_letter_is_case_insensitive(self) -> None:
self.assertEqual(
find_existing_win32_subst_for_path(
r"C:\temp\fbcode_builder_getdeps",
......@@ -62,7 +62,7 @@ class Win32SubstTest(unittest.TestCase):
"X:\\",
)
def test_path_components_are_case_insensitive(self):
def test_path_components_are_case_insensitive(self) -> None:
self.assertEqual(
find_existing_win32_subst_for_path(
r"C:\TEMP\FBCODE_builder_getdeps",
......
......@@ -43,10 +43,10 @@ class ShellQuoted(namedtuple("ShellQuoted", ("do_not_use_raw_str",))):
"or ShellQuoted.format() instead".format(repr(self))
)
def __repr__(self):
def __repr__(self) -> str:
return "{0}({1})".format(self.__class__.__name__, repr(self.do_not_use_raw_str))
def format(self, **kwargs):
def format(self, **kwargs) -> "ShellQuoted":
"""
Use instead of str.format() when the arguments are either
......@@ -65,7 +65,7 @@ class ShellQuoted(namedtuple("ShellQuoted", ("do_not_use_raw_str",))):
)
def shell_quote(s):
def shell_quote(s) -> ShellQuoted:
"Quotes a string if it is not already quoted"
return (
s
......@@ -74,19 +74,19 @@ def shell_quote(s):
)
def raw_shell(s):
def raw_shell(s: ShellQuoted):
"Not a member of ShellQuoted so we get a useful error for raw strings"
if isinstance(s, ShellQuoted):
return s.do_not_use_raw_str
raise RuntimeError("{0} should have been ShellQuoted".format(s))
def shell_join(delim, it):
def shell_join(delim, it) -> ShellQuoted:
"Joins an iterable of ShellQuoted with a delimiter between each two"
return ShellQuoted(delim.join(raw_shell(s) for s in it))
def path_join(*args):
def path_join(*args) -> ShellQuoted:
"Joins ShellQuoted and raw pieces of paths to make a shell-quoted path"
return ShellQuoted(os.path.join(*[raw_shell(shell_quote(s)) for s in args]))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment