1
0
mirror of https://github.com/facebook/proxygen.git synced 2025-08-01 10:26:51 +03:00
Files
Conner Nilsen cf2e8bc5d4 Pyre Configurationless migration for] [batch:88/244] (#723)
Summary: Pull Request resolved: https://github.com/facebookincubator/zstrong/pull/723

Reviewed By: grievejia

Differential Revision: D54471437

fbshipit-source-id: bc644553e31464ceb632034e4ce3f05ba30fbbcd
2024-03-04 18:15:44 -08:00

199 lines
6.3 KiB
Python

# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
# pyre-unsafe
import os
import shlex
import sys
from typing import Optional
class Env(object):
def __init__(self, src=None) -> None:
self._dict = {}
if src is None:
self.update(os.environ)
else:
self.update(src)
def update(self, src) -> None:
for k, v in src.items():
self.set(k, v)
def copy(self) -> "Env":
return Env(self._dict)
def _key(self, key):
# The `str` cast may not appear to be needed, but without it we run
# into issues when passing the environment to subprocess. The main
# issue is that in python2 `os.environ` (which is the initial source
# of data for the environment) uses byte based strings, but this
# project uses `unicode_literals`. `subprocess` will raise an error
# if the environment that it is passed has a mixture of byte and
# unicode strings.
# It is simplest to force everything to be `str` for the sake of
# consistency.
key = str(key)
if sys.platform.startswith("win"):
# Windows env var names are case insensitive but case preserving.
# An implementation of PAR files on windows gets confused if
# the env block contains keys with conflicting case, so make a
# pass over the contents to remove any.
# While this O(n) scan is technically expensive and gross, it
# is practically not a problem because the volume of calls is
# relatively low and the cost of manipulating the env is dwarfed
# by the cost of spawning a process on windows. In addition,
# since the processes that we run are expensive anyway, this
# overhead is not the worst thing to worry about.
for k in list(self._dict.keys()):
if str(k).lower() == key.lower():
return k
elif key in self._dict:
return key
return None
def get(self, key, defval=None):
key = self._key(key)
if key is None:
return defval
return self._dict[key]
def __getitem__(self, key):
val = self.get(key)
if key is None:
raise KeyError(key)
return val
def unset(self, key) -> None:
if key is None:
raise KeyError("attempting to unset env[None]")
key = self._key(key)
if key:
del self._dict[key]
def __delitem__(self, key) -> None:
self.unset(key)
def __repr__(self):
return repr(self._dict)
def set(self, key, value) -> None:
if key is None:
raise KeyError("attempting to assign env[None] = %r" % value)
if value is None:
raise ValueError("attempting to assign env[%s] = None" % key)
# The `str` conversion is important to avoid triggering errors
# with subprocess if we pass in a unicode value; see commentary
# in the `_key` method.
key = str(key)
value = str(value)
# The `unset` call is necessary on windows where the keys are
# case insensitive. Since this dict is case sensitive, simply
# assigning the value to the new key is not sufficient to remove
# the old value. The `unset` call knows how to match keys and
# remove any potential duplicates.
self.unset(key)
self._dict[key] = value
def __setitem__(self, key, value) -> None:
self.set(key, value)
def __iter__(self):
return self._dict.__iter__()
def __len__(self) -> int:
return len(self._dict)
def keys(self):
return self._dict.keys()
def values(self):
return self._dict.values()
def items(self):
return self._dict.items()
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
prepended if `name` already exists."""
val = env.get(name, "")
if len(val) > 0:
val = val.split(separator)
else:
val = []
if append:
val.append(item)
else:
val.insert(0, item)
env.set(name, separator.join(val))
def add_flag(env, name, flag: str, 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
`name` already exists."""
val = shlex.split(env.get(name, ""))
if append:
val.append(flag)
else:
val.insert(0, flag)
env.set(name, " ".join(val))
_path_search_cache = {}
_not_found = object()
def tpx_path() -> str:
return "xplat/testinfra/tpx/ctp.tpx"
def path_search(env, exename: str, defval: Optional[str] = None) -> Optional[str]:
"""Search for exename in the PATH specified in env.
exename is eg: `ninja` and this function knows to append a .exe
to the end on windows.
Returns the path to the exe if found, or None if either no
PATH is set in env or no executable is found."""
path = env.get("PATH", None)
if path is None:
return defval
# The project hash computation code searches for C++ compilers (g++, clang, etc)
# repeatedly. Cache the result so we don't end up searching for these over and over
# again.
cache_key = (path, exename)
result = _path_search_cache.get(cache_key, _not_found)
if result is _not_found:
result = _perform_path_search(path, exename)
_path_search_cache[cache_key] = result
return result
def _perform_path_search(path, exename: str) -> Optional[str]:
is_win = sys.platform.startswith("win")
if is_win:
exename = "%s.exe" % exename
for bindir in path.split(os.pathsep):
full_name = os.path.join(bindir, exename)
if os.path.exists(full_name) and os.path.isfile(full_name):
if not is_win and not os.access(full_name, os.X_OK):
continue
return full_name
return None