mirror of
https://github.com/facebook/proxygen.git
synced 2025-08-07 07:02:53 +03:00
fbcode_builder: getdeps: add envfuncs.py
Summary: Add some helpers for manipulating environment variables that will hold either compiler flags or paths. These are used in later diffs when setting up arguments/environment for eg: cmake. Reviewed By: sinancepel, simpkins Differential Revision: D14690993 fbshipit-source-id: 7f9753cd99d968550fe9e3ba8b7017a44683061e
This commit is contained in:
committed by
Facebook Github Bot
parent
4661be5092
commit
9c1d4ab2e6
175
build/fbcode_builder/getdeps/envfuncs.py
Normal file
175
build/fbcode_builder/getdeps/envfuncs.py
Normal file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python
|
||||
# Copyright (c) 2019-present, Facebook, Inc.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the BSD-style license found in the
|
||||
# LICENSE file in the root directory of this source tree. An additional grant
|
||||
# of patent rights can be found in the PATENTS file in the same directory.
|
||||
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
|
||||
import os
|
||||
import shlex
|
||||
import sys
|
||||
|
||||
|
||||
class Env(object):
|
||||
def __init__(self, src=None):
|
||||
self._dict = {}
|
||||
self.update(src or os.environ)
|
||||
|
||||
def update(self, src):
|
||||
for k, v in src.items():
|
||||
self.set(k, v)
|
||||
|
||||
def copy(self):
|
||||
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 everthing 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):
|
||||
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):
|
||||
self.unset(key)
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self._dict)
|
||||
|
||||
def set(self, key, value):
|
||||
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):
|
||||
self.set(key, value)
|
||||
|
||||
def __iter__(self):
|
||||
return self._dict.__iter__()
|
||||
|
||||
def __len__(self):
|
||||
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=True, separator=os.pathsep):
|
||||
""" 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, append=True):
|
||||
""" 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))
|
||||
|
||||
|
||||
def path_search(env, exename, defval=None):
|
||||
""" 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
|
||||
|
||||
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
|
Reference in New Issue
Block a user