diff options
author | Nirbheek Chauhan <nirbheek@centricular.com> | 2016-07-30 15:19:11 +0530 |
---|---|---|
committer | Nirbheek Chauhan <nirbheek@centricular.com> | 2016-07-30 15:20:01 +0530 |
commit | 577b6dfdf63cde697ff33226950175a93578e2b8 (patch) | |
tree | 48058766e57be3559518295db3d9c908489734c4 /mesonbuild/dependencies.py | |
parent | 4a92b78e6e4508afb8ca988cb47c91ea4a765fee (diff) | |
download | meson-577b6dfdf63cde697ff33226950175a93578e2b8.zip meson-577b6dfdf63cde697ff33226950175a93578e2b8.tar.gz meson-577b6dfdf63cde697ff33226950175a93578e2b8.tar.bz2 |
find_program: Find scripts without extensions on Windows
Because of how files and executables work on Windows, scripts that use
an interpreter must have an extension, and that extension must be
associated with an interpreter. The full list of executable extensions
is available in the PATHEXT environment variable.
However, UNIX-like OSes use an executable bit and read the shebang to
figure out what interpreter to use, and the scripts don't need to have
extensions. We can now detect these scripts using find_program by
manually searching in PATH and reading the shebang.
Diffstat (limited to 'mesonbuild/dependencies.py')
-rw-r--r-- | mesonbuild/dependencies.py | 87 |
1 files changed, 62 insertions, 25 deletions
diff --git a/mesonbuild/dependencies.py b/mesonbuild/dependencies.py index 3f1e214..b4f825b 100644 --- a/mesonbuild/dependencies.py +++ b/mesonbuild/dependencies.py @@ -353,37 +353,13 @@ class WxDependency(Dependency): class ExternalProgram(): def __init__(self, name, fullpath=None, silent=False, search_dir=None): self.name = name - self.fullpath = None if fullpath is not None: if not isinstance(fullpath, list): self.fullpath = [fullpath] else: self.fullpath = fullpath else: - self.fullpath = [shutil.which(name)] - if self.fullpath[0] is None and search_dir is not None: - trial = os.path.join(search_dir, name) - suffix = os.path.splitext(trial)[-1].lower()[1:] - if mesonlib.is_windows() and (suffix == 'exe' or suffix == 'com'\ - or suffix == 'bat'): - self.fullpath = [trial] - elif not mesonlib.is_windows() and os.access(trial, os.X_OK): - self.fullpath = [trial] - else: - # Now getting desperate. Maybe it is a script file that is a) not chmodded - # executable or b) we are on windows so they can't be directly executed. - try: - first_line = open(trial).readline().strip() - if first_line.startswith('#!'): - commands = first_line[2:].split('#')[0].strip().split() - if mesonlib.is_windows(): - # Windows does not have /usr/bin. - commands[0] = commands[0].split('/')[-1] - if commands[0] == 'env': - commands = commands[1:] - self.fullpath = commands + [trial] - except Exception: - pass + self.fullpath = self._search(name, search_dir) if not silent: if self.found(): mlog.log('Program', mlog.bold(name), 'found:', mlog.green('YES'), @@ -391,6 +367,67 @@ class ExternalProgram(): else: mlog.log('Program', mlog.bold(name), 'found:', mlog.red('NO')) + @staticmethod + def _shebang_to_cmd(script): + """ + Windows does not understand shebangs, so we check if the file has a + shebang and manually parse it to figure out the interpreter to use + """ + try: + first_line = open(script).readline().strip() + if first_line.startswith('#!'): + commands = first_line[2:].split('#')[0].strip().split() + if mesonlib.is_windows(): + # Windows does not have /usr/bin. + commands[0] = commands[0].split('/')[-1] + if commands[0] == 'env': + commands = commands[1:] + return commands + [script] + except Exception: + pass + return False + + @staticmethod + def _is_executable(path): + suffix = os.path.splitext(path)[-1].lower()[1:] + if mesonlib.is_windows(): + if suffix == 'exe' or suffix == 'com' or suffix == 'bat': + return True + elif os.access(path, os.X_OK): + return True + return False + + def _search_dir(self, name, search_dir): + if search_dir is None: + return False + trial = os.path.join(search_dir, name) + if not os.path.exists(trial): + return False + if self._is_executable(trial): + return [trial] + # Now getting desperate. Maybe it is a script file that is a) not chmodded + # executable or b) we are on windows so they can't be directly executed. + return self._shebang_to_cmd(trial) + + def _search(self, name, search_dir): + commands = self._search_dir(name, search_dir) + if commands: + return commands + # Do a standard search in PATH + fullpath = shutil.which(name) + if fullpath or not mesonlib.is_windows(): + # On UNIX-like platforms, the standard PATH search is enough + return [fullpath] + # On Windows, interpreted scripts must have an extension otherwise they + # cannot be found by a standard PATH search. So we do a custom search + # where we manually search for a script with a shebang in PATH. + search_dirs = os.environ.get('PATH', '').split(';') + for search_dir in search_dirs: + commands = self._search_dir(name, search_dir) + if commands: + return commands + return [None] + def found(self): return self.fullpath[0] is not None |