Skip to content

Commit 905d253

Browse files
authored
Update CI macos-latest (#673)
1 parent f0a848e commit 905d253

File tree

4 files changed

+101
-62
lines changed

4 files changed

+101
-62
lines changed

.github/workflows/python-package.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ jobs:
1616
strategy:
1717
fail-fast: false
1818
matrix:
19-
os: [windows-latest, ubuntu-22.04, macos-14]
20-
python-version: ["3.9", "3.10", "3.11", "3.12"]
19+
os: [windows-latest, ubuntu-22.04, macos-latest]
20+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
2121
env:
2222
LIMIT_NUMPY_VERSION: 2.0.0
2323
steps:
@@ -34,7 +34,7 @@ jobs:
3434
with:
3535
python-version: ${{ matrix.python-version }}
3636
cache: 'pip'
37-
37+
3838
- name: Setup Python 3.9 - macos-arm
3939
if: ${{ (matrix.os == 'macos-latest') && (matrix.python-version == '3.9') }}
4040
run: |
@@ -84,3 +84,8 @@ jobs:
8484
- name: Test with pytest
8585
run: |
8686
python${{ matrix.python-version }} -m pytest --cov=cornac
87+
88+
- name: Test running first_example.py
89+
run: |
90+
cp examples/first_example.py first_example.py
91+
python${{ matrix.python-version }} first_example.py

.github/workflows/python-publish.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ jobs:
2222
strategy:
2323
fail-fast: false
2424
matrix:
25-
os: [windows-latest, ubuntu-22.04, macos-14]
26-
python-version: ["3.9", "3.10", "3.11", "3.12"]
25+
os: [windows-latest, ubuntu-22.04, macos-latest]
26+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
2727
steps:
2828
- uses: actions/checkout@v4
2929

cornac/utils/external/eigen/Eigen/src/Core/Transpositions.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ class Transpose<TranspositionsBase<TranspositionsDerived> >
384384
const Product<OtherDerived, Transpose, AliasFreeProduct>
385385
operator*(const MatrixBase<OtherDerived>& matrix, const Transpose& trt)
386386
{
387-
return Product<OtherDerived, Transpose, AliasFreeProduct>(matrix.derived(), trt.derived());
387+
return Product<OtherDerived, Transpose, AliasFreeProduct>(matrix.derived(), trt);
388388
}
389389

390390
/** \returns the \a matrix with the inverse transpositions applied to the rows.

setup.py

Lines changed: 90 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626

2727
import os
2828
import sys
29-
import glob
3029
import shutil
30+
import platform
3131
from setuptools import Extension, Command, setup, find_packages
3232
from Cython.Distutils import build_ext
3333
import numpy as np
@@ -37,71 +37,105 @@
3737
long_description = fh.read()
3838

3939

40-
USE_OPENMP = True
40+
USE_OPENMP = False # we'll turn it on only if we find a working libomp
4141

42+
def candidates_from_env():
43+
# Let users/CI point to a custom libomp
44+
incs = []
45+
libs = []
46+
omp_dir = os.environ.get("OMP_DIR")
47+
if omp_dir:
48+
incs += [os.path.join(omp_dir, "include")]
49+
libs += [os.path.join(omp_dir, "lib")]
50+
inc_env = os.environ.get("OMP_INCLUDE")
51+
lib_env = os.environ.get("OMP_LIB")
52+
if inc_env: incs.append(inc_env)
53+
if lib_env: libs.append(lib_env)
54+
# Conda environments
55+
conda = os.environ.get("CONDA_PREFIX")
56+
if conda:
57+
incs += [os.path.join(conda, "include")]
58+
libs += [os.path.join(conda, "lib")]
59+
# Homebrew (Apple silicon and Intel) and MacPorts (optional, don’t require them)
60+
incs += ["/opt/homebrew/opt/libomp/include", "/usr/local/opt/libomp/include", "/opt/local/include"]
61+
libs += ["/opt/homebrew/opt/libomp/lib", "/usr/local/opt/libomp/lib", "/opt/local/lib"]
62+
# Common fallbacks
63+
incs += ["/usr/local/include", "/usr/include"]
64+
libs += ["/usr/local/lib", "/usr/lib"]
65+
# De-dup while keeping order
66+
def uniq(xs):
67+
seen=set(); out=[]
68+
for x in xs:
69+
if x and x not in seen:
70+
out.append(x); seen.add(x)
71+
return out
72+
return uniq(incs), uniq(libs)
4273

43-
def extract_gcc_binaries():
44-
"""Try to find GCC on OSX for OpenMP support."""
45-
patterns = [
46-
"/opt/local/bin/g++-mp-[0-9].[0-9]",
47-
"/opt/local/bin/g++-mp-[0-9]",
48-
"/usr/local/bin/g++-[0-9].[0-9]",
49-
"/usr/local/bin/g++-[0-9]",
50-
]
51-
if sys.platform.startswith("darwin"):
52-
gcc_binaries = []
53-
for pattern in patterns:
54-
gcc_binaries += glob.glob(pattern)
55-
gcc_binaries.sort()
56-
if gcc_binaries:
57-
_, gcc = os.path.split(gcc_binaries[-1])
58-
return gcc
59-
else:
60-
return None
61-
else:
62-
return None
74+
def find_libomp():
75+
incs, libs = candidates_from_env()
76+
inc_dir = next((d for d in incs if os.path.exists(os.path.join(d, "omp.h"))), None)
77+
# Prefer a real libomp over stubs
78+
lib_names = ["libomp.dylib", "libomp.a"]
79+
lib_dir = None
80+
for d in libs:
81+
if any(os.path.exists(os.path.join(d, n)) for n in lib_names):
82+
lib_dir = d
83+
break
84+
return inc_dir, lib_dir
6385

86+
compile_args = []
87+
link_args = []
6488

6589
if sys.platform.startswith("win"):
66-
# compile args from
67-
# https://msdn.microsoft.com/en-us/library/fwkeyyhe.aspx
6890
compile_args = ["/O2", "/openmp"]
6991
link_args = []
70-
else:
71-
gcc = extract_gcc_binaries()
72-
if gcc is not None:
73-
rpath = "/usr/local/opt/gcc/lib/gcc/" + gcc[-1] + "/"
74-
link_args = ["-Wl,-rpath," + rpath]
75-
else:
76-
link_args = []
92+
elif sys.platform.startswith("darwin"):
93+
# Always use Clang on macOS
94+
os.environ.setdefault("CC", "clang")
95+
os.environ.setdefault("CXX", "clang++")
7796

78-
compile_args = [
79-
"-Wno-unused-function",
80-
"-Wno-maybe-uninitialized",
81-
"-O3",
82-
"-ffast-math",
83-
]
97+
# Force single-arch arm64 on Apple silicon unless caller overrides
98+
if platform.machine() == "arm64" and not os.environ.get("ARCHFLAGS"):
99+
os.environ["ARCHFLAGS"] = "-arch arm64"
84100

85-
if sys.platform.startswith("darwin"):
86-
if gcc is not None:
87-
os.environ["CC"] = gcc
88-
os.environ["CXX"] = gcc
89-
else:
90-
if not os.path.exists("/usr/bin/g++"):
91-
print(
92-
"No GCC available. Install gcc from Homebrew using brew install gcc."
93-
)
94-
USE_OPENMP = False
95-
# required arguments for default gcc of OSX
96-
compile_args.extend(["-O2", "-stdlib=libc++", "-mmacosx-version-min=10.7"])
97-
link_args.extend(["-O2", "-stdlib=libc++", "-mmacosx-version-min=10.7"])
101+
mac_min = os.environ.get("MACOSX_DEPLOYMENT_TARGET", "12.0")
98102

99-
if USE_OPENMP:
100-
compile_args.append("-fopenmp")
101-
link_args.append("-fopenmp")
103+
# Base flags good for Clang/libc++
104+
compile_args += [
105+
"-O3", "-ffast-math",
106+
"-Wno-unused-function",
107+
"-std=c++11",
108+
"-stdlib=libc++",
109+
f"-mmacosx-version-min={mac_min}",
110+
]
111+
link_args += [
112+
"-std=c++11",
113+
"-stdlib=libc++",
114+
f"-mmacosx-version-min={mac_min}",
115+
]
102116

103-
compile_args.append("-std=c++11")
104-
link_args.append("-std=c++11")
117+
# Optional OpenMP (only if a usable libomp is present)
118+
if os.environ.get("CORNAC_DISABLE_OPENMP") != "1":
119+
inc_dir, lib_dir = find_libomp()
120+
if inc_dir and lib_dir:
121+
compile_args += ["-Xpreprocessor", "-fopenmp", f"-I{inc_dir}"]
122+
link_args += [f"-L{lib_dir}", "-lomp", f"-Wl,-rpath,{lib_dir}"]
123+
USE_OPENMP = True
124+
elif os.environ.get("CORNAC_FORCE_OPENMP") == "1":
125+
raise RuntimeError(
126+
"CORNAC_FORCE_OPENMP=1 but libomp was not found; set OMP_INCLUDE/OMP_LIB or OMP_DIR."
127+
)
128+
else:
129+
# Linux/Unix: prefer OpenMP via compiler default (GCC/Clang + libgomp)
130+
compile_args += [
131+
"-O3", "-ffast-math",
132+
"-Wno-unused-function",
133+
"-Wno-maybe-uninitialized",
134+
"-std=c++11",
135+
"-fopenmp",
136+
]
137+
link_args += ["-std=c++11", "-fopenmp"]
138+
USE_OPENMP = True
105139

106140

107141
extensions = [
@@ -333,4 +367,4 @@ def run(self):
333367
extras_require={"tests": ["pytest", "pytest-pep8", "pytest-xdist", "pytest-cov", "Flask"]},
334368
cmdclass=cmdclass,
335369
packages=find_packages(),
336-
)
370+
)

0 commit comments

Comments
 (0)