Merge pull request #2573 from koehlma/master

add MSYS2 support and fix Cygwin support
This commit is contained in:
Anthony Scopatz 2018-01-01 13:28:18 -08:00 committed by GitHub
commit adcd20f72f
Failed to generate hash of commit
14 changed files with 131 additions and 61 deletions

View file

@ -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
View 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
View 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
View 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

View file

@ -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):
"""

View file

@ -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')

View file

@ -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:

View file

@ -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):
"""

View file

@ -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')

View file

@ -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:

View file

@ -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:

View file

@ -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(

View file

@ -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')),

View file

@ -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