Skip to content

Commit a1150fa

Browse files
Copilotjobovypre-commit-ci[bot]
authored
Auto-detect GSL paths in setup.py to fix compilation failures when CFLAGS/LDFLAGS are not set (#831)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: jobovy <1044876+jobovy@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Jo Bovy <bovy@astro.utoronto.ca>
1 parent 5b3ae02 commit a1150fa

4 files changed

Lines changed: 90 additions & 15 deletions

File tree

.github/workflows/build.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,11 +248,12 @@ jobs:
248248
run: |
249249
brew install gsl libomp
250250
brew info libomp
251-
echo "CFLAGS=-I$(brew --prefix)/include -I$(brew --prefix)/opt/libomp/include" >> $GITHUB_ENV
252-
echo "CPPFLAGS=-I$(brew --prefix)/include -I$(brew --prefix)/opt/libomp/include" >> $GITHUB_ENV
253-
echo "LDFLAGS=-L$(brew --prefix)/lib -L$(brew --prefix)/opt/libomp/lib" >> $GITHUB_ENV
254-
echo "LD_LIBRARY_PATH=$(brew --prefix)/lib:$(brew --prefix)/opt/libomp/lib" >> $GITHUB_ENV
255-
echo "DYLD_LIBRARY_PATH=$(brew --prefix)/lib:$(brew --prefix)/opt/libomp/lib" >> $GITHUB_ENV
251+
# We don't setup the GSL env variables here, because they are auto-added by setup.py
252+
echo "CFLAGS=-I$(brew --prefix)/opt/libomp/include" >> $GITHUB_ENV
253+
echo "CPPFLAGS=-I$(brew --prefix)/opt/libomp/include" >> $GITHUB_ENV
254+
echo "LDFLAGS=-L$(brew --prefix)/opt/libomp/lib" >> $GITHUB_ENV
255+
echo "LD_LIBRARY_PATH=$(brew --prefix)/opt/libomp/lib" >> $GITHUB_ENV
256+
echo "DYLD_LIBRARY_PATH=$(brew --prefix)/opt/libomp/lib" >> $GITHUB_ENV
256257
- uses: actions/cache@v5
257258
with:
258259
path: ~/.cache/pip

.github/workflows/build_windows.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,11 @@ jobs:
214214
create-args: gsl
215215
cache-environment: true
216216
cache-environment-key: ${{ runner.os }}-${{ hashFiles('.github/workflows/build_windows.yml') }}
217-
- name: Set GSL environment variables
217+
- name: Set conda environment variables necessary to run gsl-config.bat and auto-detect GSL paths in setup.py
218218
shell: bash -l {0}
219219
run: |
220-
echo "INCLUDE=$CONDA_PREFIX\\Library\\include" >> $GITHUB_ENV
221-
echo "LIB=$CONDA_PREFIX\\Library\\lib" >> $GITHUB_ENV
222-
echo "LIBPATH=$CONDA_PREFIX\\Library\\lib" >> $GITHUB_ENV
223220
echo "$CONDA_PREFIX\\Library\\bin" >> $GITHUB_PATH # necessary when we don't activate the environment
221+
echo "CONDA_PREFIX=$CONDA_PREFIX" >> $GITHUB_ENV # necessary for gsl-config.bat to find GSL paths
224222
- name: Install Python dependencies
225223
run: |
226224
pip install --upgrade --upgrade-strategy eager numpy scipy matplotlib numexpr setuptools cython pytest tqdm

doc/source/installation.rst

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,15 @@ If you are reading this, either the simple installation instructions at the top
108108

109109
Once you have installed the GSL, compile ``galpy`` from source using::
110110

111+
python -m pip install --no-binary galpy galpy
112+
113+
Note that ``galpy``'s installation from source should automatically detect the
114+
relevant GSL paths, so you shouldn't have to set any environment variables. If
115+
you do run into problems with undefined GSL symbols, set the environment variables
116+
using::
117+
111118
export CFLAGS="$CFLAGS -I`gsl-config --prefix`/include"
112119
export LDFLAGS="$LDFLAGS -L`gsl-config --prefix`/lib"
113-
python -m pip install --no-binary galpy galpy
114120

115121
The commands in this section so far all install the latest release. If you want
116122
to install the latest bleeding-edge version, you have two options. If the
@@ -164,15 +170,19 @@ If you are reading this, either the simple installation instructions at the top
164170

165171
conda install -c conda-forge gsl llvm-openmp
166172

167-
Then set the path and relevant environment variables using::
168-
169-
export CFLAGS="$CFLAGS -I`gsl-config --prefix`/include"
170-
export LDFLAGS="$LDFLAGS -L`gsl-config --prefix`/lib"
171-
172173
Once you have installed the GSL and OpenMP, compile ``galpy`` from source using::
173174

174175
python -m pip install --no-binary galpy galpy
175176

177+
Note that ``galpy``'s installation from source should automatically detect the
178+
relevant GSL paths, so you shouldn't have to set any environment variables. If
179+
you do run into problems with undefined GSL symbols, set the environment variables
180+
using::
181+
182+
export CFLAGS="$CFLAGS -I`gsl-config --prefix`/include"
183+
export LDFLAGS="$LDFLAGS -L`gsl-config --prefix`/lib"
184+
export DYLD_LIBRARY_PATH="$DYLD_LIBRARY_PATH:`gsl-config --prefix`/lib"
185+
176186
The commands in this section so far all install the latest release. If you want
177187
to install the latest bleeding-edge version, you have two options. If the
178188
installation in the :ref:`tldr_installation` works for you, you can install using::

setup.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,66 @@
8686
gsl_version = gsl_version.split(".")
8787
extra_compile_args.append("-D GSL_MAJOR_VERSION=%s" % (gsl_version[0]))
8888

89+
# Use gsl-config to get GSL include and library paths to ensure they can be
90+
# found by the compiler and linker even if CFLAGS/LDFLAGS are not set;
91+
# skip paths already present in CFLAGS/LDFLAGS (or INCLUDE/LIB on Windows) to avoid duplicates
92+
gsl_include_dirs = []
93+
gsl_library_dirs = []
94+
if "PYODIDE" not in os.environ:
95+
if WIN32:
96+
_existing_includes = set(
97+
filter(None, os.environ.get("INCLUDE", "").split(os.pathsep))
98+
)
99+
_existing_libdirs = set(
100+
filter(None, os.environ.get("LIB", "").split(os.pathsep))
101+
)
102+
else:
103+
_existing_includes = {
104+
f[2:] for f in os.environ.get("CFLAGS", "").split() if f.startswith("-I")
105+
}
106+
_existing_libdirs = {
107+
f[2:] for f in os.environ.get("LDFLAGS", "").split() if f.startswith("-L")
108+
}
109+
try:
110+
# shell=True required on Windows to execute gsl-config.bat
111+
# (https://docs.python.org/3/library/subprocess.html#converting-argument-sequence)
112+
gsl_cflags = (
113+
subprocess.check_output(
114+
["gsl-config", "--cflags"], shell=sys.platform.startswith("win")
115+
)
116+
.decode("utf-8")
117+
.strip()
118+
)
119+
for flag in gsl_cflags.split():
120+
if flag.startswith("-I"):
121+
path = flag[2:].strip('"')
122+
# Verify the path actually contains GSL headers before using it
123+
if path not in _existing_includes and os.path.isfile(
124+
os.path.join(path, "gsl", "gsl_math.h")
125+
):
126+
gsl_include_dirs.append(path)
127+
except (OSError, subprocess.CalledProcessError):
128+
pass
129+
try:
130+
gsl_libs = (
131+
subprocess.check_output(
132+
["gsl-config", "--libs"], shell=sys.platform.startswith("win")
133+
)
134+
.decode("utf-8")
135+
.strip()
136+
)
137+
for flag in gsl_libs.split():
138+
if flag.startswith("-L"):
139+
path = flag[2:].strip('"')
140+
# Verify the path actually contains GSL libraries before using it
141+
if path not in _existing_libdirs and (
142+
glob.glob(os.path.join(path, "libgsl*"))
143+
or os.path.isfile(os.path.join(path, "gsl.lib"))
144+
):
145+
gsl_library_dirs.append(path)
146+
except (OSError, subprocess.CalledProcessError):
147+
pass
148+
89149
# HACK for testing
90150
# gsl_version= ['0','0']
91151

@@ -118,6 +178,7 @@
118178
"galpy/actionAngle/actionAngle_c_ext",
119179
"xsf/include",
120180
]
181+
galpy_c_include_dirs.extend(gsl_include_dirs)
121182

