Skip to content

CI: Run tests in Cygwin for Cygwin CI #78

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 51 additions & 9 deletions .github/workflows/tests-cygwin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,38 +17,80 @@ jobs:
runs-on: windows-latest
env:
FORCE_COLOR: true
SHELLOPTS: igncr
CHERE_INVOKING: true
strategy:
fail-fast: false
matrix:
python:
- '3.10'
python-minor:
- '9'

steps:
- name: Tell git to use proper newlines
run: git config --global core.autocrlf input

- name: Checkout
uses: actions/checkout@v2

- name: Set up target Python
uses: actions/setup-python@v2
- uses: actions/cache@v3
with:
python-version: ${{ matrix.python }}
path: 'C:\cygwin'
key: ${{ runner.os }}-cygwin-python-3${{ matrix.python-minor }}-${{ hashfiles('pyproject.toml') }}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not enough. With this the installation path is populated but the installation will be run anyway. I don't think this saves any time. Anyhow, I can live with the CI job taking a couple of minutes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The installation will be run, but that cache includes the list of installed packages; the install step will only download and extract packages if there are new versions (which means I should expire the cache at least once a month). I would very much prefer to run the installer to be sure the dlls get rebased properly (which means I need to run rebase-trigger full so it knows to do that).

You're probably going to have more runins with the length of the CI job; most of the ones I've built take half an hour. Should I take this back out again?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we will be merging this PR either way. The work in #202 supersedes all of it. I just have one funny test error on Cygwin to track down: https://github.com/mesonbuild/meson-python/actions/runs/3456021871/jobs/5768534392 We setup two virtual environments as part of the tests, for some reason the second time we do it, it fails. The annoying thing is that it fails with a backtrace that does not match my copy of the CPython 3.9.15 source code and I haven't been able to locate the Cygwin Python sources

Copy link
Author

@DWesl DWesl Nov 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you've got a Cygwin install, run setup-x86_64.exe again and check the Src? tick box; you may also want to install the cygport package to simplify the build process (cygport python39.cygport download prep compile test install package if you want to do a local install, using the Cygwin source package mentioned above and a local download repository)

You may also want to check the CPython 3.9.10 source, since that's what's getting installed in the failing CI job

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have any Windows machine were to install Cygwin. I was hoping that a git repository with the sources exists somewhere. With your indications above I found it, https://cygwin.com/git-cygwin-packages/?p=git/cygwin-packages/python39.git but it seems the sources are maintained as a set of patches... This is so '90ies... 😄

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accordingly to https://cygwin.com/cgi-bin2/package-grep.cgi there is no Cygwin package that installs something in/usr/share/python-wheels/ so I am not sure I understand how the patch you linked is even supposed to work.

Copy link
Author

@DWesl DWesl Nov 13, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DWesl can you please check the content of /usr/share/python-wheels/ on your Cygwin system?

I have a few hundred wheels in there, because I often create my own packages and make sure my packages install wheels there so I don't have to sit waiting for scipy to build every time I want to test something with tox. I think it worked fine before I started doing that, but I haven't checked.

Also, what does it happen if you do something like python -m venv $(mktemp -d)?

Works the same as the other one, once I found the temporary environment.

It looks like the function assumes there is some file for the package, but only looks for architecture independent packages; is it perhaps finding only sdists and architecture-dependent wheels?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh? Why do you have wheel files in there? Also, wheel files are only a distribution bundle, they need to be unpacked to be used, so if you install a wheel package you don't have any wheel file at the end, except in a cache, maybe. However, /usr/share/python-wheels/ is not the default path of the pip cache.

When venv creates a new virtual environment it optionally populates it with the setuptools and pip packages. Because pip does not exist yet in the virtual environment, the ensurepip module is used for doing it. Customarily, the Python distribution comes with two wheel files for setuptools and pip bundled with it and these are unpacked by ensurepip into the virtual environment. Cygwin decided to patch ensurepip to look for the wheel it needs in /usr/share/python-wheels/ but it does not place any wheel there. How is this supposed to work? Who should place the wheels there?

Now that we understood the issue, I'm patching it in the CI setup. But Python as distribute by Cygwin is broken.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix

