Skip to content

Commit fa9300f

Browse files
committed
Merge branch 'githubaction-clang-migration' into dev
2 parents 1cd6c79 + 91cb979 commit fa9300f

4 files changed

Lines changed: 173 additions & 50 deletions

File tree

.github/workflows/build_test.yml

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,54 +8,51 @@ jobs:
88
runs-on: ${{ matrix.os }}
99
strategy:
1010
matrix:
11-
os: [ubuntu-latest, windows-latest, macos-15-intel, macos-latest]
12-
include:
13-
- os: macos-15-intel
14-
c_compiler: gcc-15
15-
- os: macos-latest
16-
c_compiler: gcc-15
11+
os:
12+
- ubuntu-latest
13+
- windows-latest
14+
- macos-15-intel
15+
- macos-15
1716

1817
steps:
1918
- uses: actions/checkout@v4
2019

21-
# Used to host cibuildwheel
2220
- uses: actions/setup-python@v5
2321
with:
2422
python-version: "3.11"
2523

26-
- name: Install gcc on macOS
27-
if: runner.os == 'macOS'
28-
run: brew install gcc@15
29-
3024
- name: Install libomp on macOS
3125
if: runner.os == 'macOS'
32-
run: brew install libomp
26+
run: |
27+
brew install libomp
28+
echo "LIBOMP_PREFIX=$(brew --prefix libomp)" >> "$GITHUB_ENV"
3329
3430
- name: Install cibuildwheel
3531
run: python -m pip install cibuildwheel
3632

3733
- name: Build wheels
3834
env:
39-
CIBW_ENVIRONMENT_MACOS: CC=gcc-15 MACOSX_DEPLOYMENT_TARGET=26.0
35+
CIBW_ENVIRONMENT_MACOS: >-
36+
CC=clang
37+
CXX=clang++
38+
MACOSX_DEPLOYMENT_TARGET=15.0
39+
LIBOMP_PREFIX=${{ env.LIBOMP_PREFIX }}
4040
run: python -m cibuildwheel --output-dir wheelhouse
4141

42-
- name: Test on all platforms except MacOS
43-
if: runner.os != 'macOS'
42+
- name: Install test dependencies
4443
run: |
4544
python -m pip install -r REQUIREMENTS.txt
46-
python -m pip install -e .
4745
python -m pip install -r REQUIREMENTS-DEV.txt
48-
pytest -v
4946
50-
- name: Test on MacOS
51-
if: runner.os == 'macOS'
52-
env:
53-
CC: gcc-15
47+
- name: Install built wheel
48+
shell: bash
5449
run: |
5550
python -m pip install -r REQUIREMENTS.txt
5651
python -m pip install -e .
5752
python -m pip install -r REQUIREMENTS-DEV.txt
58-
pytest -v
53+
54+
- name: Run tests
55+
run: pytest -v
5956

6057
- uses: actions/upload-artifact@v4
6158
with:

docs/installation.rst

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Compilation of the C library
1313
Each release of EPANET-PLUS contains binaries build for all major platforms.
1414

1515
We offer platform-specific scripts for building the C library -- note that those
16-
require the latest version of `gcc`:
16+
require the latest version of `gcc` (for MacOSX users: you have to install gcc-15 from HomeBrew):
1717

1818
- Linux: `compile_linux.sh <https://raw.githubusercontent.com/WaterFutures/EPANET-PLUS/main/compile_linux.sh>`_
1919
- MacOS: `compile_macos.sh <https://raw.githubusercontent.com/WaterFutures/EPANET-PLUS/main/compile_macos.sh>`_
@@ -65,3 +65,9 @@ Build and install the package:
6565

6666
This step triggers the build of the C extension, which requires a C compiler to be
6767
installed on the system.
68+
Furthermore, on MacOSX, you have to run:
69+
70+
..code:: bash
71+
72+
brew install libomp
73+
export LIBOMP_PREFIX="$(brew --prefix libomp)"

pyproject.toml

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[build-system]
2-
requires = ["setuptools", "numpy"]
2+
requires = ["setuptools", "wheel", "numpy"]
33
build-backend = "setuptools.build_meta"
44

55
[project]
@@ -23,7 +23,7 @@ classifiers = [
2323
"Programming Language :: Python :: 3.13",
2424
"Programming Language :: Python :: 3.14"
2525
]
26-
keywords=["epanet", "water", "networks", "hydraulics", "quality", "simulations"]
26+
keywords = ["epanet", "water", "networks", "hydraulics", "quality", "simulations"]
2727
dynamic = ["version", "readme", "dependencies"]
2828