122183
# actionAngleTorus C extension (files here, so we can compile a single extension if so desidered)
123184
actionAngleTorus_c_src = glob.glob("galpy/actionAngle/actionAngleTorus_c_ext/*.cc")
@@ -150,6 +211,7 @@
150211
"galpy/potential/potential_c_ext",
151212
"xsf/include",
152213
]
214+
actionAngleTorus_include_dirs.extend(gsl_include_dirs)
153215

154216
if single_ext: # add the code and libraries for the actionAngleTorus extensions
155217
if os.path.exists("galpy/actionAngle/actionAngleTorus_c_ext/torus/src"):
@@ -167,6 +229,8 @@
167229
sources=galpy_c_src,
168230
libraries=galpy_c_libraries,
169231
include_dirs=galpy_c_include_dirs,
232+
library_dirs=gsl_library_dirs,
233+
runtime_library_dirs=[] if WIN32 else gsl_library_dirs,
170234
extra_compile_args=extra_compile_args,
171235
extra_link_args=extra_link_args,
172236
)
@@ -185,6 +249,8 @@
185249
sources=actionAngleTorus_c_src,
186250
libraries=galpy_c_libraries,
187251
include_dirs=actionAngleTorus_include_dirs,
252+
library_dirs=gsl_library_dirs,
253+
runtime_library_dirs=[] if WIN32 else gsl_library_dirs,
188254
extra_compile_args=extra_compile_args,
189255
extra_link_args=extra_link_args,
190256
)

0 commit comments

Comments
 (0)