- name: Install
# Cygwin patches Python's ensurepip module to look for the
# wheels needed to initialize a new virtual environment in
# /usr/share/python-wheels/ but nothing in Cygwin actually
# puts the setuptools and pip wheels there. Fix this.
run: |
mkdir /usr/share/python-wheels/
pushd /usr/share/python-wheels/
python -m pip --disable-pip-version-check download setuptools pip
popd
python -m pip --disable-pip-version-check install .[test]
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0}

Now the tests succeed on Cygwin too!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Success!

- name: Setup Cygwin
uses: cygwin/cygwin-install-action@v2
with:
packages: >-
python3${{ matrix.python-minor }}
python3${{ matrix.python-minor }}-pip
cmake make ninja gcc-core gcc-g++ git
gnupg gnupg2 meson pkg-config

- name: Tell Cygwin git about this repository
# This addresses the "fatal: detected dubious ownership in
# repository" and "fatal: not in a git directory" errors
# encountered when trying to run Cygwin git in a directory not
# owned by the current user. This happens when the tests run
# Cygwin git in a directory outside the Cygwin filesystem.
run: git config --global --add safe.directory '*'
shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0}

- name: Install nox
shell: bash.exe -eo pipefail -o igncr "{0}"
env:
PATH: "/bin:/usr/bin:/usr/local/bin:/usr/lib/lapack"
TMP: "/tmp"
TEMP: "/tmp"
run: |
python -m pip install nox
/usr/bin/python -m pip install nox
nox --version

- name: Download setuptools/pip wheels for ensurepip
shell: bash.exe -eo pipefail -o igncr "{0}"
env:
PATH: "/bin:/usr/bin:/usr/local/bin:/usr/lib/lapack"
TMP: "/tmp"
TEMP: "/tmp"
run: |
mkdir -p /usr/share/python-wheels
/usr/bin/python -m pip wheel --wheel-dir /usr/share/python-wheels setuptools pip wheel

- name: Run tests
run: nox -s test-${{ matrix.python }}
shell: bash.exe -eo pipefail -o igncr "{0}"
env:
PATH: "/bin:/usr/bin:/usr/local/bin:/usr/lib/lapack"
CHERE_INVOKING: 1
TMP: "/tmp"
TEMP: "/tmp"
run: |
nox -s test-3.${{ matrix.python-minor }}

- name: Send coverage report
uses: codecov/codecov-action@v1
if: ${{ always() }}
env:
PYTHON: cygwin-${{ matrix.python }}
PYTHON: cygwin-3.${{ matrix.python-minor }}
with:
flags: tests
env_vars: PYTHON
name: cygwin-${{ matrix.python }}
name: cygwin-3.${{ matrix.python-minor }}
8 changes: 6 additions & 2 deletions mesonpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ def _init_colors() -> Dict[str, str]:


_LINUX_NATIVE_MODULE_REGEX = re.compile(r'^(?P<name>.+)\.(?P<tag>.+)\.so$')
_CYGWIN_NATIVE_MODULE_REGEX = re.compile(r'^(?P<name>.+)\.(?P<tag>.+)\.dll$')
_WINDOWS_NATIVE_MODULE_REGEX = re.compile(r'^(?P<name>.+)\.(?P<tag>.+)\.pyd$')
_STABLE_ABI_TAG_REGEX = re.compile(r'^abi(?P<abi_number>[0-9]+)$')

