mirror of
https://github.com/xonsh/xonsh.git
synced 2025-03-06 09:20:57 +01:00
Merge pull request #2573 from koehlma/master
add MSYS2 support and fix Cygwin support
This commit is contained in:
commit
adcd20f72f
14 changed files with 131 additions and 61 deletions
|
@ -11,9 +11,17 @@ environment:
|
|||
DISTUTILS_USE_SDK: "1"
|
||||
- PYTHON: "C:\\Python35-x64"
|
||||
- PYTHON: "C:\\Python36-x64"
|
||||
- XONSH_TEST_ENV: "MSYS2"
|
||||
MSYS2_PATH: "C:\\msys64"
|
||||
# TODO: Miniconda
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
# ignore MSYS2 test results for now
|
||||
- XONSH_TEST_ENV: "MSYS2"
|
||||
|
||||
install:
|
||||
- "%PYTHON%\\Scripts\\pip install -r requirements-tests.txt"
|
||||
- ".appveyor_install.cmd"
|
||||
build: off
|
||||
test_script:
|
||||
- "%PYTHON%\\Scripts\\py.test"
|
||||
- ".appveyor_test.cmd"
|
||||
|
|
11
.appveyor_install.cmd
Normal file
11
.appveyor_install.cmd
Normal file
|
@ -0,0 +1,11 @@
|
|||
@ECHO OFF
|
||||
|
||||
IF "%XONSH_TEST_ENV%" == "MSYS2" (
|
||||
echo "MSYS2 Environment"
|
||||
%MSYS2_PATH%\usr\bin\pacman.exe -Syu --noconfirm
|
||||
%MSYS2_PATH%\usr\bin\pacman.exe -S --noconfirm python3 python3-pip
|
||||
%MSYS2_PATH%\usr\bin\bash.exe -c "/usr/bin/pip install -r requirements-tests.txt"
|
||||
) ELSE (
|
||||
echo "Windows Environment"
|
||||
%PYTHON%\Scripts\pip install -r requirements-tests.txt
|
||||
)
|
12
.appveyor_test.cmd
Normal file
12
.appveyor_test.cmd
Normal file
|
@ -0,0 +1,12 @@
|
|||
@ECHO OFF
|
||||
|
||||
IF "%XONSH_TEST_ENV%" == "MSYS2" (
|
||||
echo "MSYS2 Environment"
|
||||
REM We monkey path `py._path.local.PosixPath` here such that it does not
|
||||
REM allow to create symlinks which are not supported by MSYS2 anyway. As a
|
||||
REM result the other pytest code uses a workaround.
|
||||
call %MSYS2_PATH%\usr\bin\bash.exe -c "/usr/bin/python -u -c 'import py._path.local; del py._path.local.PosixPath.mksymlinkto; import pytest; raise SystemExit(pytest.main())'" || EXIT 1
|
||||
) ELSE (
|
||||
echo "Windows Environment"
|
||||
call %PYTHON%\Scripts\py.test || EXIT 1
|
||||
)
|
15
news/msys2.rst
Normal file
15
news/msys2.rst
Normal file
|
@ -0,0 +1,15 @@
|
|||
**Added:**
|
||||
|
||||
* Support for MSYS2.
|
||||
|
||||
**Changed:** None
|
||||
|
||||
**Deprecated:** None
|
||||
|
||||
**Removed:** None
|
||||
|
||||
**Fixed:**
|
||||
|
||||
* Unexpected process suspension on Cygwin and MSYS2.
|
||||
|
||||
**Security:** None
|
|
@ -6,10 +6,11 @@ import os
|
|||
import pytest
|
||||
from xontrib.voxapi import Vox
|
||||
|
||||
from tools import skip_if_on_conda
|
||||
from tools import skip_if_on_conda, skip_if_on_msys
|
||||
from xonsh.platform import ON_WINDOWS
|
||||
|
||||
|
||||
@skip_if_on_msys
|
||||
@skip_if_on_conda
|
||||
def test_crud(xonsh_builtins, tmpdir):
|
||||
"""
|
||||
|
@ -47,6 +48,7 @@ def test_crud(xonsh_builtins, tmpdir):
|
|||
assert last_event == ('delete', 'spam')
|
||||
|
||||
|
||||
@skip_if_on_msys
|
||||
@skip_if_on_conda
|
||||
def test_activate(xonsh_builtins, tmpdir):
|
||||
"""
|
||||
|
@ -78,6 +80,7 @@ def test_activate(xonsh_builtins, tmpdir):
|
|||
assert last_event == ('deactivate', 'spam')
|
||||
|
||||
|
||||
@skip_if_on_msys
|
||||
@skip_if_on_conda
|
||||
def test_path(xonsh_builtins, tmpdir):
|
||||
"""
|
||||
|
@ -100,6 +103,7 @@ def test_path(xonsh_builtins, tmpdir):
|
|||
assert oldpath == xonsh_builtins.__xonsh_env__['PATH']
|
||||
|
||||
|
||||
@skip_if_on_msys
|
||||
@skip_if_on_conda
|
||||
def test_crud_subdir(xonsh_builtins, tmpdir):
|
||||
"""
|
||||
|
@ -130,6 +134,7 @@ try:
|
|||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
@skip_if_on_msys
|
||||
@skip_if_on_conda
|
||||
def test_crud_path(xonsh_builtins, tmpdir):
|
||||
"""
|
||||
|
@ -150,6 +155,7 @@ else:
|
|||
assert not tmpdir.check()
|
||||
|
||||
|
||||
@skip_if_on_msys
|
||||
@skip_if_on_conda
|
||||
def test_crud_subdir(xonsh_builtins, tmpdir):
|
||||
"""
|
||||
|
|
|
@ -24,6 +24,7 @@ VER_MAJOR_MINOR = sys.version_info[:2]
|
|||
VER_FULL = sys.version_info[:3]
|
||||
ON_DARWIN = (platform.system() == 'Darwin')
|
||||
ON_WINDOWS = (platform.system() == 'Windows')
|
||||
ON_MSYS = (sys.platform == 'msys')
|
||||
ON_CONDA = True in [conda in pytest.__file__.lower() for conda
|
||||
in ['conda', 'anaconda', 'miniconda']]
|
||||
ON_TRAVIS = 'TRAVIS' in os.environ and 'CI' in os.environ
|
||||
|
@ -36,6 +37,9 @@ skip_if_lt_py36 = pytest.mark.skipif(VER_MAJOR_MINOR < VER_3_6, reason="Py3.6+ o
|
|||
skip_if_on_conda = pytest.mark.skipif(ON_CONDA,
|
||||
reason="Conda and virtualenv _really_ hate each other")
|
||||
|
||||
skip_if_on_msys = pytest.mark.skipif(ON_MSYS,
|
||||
reason="MSYS and virtualenv _really_ hate each other")
|
||||
|
||||
skip_if_on_windows = pytest.mark.skipif(ON_WINDOWS, reason='Unix stuff')
|
||||
|
||||
skip_if_on_unix = pytest.mark.skipif(not ON_WINDOWS, reason='Windows stuff')
|
||||
|
|
|
@ -14,7 +14,7 @@ import collections.abc as cabc
|
|||
|
||||
from xonsh.lazyasd import lazyobject
|
||||
from xonsh.tools import to_bool, ensure_string
|
||||
from xonsh.platform import ON_WINDOWS, ON_CYGWIN, os_environ
|
||||
from xonsh.platform import ON_WINDOWS, ON_CYGWIN, ON_MSYS, os_environ
|
||||
|
||||
COMMAND = """{seterrprevcmd}
|
||||
{prevcmd}
|
||||
|
@ -291,7 +291,8 @@ def foreign_shell_data(shell, interactive=True, login=False, envcmd=None,
|
|||
s = subprocess.check_output(cmd, stderr=subprocess.PIPE, env=currenv,
|
||||
# start new session to avoid hangs
|
||||
# (doesn't work on Cygwin though)
|
||||
start_new_session=(not ON_CYGWIN),
|
||||
start_new_session=((not ON_CYGWIN) and
|
||||
(not ON_MSYS)),
|
||||
universal_newlines=True)
|
||||
except (subprocess.CalledProcessError, FileNotFoundError):
|
||||
if not safe:
|
||||
|
|
|
@ -10,7 +10,8 @@ import subprocess
|
|||
import collections
|
||||
|
||||
from xonsh.lazyasd import LazyObject
|
||||
from xonsh.platform import FD_STDERR, ON_DARWIN, ON_WINDOWS, ON_CYGWIN, LIBC
|
||||
from xonsh.platform import (FD_STDERR, ON_DARWIN, ON_WINDOWS, ON_CYGWIN,
|
||||
ON_MSYS, LIBC)
|
||||
from xonsh.tools import unthreadable
|
||||
|
||||
|
||||
|
@ -34,7 +35,7 @@ if ON_DARWIN:
|
|||
pass
|
||||
elif ON_WINDOWS:
|
||||
pass
|
||||
elif ON_CYGWIN:
|
||||
elif ON_CYGWIN or ON_MSYS:
|
||||
# Similar to what happened on OSX, more issues on Cygwin
|
||||
# (see Github issue #514).
|
||||
def _send_signal(job, signal):
|
||||
|
@ -110,55 +111,60 @@ else:
|
|||
signal.SIGTSTP, signal.SIGCHLD),
|
||||
globals(), '_block_when_giving')
|
||||
|
||||
# give_terminal_to is a simplified version of:
|
||||
# give_terminal_to from bash 4.3 source, jobs.c, line 4030
|
||||
# this will give the terminal to the process group pgid
|
||||
if ON_CYGWIN:
|
||||
if ON_CYGWIN or ON_MSYS:
|
||||
# on cygwin, signal.pthread_sigmask does not exist in Python, even
|
||||
# though pthread_sigmask is defined in the kernel. thus, we use
|
||||
# ctypes to mimic the calls in the "normal" version below.
|
||||
def give_terminal_to(pgid):
|
||||
omask = ctypes.c_ulong()
|
||||
mask = ctypes.c_ulong()
|
||||
LIBC.sigemptyset(ctypes.byref(mask))
|
||||
for i in _block_when_giving:
|
||||
LIBC.sigaddset(ctypes.byref(mask), ctypes.c_int(i))
|
||||
LIBC.sigemptyset(ctypes.byref(omask))
|
||||
LIBC.sigprocmask(ctypes.c_int(signal.SIG_BLOCK),
|
||||
ctypes.byref(mask),
|
||||
ctypes.byref(omask))
|
||||
LIBC.tcsetpgrp(ctypes.c_int(FD_STDERR), ctypes.c_int(pgid))
|
||||
LIBC.sigprocmask(ctypes.c_int(signal.SIG_SETMASK),
|
||||
ctypes.byref(omask), None)
|
||||
return True
|
||||
LIBC.pthread_sigmask.restype = ctypes.c_int
|
||||
LIBC.pthread_sigmask.argtypes = [ctypes.c_int,
|
||||
ctypes.POINTER(ctypes.c_ulong),
|
||||
ctypes.POINTER(ctypes.c_ulong)]
|
||||
|
||||
def _pthread_sigmask(how, signals):
|
||||
mask = 0
|
||||
for sig in signals:
|
||||
mask |= 1 << sig
|
||||
oldmask = ctypes.c_ulong()
|
||||
mask = ctypes.c_ulong(mask)
|
||||
result = LIBC.pthread_sigmask(how, ctypes.byref(mask),
|
||||
ctypes.byref(oldmask))
|
||||
if result:
|
||||
raise OSError(result, 'Sigmask error.')
|
||||
|
||||
return {sig for sig in getattr(signal, 'Signals', range(0, 65))
|
||||
if (oldmask.value >> sig) & 1}
|
||||
else:
|
||||
def give_terminal_to(pgid):
|
||||
if pgid is None:
|
||||
_pthread_sigmask = signal.pthread_sigmask
|
||||
|
||||
# give_terminal_to is a simplified version of:
|
||||
# give_terminal_to from bash 4.3 source, jobs.c, line 4030
|
||||
# this will give the terminal to the process group pgid
|
||||
def give_terminal_to(pgid):
|
||||
if pgid is None:
|
||||
return False
|
||||
oldmask = _pthread_sigmask(signal.SIG_BLOCK, _block_when_giving)
|
||||
try:
|
||||
os.tcsetpgrp(FD_STDERR, pgid)
|
||||
return True
|
||||
except ProcessLookupError:
|
||||
# when the process finished before giving terminal to it,
|
||||
# see issue #2288
|
||||
return False
|
||||
except OSError as e:
|
||||
if e.errno == 22: # [Errno 22] Invalid argument
|
||||
# there are cases that all the processes of pgid have
|
||||
# finished, then we don't need to do anything here, see
|
||||
# issue #2220
|
||||
return False
|
||||
oldmask = signal.pthread_sigmask(signal.SIG_BLOCK,
|
||||
_block_when_giving)
|
||||
try:
|
||||
os.tcsetpgrp(FD_STDERR, pgid)
|
||||
return True
|
||||
except ProcessLookupError:
|
||||
# when the process finished before giving terminal to it,
|
||||
# see issue #2288
|
||||
elif e.errno == 25: # [Errno 25] Inappropriate ioctl for device
|
||||
# There are also cases where we are not connected to a
|
||||
# real TTY, even though we may be run in interactive
|
||||
# mode. See issue #2267 for an example with emacs
|
||||
return False
|
||||
except OSError as e:
|
||||
if e.errno == 22: # [Errno 22] Invalid argument
|
||||
# there are cases that all the processes of pgid have
|
||||
# finished, then we don't need to do anything here, see
|
||||
# issue #2220
|
||||
return False
|
||||
elif e.errno == 25: # [Errno 25] Inappropriate ioctl for device
|
||||
# There are also cases where we are not connected to a
|
||||
# real TTY, even though we may be run in interactive
|
||||
# mode. See issue #2267 for an example with emacs
|
||||
return False
|
||||
else:
|
||||
raise
|
||||
finally:
|
||||
signal.pthread_sigmask(signal.SIG_SETMASK, oldmask)
|
||||
else:
|
||||
raise
|
||||
finally:
|
||||
_pthread_sigmask(signal.SIG_SETMASK, oldmask)
|
||||
|
||||
def wait_for_active_job(last_task=None, backgrounded=False):
|
||||
"""
|
||||
|
|
|
@ -47,6 +47,8 @@ ON_WINDOWS = LazyBool(lambda: platform.system() == 'Windows',
|
|||
"""``True`` if executed on a native Windows platform, else ``False``. """
|
||||
ON_CYGWIN = LazyBool(lambda: sys.platform == 'cygwin', globals(), 'ON_CYGWIN')
|
||||
"""``True`` if executed on a Cygwin Windows platform, else ``False``. """
|
||||
ON_MSYS = LazyBool(lambda: sys.platform == 'msys', globals(), 'ON_MSYS')
|
||||
"""``True`` if executed on a MSYS Windows platform, else ``False``. """
|
||||
ON_POSIX = LazyBool(lambda: (os.name == 'posix'), globals(), 'ON_POSIX')
|
||||
"""``True`` if executed on a POSIX-compliant platform, else ``False``. """
|
||||
ON_FREEBSD = LazyBool(lambda: (sys.platform.startswith('freebsd')),
|
||||
|
@ -440,7 +442,7 @@ def BASH_COMPLETIONS_DEFAULT():
|
|||
"""A possibly empty tuple with default paths to Bash completions known for
|
||||
the current platform.
|
||||
"""
|
||||
if ON_LINUX or ON_CYGWIN:
|
||||
if ON_LINUX or ON_CYGWIN or ON_MSYS:
|
||||
bcd = ('/usr/share/bash-completion/bash_completion', )
|
||||
elif ON_DARWIN:
|
||||
bcd = ('/usr/local/share/bash-completion/bash_completion', # v2.x
|
||||
|
@ -458,7 +460,7 @@ def BASH_COMPLETIONS_DEFAULT():
|
|||
|
||||
@lazyobject
|
||||
def PATH_DEFAULT():
|
||||
if ON_LINUX or ON_CYGWIN:
|
||||
if ON_LINUX or ON_CYGWIN or ON_MSYS:
|
||||
if linux_distro() == 'arch':
|
||||
pd = ('/usr/local/sbin',
|
||||
'/usr/local/bin', '/usr/bin', '/usr/bin/site_perl',
|
||||
|
@ -489,6 +491,8 @@ def LIBC():
|
|||
libc = ctypes.CDLL(ctypes.util.find_library("c"))
|
||||
elif ON_CYGWIN:
|
||||
libc = ctypes.CDLL('cygwin1.dll')
|
||||
elif ON_MSYS:
|
||||
libc = ctypes.CDLL('msys-2.0.dll')
|
||||
elif ON_BSD:
|
||||
try:
|
||||
libc = ctypes.CDLL('libc.so')
|
||||
|
|
|
@ -23,8 +23,8 @@ import threading
|
|||
import subprocess
|
||||
import collections.abc as cabc
|
||||
|
||||
from xonsh.platform import (ON_WINDOWS, ON_POSIX, CAN_RESIZE_WINDOW,
|
||||
LFLAG, CC)
|
||||
from xonsh.platform import (ON_WINDOWS, ON_POSIX, ON_MSYS, ON_CYGWIN,
|
||||
CAN_RESIZE_WINDOW, LFLAG, CC)
|
||||
from xonsh.tools import (redirect_stdout, redirect_stderr, print_exception,
|
||||
XonshCalledProcessError, findfirst, on_main_thread,
|
||||
XonshError, format_std_prepost)
|
||||
|
@ -2285,7 +2285,8 @@ def pause_call_resume(p, f, *args, **kwargs):
|
|||
args : remaining arguments
|
||||
kwargs : keyword arguments
|
||||
"""
|
||||
can_send_signal = hasattr(p, 'send_signal') and ON_POSIX
|
||||
can_send_signal = (hasattr(p, 'send_signal') and ON_POSIX and
|
||||
not ON_MSYS and not ON_CYGWIN)
|
||||
if can_send_signal:
|
||||
p.send_signal(signal.SIGSTOP)
|
||||
try:
|
||||
|
|
|
@ -116,7 +116,7 @@ def _FORMATTER():
|
|||
|
||||
def default_prompt():
|
||||
"""Creates a new instance of the default prompt."""
|
||||
if xp.ON_CYGWIN:
|
||||
if xp.ON_CYGWIN or xp.ON_MSYS:
|
||||
dp = ('{env_name:{} }{BOLD_GREEN}{user}@{hostname}'
|
||||
'{BOLD_BLUE} {cwd} {prompt_end}{NO_COLOR} ')
|
||||
elif xp.ON_WINDOWS:
|
||||
|
|
|
@ -28,7 +28,8 @@ from xonsh.ansi_colors import (ansi_partial_color_format, ansi_color_style_names
|
|||
from xonsh.prompt.base import multiline_prompt
|
||||
from xonsh.tools import (print_exception, check_for_partial_string, to_bool,
|
||||
columnize, carriage_return)
|
||||
from xonsh.platform import ON_WINDOWS, ON_CYGWIN, ON_DARWIN, ON_POSIX, os_environ
|
||||
from xonsh.platform import (ON_WINDOWS, ON_CYGWIN, ON_MSYS, ON_DARWIN, ON_POSIX,
|
||||
os_environ)
|
||||
from xonsh.lazyimps import pygments, pyghooks, winutils
|
||||
from xonsh.events import events
|
||||
|
||||
|
@ -71,7 +72,7 @@ def setup_readline():
|
|||
uses_libedit = readline.__doc__ and 'libedit' in readline.__doc__
|
||||
readline.set_completer_delims(' \t\n')
|
||||
# Cygwin seems to hang indefinitely when querying the readline lib
|
||||
if (not ON_CYGWIN) and (not readline.__file__.endswith('.py')):
|
||||
if (not ON_CYGWIN) and (not ON_MSYS) and (not readline.__file__.endswith('.py')):
|
||||
RL_LIB = lib = ctypes.cdll.LoadLibrary(readline.__file__)
|
||||
try:
|
||||
RL_COMPLETION_SUPPRESS_APPEND = ctypes.c_int.in_dll(
|
||||
|
|
|
@ -23,7 +23,7 @@ from xonsh import __version__ as XONSH_VERSION
|
|||
from xonsh.prompt.base import is_template_string
|
||||
from xonsh.platform import (is_readline_available, ptk_version,
|
||||
PYTHON_VERSION_INFO, pygments_version, ON_POSIX, ON_LINUX, linux_distro,
|
||||
ON_DARWIN, ON_WINDOWS, ON_CYGWIN, DEFAULT_ENCODING, githash)
|
||||
ON_DARWIN, ON_WINDOWS, ON_CYGWIN, DEFAULT_ENCODING, ON_MSYS, githash)
|
||||
from xonsh.tools import (to_bool, is_string, print_exception, is_superuser,
|
||||
color_style_names, print_color, color_style)
|
||||
from xonsh.xontribs import xontrib_metadata, find_xontrib
|
||||
|
@ -382,6 +382,7 @@ def _info(ns):
|
|||
('on darwin', ON_DARWIN),
|
||||
('on windows', ON_WINDOWS),
|
||||
('on cygwin', ON_CYGWIN),
|
||||
('on msys2', ON_MSYS),
|
||||
('is superuser', is_superuser()),
|
||||
('default encoding', DEFAULT_ENCODING),
|
||||
('xonsh encoding', env.get('XONSH_ENCODING')),
|
||||
|
|
|
@ -13,7 +13,7 @@ import functools
|
|||
from pathlib import Path
|
||||
|
||||
from xonsh.tools import print_exception
|
||||
from xonsh.platform import ON_WINDOWS, ON_CYGWIN
|
||||
from xonsh.platform import ON_WINDOWS, ON_CYGWIN, ON_MSYS
|
||||
|
||||
|
||||
def _chdir_up(path):
|
||||
|
@ -89,7 +89,7 @@ def _cwd_restore_wrapper(func):
|
|||
|
||||
@events.on_ptk_create
|
||||
def setup_release_cwd_hook(prompter, history, completer, bindings, **kw):
|
||||
if ON_WINDOWS and not ON_CYGWIN:
|
||||
if ON_WINDOWS and not ON_CYGWIN and not ON_MSYS:
|
||||
prompter.prompt = _cwd_release_wrapper(prompter.prompt)
|
||||
if completer.completer:
|
||||
# Temporarily restore cwd for callbacks to the completer
|
||||
|
|
Loading…
Add table
Reference in a new issue