Skip to content

Commit 46c5082

Browse files
authored
Merge pull request #111 from Tieqiong/winwheel
fix: wheel for windows
2 parents 4b2ca35 + 7e12beb commit 46c5082

File tree

2 files changed

+132
-33
lines changed

2 files changed

+132
-33
lines changed

.github/workflows/wheel.yml

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
name: Wheel builder
2+
3+
on:
4+
pull_request:
5+
push:
6+
workflow_dispatch:
7+
8+
jobs:
9+
build_wheels:
10+
11+
defaults:
12+
run:
13+
shell: bash -l {0}
14+
15+
name: Build wheel ${{ matrix.python[0] }}-${{ matrix.buildplat[0] }}
16+
runs-on: ${{ matrix.buildplat[0] }}
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
buildplat:
21+
- [ubuntu-latest, manylinux_x86_64]
22+
- [macos-13, macosx_x86_64]
23+
- [macos-14, macosx_arm64]
24+
- [windows-latest, win_amd64]
25+
python:
26+
- ["3.11", "cp311"]
27+
- ["3.12", "cp312"]
28+
29+
steps:
30+
- name: Check out #${{ inputs.project }}
31+
uses: actions/checkout@v4
32+
33+
- name: Set up Python ${{ matrix.python[0] }}
34+
uses: actions/setup-python@v5
35+
with:
36+
python-version: ${{ matrix.python[0] }}
37+
38+
- name: Build wheels for Linux
39+
if: runner.os == 'Linux'
40+
uses: pypa/cibuildwheel@v2.21.1
41+
env:
42+
CIBW_BUILD: ${{ matrix.python[1] }}-${{ matrix.buildplat[1] }}
43+
CIBW_BEFORE_BUILD: yum install -y gsl-devel && pip install -e .
44+
with:
45+
output-dir: wheelhouse
46+
47+
- name: Build wheels for macOS
48+
if: runner.os == 'macOS'
49+
uses: pypa/cibuildwheel@v2.21.1
50+
env:
51+
CIBW_BUILD: ${{ matrix.python[1] }}-${{ matrix.buildplat[1] }}
52+
MACOSX_DEPLOYMENT_TARGET: 13.0
53+
CIBW_BEFORE_BUILD: brew install gsl && pip install -e .
54+
with:
55+
output-dir: wheelhouse
56+
57+
- name: Set up conda for Windows
58+
if: runner.os == 'Windows'
59+
uses: conda-incubator/setup-miniconda@v3
60+
with:
61+
activate-environment: gsl
62+
auto-update-conda: true
63+
environment-file: environment.yml
64+
auto-activate-base: false
65+
66+
- name: install gsl for Windows
67+
if: runner.os == 'Windows'
68+
run: |
69+
conda config --set always_yes yes --set changeps1 no
70+
conda install gsl
71+
72+
- name: Build wheels for Windows
73+
if: runner.os == 'Windows'
74+
uses: pypa/cibuildwheel@v2.21.1
75+
env:
76+
CIBW_BUILD: ${{ matrix.python[1] }}-${{ matrix.buildplat[1] }}
77+
CONDA_PREFIX: ${{ env.CONDA_PREFIX }}
78+
with:
79+
output-dir: wheelhouse
80+
81+
- uses: actions/upload-artifact@v4
82+
with:
83+
name: ${{ matrix.python[0] }}-${{ matrix.buildplat[0] }}
84+
path: ./wheelhouse/*.whl

setup.py

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -11,97 +11,111 @@
1111
import glob
1212
import os
1313
import re
14+
import shutil
1415
import sys
1516
import warnings
17+
from pathlib import Path
1618

1719
from setuptools import Extension, setup
20+
from setuptools.command.build_ext import build_ext
1821

1922
# Use this version when git data are not available, like in git zip archive.
2023
# Update when tagging a new release.
2124
FALLBACK_VERSION = "1.4.3"
2225

23-
MYDIR = os.path.dirname(os.path.abspath(__file__))
26+
MYDIR = str(Path(__file__).parent.resolve())
2427

2528
# Helper functions -----------------------------------------------------------
2629

2730

2831
def get_compiler_type():
29-
"""find compiler used for building extensions."""
32+
"""Find compiler used for building extensions."""
3033
cc_arg = [a for a in sys.argv if a.startswith("--compiler=")]
3134
if cc_arg:
32-
compiler_type = cc_arg[-1].split("=", 1)[1]
33-
else:
34-
from distutils.ccompiler import new_compiler
35+
return cc_arg[-1].split("=", 1)[1]
36+
from distutils.ccompiler import new_compiler
3537

36-
compiler_type = new_compiler().compiler_type
37-
return compiler_type
38+
return new_compiler().compiler_type
3839

3940

4041
def get_gsl_config():
4142
"""Return dictionary with paths to GSL library."""
42-
gslcfgpaths = [os.path.join(p, "gsl-config") for p in ([MYDIR] + os.environ["PATH"].split(os.pathsep))]
43-
gslcfgpaths = [p for p in gslcfgpaths if os.path.isfile(p)]
43+
gslcfgpaths = [Path(p) / "gsl-config" for p in ([MYDIR] + os.environ["PATH"].split(os.pathsep))]
44+
gslcfgpaths = [p for p in gslcfgpaths if p.is_file()]
4445
rv = {"include_dirs": [], "library_dirs": []}
4546
if not gslcfgpaths:
46-
wmsg = "Cannot find gsl-config in {!r} nor in system PATH."
47-
warnings.warn(wmsg.format(MYDIR))
47+
warnings.warn(f"Cannot find gsl-config in {MYDIR} nor in system PATH.")
4848
return rv
4949
gslcfg = gslcfgpaths[0]
50-
with open(gslcfg) as fp:
51-
txt = fp.read()
52-
mprefix = re.search("(?m)^prefix=(.+)", txt)
50+
txt = gslcfg.read_text()
51+
mprefix = re.search(r"(?m)^prefix=(.+)", txt)
5352
minclude = re.search(r"(?m)^[^#]*\s-I(\S+)", txt)
5453
mlibpath = re.search(r"(?m)^[^#]*\s-L(\S+)", txt)
5554
if not mprefix:
56-
emsg = "Cannot find 'prefix=' line in {}."
57-
raise RuntimeError(emsg.format(gslcfg))
58-
p = mprefix.group(1)
59-
inc = minclude.group(1) if minclude else (p + "/include")
60-
lib = mlibpath.group(1) if mlibpath else (p + "/lib")
61-
rv["include_dirs"] += [inc]
62-
rv["library_dirs"] += [lib]
55+
raise RuntimeError(f"Cannot find 'prefix=' line in {gslcfg}.")
56+
p = Path(mprefix.group(1))
57+
rv["include_dirs"].append(str(minclude.group(1) if minclude else p / "include"))
58+
rv["library_dirs"].append(str(mlibpath.group(1) if mlibpath else p / "lib"))
6359
return rv
6460

6561

6662
def get_gsl_config_win():
6763
"""Return dictionary with paths to GSL library on Windows."""
68-
gsl_path = os.environ.get("GSL_PATH")
64+
gsl_path = os.environ.get("GSL_PATH", "")
6965
if gsl_path:
70-
inc = os.path.join(gsl_path, "include")
71-
lib = os.path.join(gsl_path, "lib")
66+
inc = Path(gsl_path) / "include"
67+
lib = Path(gsl_path) / "lib"
7268
else:
7369
conda_prefix = os.environ.get("CONDA_PREFIX")
7470
if conda_prefix:
75-
inc = os.path.join(conda_prefix, "Library", "include")
76-
lib = os.path.join(conda_prefix, "Library", "lib")
71+
inc = Path(conda_prefix) / "Library" / "include"
72+
lib = Path(conda_prefix) / "Library" / "lib"
7773
else:
7874
raise EnvironmentError(
7975
"Neither GSL_PATH nor CONDA_PREFIX environment variables are set. "
8076
"Please ensure GSL is installed and GSL_PATH is correctly set."
8177
)
78+
return {"include_dirs": [str(inc)], "library_dirs": [str(lib)]}
79+
80+
81+
class CustomBuildExt(build_ext):
82+
def run(self):
83+
super().run()
84+
gsl_path = (
85+
Path(os.environ.get("GSL_PATH"))
86+
if os.environ.get("GSL_PATH")
87+
else Path(os.environ.get("CONDA_PREFIX", "")) / "Library"
88+
)
89+
bin_path = gsl_path / "bin"
90+
dest_path = Path(self.build_lib) / "diffpy" / "pdffit2"
91+
dest_path.mkdir(parents=True, exist_ok=True)
8292

83-
return {"include_dirs": [inc], "library_dirs": [lib]}
93+
for dll_file in bin_path.glob("gsl*.dll"):
94+
shutil.copy(str(dll_file), str(dest_path))
8495

8596

8697
# ----------------------------------------------------------------------------
8798

88-
# compile and link options
89-
define_macros = []
99+
# Compile and link options
90100
os_name = os.name
91101
if os_name == "nt":
92102
gcfg = get_gsl_config_win()
93103
else:
94104
gcfg = get_gsl_config()
95-
include_dirs = [MYDIR] + gcfg["include_dirs"]
96-
library_dirs = []
105+
97106
if sys.platform == "darwin":
98107
libraries = []
99108
else:
100109
libraries = ["gsl"]
110+
111+
include_dirs = [MYDIR] + gcfg["include_dirs"]
112+
library_dirs = []
113+
define_macros = []
101114
extra_objects = []
102115
extra_compile_args = []
103116
extra_link_args = []
104117

118+
105119
compiler_type = get_compiler_type()
106120
if compiler_type in ("unix", "cygwin", "mingw32"):
107121
extra_compile_args = ["-std=c++11", "-Wall", "-Wno-write-strings", "-O3", "-funroll-loops", "-ffast-math"]
@@ -114,7 +128,7 @@ def get_gsl_config_win():
114128
library_dirs += gcfg["library_dirs"]
115129
# add optimization flags for other compilers if needed
116130

117-
# define extension arguments here
131+
# Define extension arguments
118132
ext_kws = {
119133
"include_dirs": include_dirs,
120134
"libraries": libraries,
@@ -126,14 +140,15 @@ def get_gsl_config_win():
126140
}
127141

128142

129-
# define extension here
143+
# Define extensions
130144
def create_extensions():
131145
ext = Extension("diffpy.pdffit2.pdffit2", glob.glob("src/extensions/**/*.cc"), **ext_kws)
132146
return [ext]
133147

134148

135149
setup_args = dict(
136150
ext_modules=[],
151+
cmdclass={"build_ext": CustomBuildExt},
137152
)
138153

139154
if __name__ == "__main__":

0 commit comments

Comments
 (0)