From 672736e836f48d0fb9c19454bf5321c48f33b139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Fri, 22 Sep 2017 17:37:48 +0200 Subject: [PATCH] WIP: Support Windows --- .gitignore | 2 ++ appveyor.yml | 26 ++++++++++++++++++++++ autogen.py | 13 +++++++++++ setup.py | 17 +++++++++++--- test/conftest.py | 14 ++++++++++++ test/test_io.py | 5 +++++ windows/inject_admesh.py | 20 +++++++++++++++++ windows/prepare_admesh.py | 47 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 appveyor.yml create mode 100644 test/conftest.py create mode 100644 windows/inject_admesh.py create mode 100644 windows/prepare_admesh.py diff --git a/.gitignore b/.gitignore index 042b2b0..f64884f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ gccdump.s test/block_* .eggs .tox +/windows/admesh +*.whl diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..3735308 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,26 @@ +environment: + CIBW_BEFORE_BUILD: '{pip} install Cython pytest-runner' + CIBW_TEST_REQUIRES: 'pytest' + CIBW_TEST_COMMAND: 'python -m pytest -v -s {project}\test' + TWINE_USERNAME: hroncok + # Note: TWINE_PASSWORD is set in Appveyor settings + matrix: + - CIBW_SKIP: '*32' + - CIBW_SKIP: '*64' + +build_script: + - pip install cibuildwheel==0.5.1 requests Cython pytest-runner + - python windows\prepare_admesh.py + - cibuildwheel --output-dir wheelhouse + - python windows\inject_admesh.py wheelhouse\*.whl + - > + IF "%APPVEYOR_REPO_TAG%" == "true" + ( + python -m pip install twine + && + python -m twine upload wheelhouse/*.whl + ) + +artifacts: + - path: "wheelhouse\\*.whl" + name: Wheels diff --git a/autogen.py b/autogen.py index a77ee6d..ad182ec 100644 --- a/autogen.py +++ b/autogen.py @@ -1,7 +1,9 @@ from __future__ import print_function from Cython.Distutils import build_ext +import glob import subprocess import os +import platform import sys @@ -178,6 +180,9 @@ def format_function(cls, function, static, args, chars): extra=extra) def get_header(self, header): + if platform.system().startswith('Windows'): + return self.get_header_windows(header) + cflags = os.environ.get('CFLAGS', '') p = subprocess.Popen('gcc -v -E -'.split() + cflags.split(), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -196,3 +201,11 @@ def get_header(self, header): print("found %s" % candidate) return candidate return None + + def get_header_windows(self, header): + path = 'windows/admesh*/include/{h}'.format(h=header) + try: + path = glob.glob(path)[0] + except IndexError: + raise RuntimeError('Run `python windows/prepare_admesh.py` first!') + return path diff --git a/setup.py b/setup.py index 662ce78..cf907ce 100755 --- a/setup.py +++ b/setup.py @@ -1,13 +1,25 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from setuptools import setup, find_packages, Extension +import platform +from setuptools import setup, Extension def _autogen(*args, **kwargs): from autogen import Autogen return Autogen(*args, **kwargs) + long_description = ''.join(open('README.rst').readlines()) +ext_kwargs = {} + + +if platform.system().startswith('Windows'): + ext_kwargs['include_dirs'] = ['windows/admesh/include/'] + ext_kwargs['library_dirs'] = ['windows/admesh/lib/'] + ext_kwargs['libraries'] = ['libadmesh'] +else: + ext_kwargs['libraries'] = ['admesh'] + setup( name='admesh', @@ -21,9 +33,8 @@ def _autogen(*args, **kwargs): license='GPLv2+', setup_requires=['Cython>=0.22', 'pytest-runner'], tests_require=['pytest'], - packages=find_packages(), cmdclass={'build_ext': _autogen}, - ext_modules=[Extension("admesh", ["admesh.pyx"], libraries=["admesh"])], + ext_modules=[Extension('admesh', ['admesh.pyx'], **ext_kwargs)], classifiers=['Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'Intended Audience :: Manufacturing', diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..d5763dc --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,14 @@ +from __future__ import print_function +import os +import platform +import site +import shutil + + +# This is a hack! TODO explain (if it works) XXX +if 'CIBUILDWHEEL' in os.environ and platform.system().startswith('Windows'): + site_packages = site.getsitepackages()[1] + dirname = os.path.dirname(__file__) + dll = os.path.join(dirname, '..', 'windows', 'admesh', 'libadmesh-1.dll') + shutil.copy(dll, site_packages) + print('Copying', dll, 'to', site_packages) diff --git a/test/test_io.py b/test/test_io.py index f47ccd7..e8d5cb6 100644 --- a/test/test_io.py +++ b/test/test_io.py @@ -2,10 +2,15 @@ from admesh import Stl from utils import asset import filecmp +import platform +import pytest + class TestIO(object): '''Tests for the basic IO operations''' + @pytest.mark.skipif(platform.system().startswith('Windows'), + reason='Extra zeros on Windows') def test_saved_equals_original_ascii(self): '''Tests if saved ASCII file is identical to the loaded one''' stl = Stl(asset('block.stl')) diff --git a/windows/inject_admesh.py b/windows/inject_admesh.py new file mode 100644 index 0000000..05f86df --- /dev/null +++ b/windows/inject_admesh.py @@ -0,0 +1,20 @@ +from __future__ import print_function +import glob +import sys +import zipfile + + +DLL = 'libadmesh-1.dll' +DLLPATH = 'windows/admesh/' + DLL + + +def inject_into(wheelpath): + with zipfile.ZipFile(wheelpath, 'a') as wheel: + wheel.write(DLLPATH, DLL) + + +if __name__ == '__main__': + for arg in sys.argv[1:]: + for path in glob.glob(arg): + print('Injecting the admesh dll into', path) + inject_into(path) diff --git a/windows/prepare_admesh.py b/windows/prepare_admesh.py new file mode 100644 index 0000000..02a27c4 --- /dev/null +++ b/windows/prepare_admesh.py @@ -0,0 +1,47 @@ +from __future__ import print_function +import io +import os +import requests +import shutil +import sys +import zipfile + + +VERSION = '0.98.3' +ARCH = 64 if sys.maxsize > 2**32 else 32 + +# AppVeyor hack +# When this is run, it is (currently) with 32bit Python +# However, we need it to be the same as Pythons that will build the wheels +# On AppVeyor, we run twice, once with CIBW_SKIP to skip 64b wheels, later 32b, +if 'CIBW_SKIP' in os.environ: + ARCH = 64 if os.environ['CIBW_SKIP'].endswith('32') else 32 + + +DIRNAME = 'admesh-win{arch}-{version}'.format(arch=ARCH, version=VERSION) + +URL = ('https://github.com/admesh/admesh/releases/download/v{version}/' + '{dirname}.zip').format(version=VERSION, dirname=DIRNAME) + + +def download_unpack(): + response = requests.get(URL, stream=True) + with zipfile.ZipFile(io.BytesIO(response.content), 'r') as admeshzip: + admeshzip.extractall() + + +def move(): + dst = 'windows/admesh' + if os.path.exists(dst): + shutil.rmtree(dst) + shutil.move(DIRNAME, dst) + + # mingw names it .dll.a, but python expects .lib + shutil.move(dst + '/lib/libadmesh.dll.a', + dst + '/lib/libadmesh.lib') + + +if __name__ == '__main__': + print('Downloading', URL) + download_unpack() + move()