Expand Down Expand Up @@ -345,7 +346,7 @@ def _calculate_file_abi_tag_heuristic_posix(self, filename: str) -> Optional[mes
# preventive and check its value to make sure it matches our expectations
try:
extension = sysconfig.get_config_vars().get('SHLIB_SUFFIX', '.so')
if extension != '.so':
if extension not in ('.so', '.dll'):
raise NotImplementedError(
f"We don't currently support the {extension} extension. "
'Please report this to https://github.com/mesonbuild/mesonpy/issues '
Expand All @@ -358,7 +359,10 @@ def _calculate_file_abi_tag_heuristic_posix(self, filename: str) -> Optional[mes
'Please report this to https://github.com/mesonbuild/mesonpy/issues '
'and include the output of `python -m sysconfig`.'
)
match = _LINUX_NATIVE_MODULE_REGEX.match(filename)
if sys.platform == 'cygwin':
match = _CYGWIN_NATIVE_MODULE_REGEX.match(filename)
else:
match = _LINUX_NATIVE_MODULE_REGEX.match(filename)
if not match: # this file does not appear to be a native module
return None
tag = match.group('tag')
Expand Down
5 changes: 5 additions & 0 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import os
import os.path
import platform

import nox

Expand Down Expand Up @@ -46,6 +47,10 @@ def test(session):
if os.environ.get('GITHUB_ACTIONS') == 'true':
session.install('pytest-github-actions-annotate-failures')

# https://github.com/gitpython-developers/GitPython/pull/1455
if platform.system().startswith('CYGWIN'):
session.install('git+https://github.com/gitpython-developers/GitPython.git')

session.run(
'pytest',
'--showlocals', '-vv',
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ test = [
'Cython',
'pyproject-metadata>=0.6.1',
'wheel',
'importlib_metadata; python_version < "3.8"',
]
docs = [
'furo>=2021.08.31',
Expand Down
14 changes: 9 additions & 5 deletions tests/test_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
INTERPRETER_VERSION = f'{sys.version_info[0]}{sys.version_info[1]}'


if platform.python_implementation() == 'CPython':
python_implementation = platform.python_implementation()
if python_implementation == 'CPython' or python_implementation.startswith('CYGWIN'):
INTERPRETER_TAG = f'cp{INTERPRETER_VERSION}'
PYTHON_TAG = INTERPRETER_TAG
# Py_UNICODE_SIZE has been a runtime option since Python 3.3,
Expand All @@ -30,11 +31,11 @@
pymalloc = sysconfig.get_config_var('WITH_PYMALLOC')
if pymalloc or pymalloc is None: # none is the default value, which is enable
INTERPRETER_TAG += 'm'
elif platform.python_implementation() == 'PyPy':
elif python_implementation == 'PyPy':
INTERPRETER_TAG = sysconfig.get_config_var('SOABI').replace('-', '_')
PYTHON_TAG = f'pp{INTERPRETER_VERSION}'
else:
raise NotImplementedError(f'Unknown implementation: {platform.python_implementation()}')
raise NotImplementedError(f'Unknown implementation: {python_implementation}')

platform_ = sysconfig.get_platform()
if platform.system() == 'Darwin':
Expand All @@ -51,7 +52,7 @@
SHARED_LIB_EXT = 'so'
elif platform.system() == 'Darwin':
SHARED_LIB_EXT = 'dylib'
elif platform.system() == 'Windows':
elif platform.system() == 'Windows' or platform.system().startswith('CYGWIN'):
SHARED_LIB_EXT = 'pyd'
else:
raise NotImplementedError(f'Unknown system: {platform.system()}')
Expand Down Expand Up @@ -93,7 +94,7 @@ def test_scipy_like(wheel_scipy_like):
'mypkg/submod/__init__.py',
'mypkg/submod/unknown_filetype.npq',
}
if os.name == 'nt':
if os.name == 'nt' or sys.platform == 'cygwin':
# Currently Meson is installing `.dll.a` (import libraries) next to
# `.pyd` extension modules. Those are very small, so it's not a major
# issue - just sloppy. For now, ensure we don't fail on those
Expand Down Expand Up @@ -143,6 +144,8 @@ def test_purelib_and_platlib(wheel_purelib_and_platlib):
}
if platform.system() == 'Windows':
expecting.add('plat{}'.format(EXT_SUFFIX.replace('pyd', 'dll.a')))
elif sys.platform == 'cygwin':
expecting.add('plat{}'.format(EXT_SUFFIX.replace('dll', 'dll.a')))

assert wheel_contents(artifact) == expecting

Expand Down Expand Up @@ -193,6 +196,7 @@ def test_executable_bit(wheel_executable_bit):
executable_files = {
'executable_bit-1.0.0.data/purelib/executable_module.py',
'executable_bit-1.0.0.data/scripts/example',
'executable_bit-1.0.0.data/scripts/example.exe',
'executable_bit-1.0.0.data/scripts/example-script',
'executable_bit-1.0.0.data/data/bin/example-script',
}
Expand Down