2929
[project.urls]
@@ -41,26 +41,4 @@ dependencies = {file = "REQUIREMENTS.txt"}
4141
before-all = "uname -a"
4242

4343
[tool.setuptools.packages]
44-
find = {}
45-
46-
[tool.setuptools]
47-
ext-modules = [
48-
{name = "epanet", extra-compile-args = ["-fopenmp"], extra-link-args = ["-fopenmp"], include-dirs=["epanet-src/include", "epanet-src/", "epanet-msx-src/include", "epanet-msx-src/",
49-
"python-extension", "epanet_plus/include", "epanet-src/util"], sources = ["python-extension/pyepanet.c",
50-
"python-extension/pyepanet2.c", "python-extension/pyepanetmsx.c", "python-extension/pyepanet_plus.c",
51-
"python-extension/ext.c", "epanet_plus/epanet_plus.c", "epanet-src/epanet2.c", "epanet-src/hydcoeffs.c",
52-
"epanet-src/inpfile.c", "epanet-src/quality.c", "epanet-src/rules.c", "epanet-src/epanet.c",
53-
"epanet-src/hydraul.c", "epanet-src/input1.c", "epanet-src/mempool.c", "epanet-src/qualreact.c",
54-
"epanet-src/smatrix.c", "epanet-src/genmmd.c", "epanet-src/hydsolver.c", "epanet-src/input2.c",
55-
"epanet-src/output.c", "epanet-src/qualroute.c", "epanet-src/hash.c", "epanet-src/hydstatus.c",
56-
"epanet-src/input3.c", "epanet-src/project.c", "epanet-src/report.c", "epanet-msx-src/hash.c",
57-
"epanet-src/flowbalance.c", "epanet-src/leakage.c", "epanet-src/validate.c",
58-
"epanet-src/util/filemanager.c", "epanet-src/util/errormanager.c", "epanet-src/util/cstr_helper.c",
59-
"epanet-msx-src/msxcompiler.c", "epanet-msx-src/msxfuncs.c", "epanet-msx-src/msxqual.c",
60-
"epanet-msx-src/msxutils.c", "epanet-msx-src/smatrix.c", "epanet-msx-src/mathexpr.c",
61-
"epanet-msx-src/msxdispersion.c", "epanet-msx-src/msxinp.c", "epanet-msx-src/msxrpt.c",
62-
"epanet-msx-src/newton.c", "epanet-msx-src/mempool.c", "epanet-msx-src/msxerr.c",
63-
"epanet-msx-src/msxout.c", "epanet-msx-src/msxtank.c", "epanet-msx-src/rk5.c",
64-
"epanet-msx-src/msxchem.c", "epanet-msx-src/msxfile.c", "epanet-msx-src/msxproj.c",
65-
"epanet-msx-src/msxtoolkit.c", "epanet-msx-src/ros2.c"]}
66-
]
44+
find = {}

setup.py

Lines changed: 144 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,149 @@
1-
from setuptools import setup
1+
import os
2+
import sys
3+
import shutil
4+
import subprocess
5+
from setuptools import setup, Extension
26
from setuptools.command.build_ext import build_ext
37

48

