|
|
@ -31,14 +31,17 @@ if T.TYPE_CHECKING: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ExternalProgram: |
|
|
|
class ExternalProgram: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""A program that is found on the system.""" |
|
|
|
|
|
|
|
|
|
|
|
windows_exts = ('exe', 'msc', 'com', 'bat', 'cmd') |
|
|
|
windows_exts = ('exe', 'msc', 'com', 'bat', 'cmd') |
|
|
|
# An 'ExternalProgram' always runs on the build machine |
|
|
|
|
|
|
|
for_machine = MachineChoice.BUILD |
|
|
|
for_machine = MachineChoice.BUILD |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, name: str, command: T.Optional[T.List[str]] = None, |
|
|
|
def __init__(self, name: str, command: T.Optional[T.List[str]] = None, |
|
|
|
silent: bool = False, search_dir: T.Optional[str] = None, |
|
|
|
silent: bool = False, search_dir: T.Optional[str] = None, |
|
|
|
extra_search_dirs: T.Optional[T.List[str]] = None): |
|
|
|
extra_search_dirs: T.Optional[T.List[str]] = None): |
|
|
|
self.name = name |
|
|
|
self.name = name |
|
|
|
|
|
|
|
self.path = None # type: T.Optional[str] |
|
|
|
if command is not None: |
|
|
|
if command is not None: |
|
|
|
self.command = mesonlib.listify(command) |
|
|
|
self.command = mesonlib.listify(command) |
|
|
|
if mesonlib.is_windows(): |
|
|
|
if mesonlib.is_windows(): |
|
|
@ -61,15 +64,16 @@ class ExternalProgram: |
|
|
|
if self.found(): |
|
|
|
if self.found(): |
|
|
|
break |
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if self.found(): |
|
|
|
# Set path to be the last item that is actually a file (in order to |
|
|
|
# Set path to be the last item that is actually a file (in order to |
|
|
|
# skip options in something like ['python', '-u', 'file.py']. If we |
|
|
|
# skip options in something like ['python', '-u', 'file.py']. If we |
|
|
|
# can't find any components, default to the last component of the path. |
|
|
|
# can't find any components, default to the last component of the path. |
|
|
|
self.path = self.command[-1] |
|
|
|
for arg in reversed(self.command): |
|
|
|
for i in range(len(self.command) - 1, -1, -1): |
|
|
|
|
|
|
|
arg = self.command[i] |
|
|
|
|
|
|
|
if arg is not None and os.path.isfile(arg): |
|
|
|
if arg is not None and os.path.isfile(arg): |
|
|
|
self.path = arg |
|
|
|
self.path = arg |
|
|
|
break |
|
|
|
break |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
self.path = self.command[-1] |
|
|
|
|
|
|
|
|
|
|
|
if not silent: |
|
|
|
if not silent: |
|
|
|
# ignore the warning because derived classes never call this __init__ |
|
|
|
# ignore the warning because derived classes never call this __init__ |
|
|
@ -94,7 +98,7 @@ class ExternalProgram: |
|
|
|
return ' '.join(self.command) |
|
|
|
return ' '.join(self.command) |
|
|
|
|
|
|
|
|
|
|
|
@classmethod |
|
|
|
@classmethod |
|
|
|
def from_bin_list(cls, env: 'Environment', for_machine: MachineChoice, name): |
|
|
|
def from_bin_list(cls, env: 'Environment', for_machine: MachineChoice, name: str) -> 'ExternalProgram': |
|
|
|
# There is a static `for_machine` for this class because the binary |
|
|
|
# There is a static `for_machine` for this class because the binary |
|
|
|
# aways runs on the build platform. (It's host platform is our build |
|
|
|
# aways runs on the build platform. (It's host platform is our build |
|
|
|
# platform.) But some external programs have a target platform, so this |
|
|
|
# platform.) But some external programs have a target platform, so this |
|
|
@ -129,20 +133,22 @@ class ExternalProgram: |
|
|
|
return os.pathsep.join(paths) |
|
|
|
return os.pathsep.join(paths) |
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
@staticmethod |
|
|
|
def from_entry(name, command): |
|
|
|
def from_entry(name: str, command: T.Union[str, T.List[str]]) -> 'ExternalProgram': |
|
|
|
if isinstance(command, list): |
|
|
|
if isinstance(command, list): |
|
|
|
if len(command) == 1: |
|
|
|
if len(command) == 1: |
|
|
|
command = command[0] |
|
|
|
command = command[0] |
|
|
|
# We cannot do any searching if the command is a list, and we don't |
|
|
|
# We cannot do any searching if the command is a list, and we don't |
|
|
|
# need to search if the path is an absolute path. |
|
|
|
# need to search if the path is an absolute path. |
|
|
|
if isinstance(command, list) or os.path.isabs(command): |
|
|
|
if isinstance(command, list) or os.path.isabs(command): |
|
|
|
|
|
|
|
if isinstance(command, str): |
|
|
|
|
|
|
|
command = [command] |
|
|
|
return ExternalProgram(name, command=command, silent=True) |
|
|
|
return ExternalProgram(name, command=command, silent=True) |
|
|
|
assert isinstance(command, str) |
|
|
|
assert isinstance(command, str) |
|
|
|
# Search for the command using the specified string! |
|
|
|
# Search for the command using the specified string! |
|
|
|
return ExternalProgram(command, silent=True) |
|
|
|
return ExternalProgram(command, silent=True) |
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
@staticmethod |
|
|
|
def _shebang_to_cmd(script: str) -> T.Optional[list]: |
|
|
|
def _shebang_to_cmd(script: str) -> T.Optional[T.List[str]]: |
|
|
|
""" |
|
|
|
""" |
|
|
|
Check if the file has a shebang and manually parse it to figure out |
|
|
|
Check if the file has a shebang and manually parse it to figure out |
|
|
|
the interpreter to use. This is useful if the script is not executable |
|
|
|
the interpreter to use. This is useful if the script is not executable |
|
|
@ -184,11 +190,11 @@ class ExternalProgram: |
|
|
|
commands = mesonlib.python_command + commands[1:] |
|
|
|
commands = mesonlib.python_command + commands[1:] |
|
|
|
return commands + [script] |
|
|
|
return commands + [script] |
|
|
|
except Exception as e: |
|
|
|
except Exception as e: |
|
|
|
mlog.debug(e) |
|
|
|
mlog.debug(str(e)) |
|
|
|
mlog.debug(f'Unusable script {script!r}') |
|
|
|
mlog.debug(f'Unusable script {script!r}') |
|
|
|
return None |
|
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
def _is_executable(self, path): |
|
|
|
def _is_executable(self, path: str) -> bool: |
|
|
|
suffix = os.path.splitext(path)[-1].lower()[1:] |
|
|
|
suffix = os.path.splitext(path)[-1].lower()[1:] |
|
|
|
execmask = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH |
|
|
|
execmask = stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH |
|
|
|
if mesonlib.is_windows(): |
|
|
|
if mesonlib.is_windows(): |
|
|
@ -217,7 +223,7 @@ class ExternalProgram: |
|
|
|
return [trial_ext] |
|
|
|
return [trial_ext] |
|
|
|
return None |
|
|
|
return None |
|
|
|
|
|
|
|
|
|
|
|
def _search_windows_special_cases(self, name: str, command: str) -> list: |
|
|
|
def _search_windows_special_cases(self, name: str, command: str) -> T.List[T.Optional[str]]: |
|
|
|
''' |
|
|
|
''' |
|
|
|
Lots of weird Windows quirks: |
|
|
|
Lots of weird Windows quirks: |
|
|
|
1. PATH search for @name returns files with extensions from PATHEXT, |
|
|
|
1. PATH search for @name returns files with extensions from PATHEXT, |
|
|
@ -260,7 +266,7 @@ class ExternalProgram: |
|
|
|
return commands |
|
|
|
return commands |
|
|
|
return [None] |
|
|
|
return [None] |
|
|
|
|
|
|
|
|
|
|
|
def _search(self, name: str, search_dir: T.Optional[str]) -> list: |
|
|
|
def _search(self, name: str, search_dir: T.Optional[str]) -> T.List[T.Optional[str]]: |
|
|
|
''' |
|
|
|
''' |
|
|
|
Search in the specified dir for the specified executable by name |
|
|
|
Search in the specified dir for the specified executable by name |
|
|
|
and if not found search in PATH |
|
|
|
and if not found search in PATH |
|
|
@ -285,7 +291,7 @@ class ExternalProgram: |
|
|
|
def get_command(self) -> T.List[str]: |
|
|
|
def get_command(self) -> T.List[str]: |
|
|
|
return self.command[:] |
|
|
|
return self.command[:] |
|
|
|
|
|
|
|
|
|
|
|
def get_path(self) -> str: |
|
|
|
def get_path(self) -> T.Optional[str]: |
|
|
|
return self.path |
|
|
|
return self.path |
|
|
|
|
|
|
|
|
|
|
|
def get_name(self) -> str: |
|
|
|
def get_name(self) -> str: |
|
|
@ -314,16 +320,16 @@ class EmptyExternalProgram(ExternalProgram): # lgtm [py/missing-call-to-init] |
|
|
|
such as a cross file exe_wrapper to represent that it's not required. |
|
|
|
such as a cross file exe_wrapper to represent that it's not required. |
|
|
|
''' |
|
|
|
''' |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
|
def __init__(self) -> None: |
|
|
|
self.name = None |
|
|
|
self.name = None |
|
|
|
self.command = [] |
|
|
|
self.command = [] |
|
|
|
self.path = None |
|
|
|
self.path = None |
|
|
|
|
|
|
|
|
|
|
|
def __repr__(self): |
|
|
|
def __repr__(self) -> str: |
|
|
|
r = '<{} {!r} -> {!r}>' |
|
|
|
r = '<{} {!r} -> {!r}>' |
|
|
|
return r.format(self.__class__.__name__, self.name, self.command) |
|
|
|
return r.format(self.__class__.__name__, self.name, self.command) |
|
|
|
|
|
|
|
|
|
|
|
def found(self): |
|
|
|
def found(self) -> bool: |
|
|
|
return True |
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|