9+
INCLUDE_DIRS = [
10+
"epanet-src/include",
11+
"epanet-src/",
12+
"epanet-msx-src/include",
13+
"epanet-msx-src/",
14+
"python-extension",
15+
"epanet_plus/include",
16+
"epanet-src/util",
17+
]
18+
19+
SOURCES = [
20+
"python-extension/pyepanet.c",
21+
"python-extension/pyepanet2.c",
22+
"python-extension/pyepanetmsx.c",
23+
"python-extension/pyepanet_plus.c",
24+
"python-extension/ext.c",
25+
"epanet_plus/epanet_plus.c",
26+
"epanet-src/epanet2.c",
27+
"epanet-src/hydcoeffs.c",
28+
"epanet-src/inpfile.c",
29+
"epanet-src/quality.c",
30+
"epanet-src/rules.c",
31+
"epanet-src/epanet.c",
32+
"epanet-src/hydraul.c",
33+
"epanet-src/input1.c",
34+
"epanet-src/mempool.c",
35+
"epanet-src/qualreact.c",
36+
"epanet-src/smatrix.c",
37+
"epanet-src/genmmd.c",
38+
"epanet-src/hydsolver.c",
39+
"epanet-src/input2.c",
40+
"epanet-src/output.c",
41+
"epanet-src/qualroute.c",
42+
"epanet-src/hash.c",
43+
"epanet-src/hydstatus.c",
44+
"epanet-src/input3.c",
45+
"epanet-src/project.c",
46+
"epanet-src/report.c",
47+
"epanet-msx-src/hash.c",
48+
"epanet-src/flowbalance.c",
49+
"epanet-src/leakage.c",
50+
"epanet-src/validate.c",
51+
"epanet-src/util/filemanager.c",
52+
"epanet-src/util/errormanager.c",
53+
"epanet-src/util/cstr_helper.c",
54+
"epanet-msx-src/msxcompiler.c",
55+
"epanet-msx-src/msxfuncs.c",
56+
"epanet-msx-src/msxqual.c",
57+
"epanet-msx-src/msxutils.c",
58+
"epanet-msx-src/smatrix.c",
59+
"epanet-msx-src/mathexpr.c",
60+
"epanet-msx-src/msxdispersion.c",
61+
"epanet-msx-src/msxinp.c",
62+
"epanet-msx-src/msxrpt.c",
63+
"epanet-msx-src/newton.c",
64+
"epanet-msx-src/mempool.c",
65+
"epanet-msx-src/msxerr.c",
66+
"epanet-msx-src/msxout.c",
67+
"epanet-msx-src/msxtank.c",
68+
"epanet-msx-src/rk5.c",
69+
"epanet-msx-src/msxchem.c",
70+
"epanet-msx-src/msxfile.c",
71+
"epanet-msx-src/msxproj.c",
72+
"epanet-msx-src/msxtoolkit.c",
73+
"epanet-msx-src/ros2.c",
74+
]
75+
76+
def get_libomp_prefix() -> str | None:
77+
prefix = os.environ.get("LIBOMP_PREFIX")
78+
if prefix:
79+
return prefix
80+
81+
brew = shutil.which("brew")
82+
if brew:
83+
try:
84+
return subprocess.check_output(
85+
[brew, "--prefix", "libomp"],
86+
text=True,
87+
).strip()
88+
except subprocess.CalledProcessError:
89+
pass
90+
91+
return None
92+
93+
94+
def get_openmp_config() -> dict[str, list[str]]:
95+
if sys.platform == "darwin":
96+
prefix = get_libomp_prefix()
97+
if not prefix:
98+
raise RuntimeError(
99+
"OpenMP runtime `libomp` not found on macOS. "
100+
"Install it with `brew install libomp` and set "
101+
"`LIBOMP_PREFIX=$(brew --prefix libomp)`."
102+
)
103+
104+
return {
105+
"include_dirs": [f"{prefix}/include"],
106+
"extra_compile_args": ["-Xpreprocessor", "-fopenmp"],
107+
"extra_link_args": [
108+
f"-L{prefix}/lib",
109+
f"-Wl,-rpath,{prefix}/lib",
110+
"-lomp",
111+
],
112+
}
113+
114+
if sys.platform.startswith("linux"):
115+
return {
116+
"include_dirs": [],
117+
"extra_compile_args": ["-fopenmp"],
118+
"extra_link_args": ["-fopenmp"],
119+
}
120+
121+
if sys.platform == "win32":
122+
return {
123+
"include_dirs": [],
124+
"extra_compile_args": ["/openmp"],
125+
"extra_link_args": [],
126+
}
127+
128+
return {
129+
"include_dirs": [],
130+
"extra_compile_args": [],
131+
"extra_link_args": [],
132+
}
133+
134+
135+
omp = get_openmp_config()
136+
137+
138+
extension = Extension(
139+
name="epanet",
140+
include_dirs=INCLUDE_DIRS + omp["include_dirs"],
141+
sources=SOURCES,
142+
extra_compile_args=omp["extra_compile_args"],
143+
extra_link_args=omp["extra_link_args"],
144+
)
145+
146+
5147
class build_ext_with_numpy(build_ext):
6148
def build_extensions(self):
7149
import numpy
@@ -13,4 +155,4 @@ def build_extensions(self):
13155
super().build_extensions()
14156

15157

16-
setup(cmdclass={"build_ext": build_ext_with_numpy})
158+
setup(ext_modules=[extension], cmdclass={"build_ext": build_ext_with_numpy})

0 commit comments

Comments
 (0)