2828
2929
3030def get_compiler_type ():
31- """Find compiler used for building extensions ."""
31+ """Return the compiler type used during the build ."""
3232 cc_arg = [a for a in sys .argv if a .startswith ("--compiler=" )]
3333 if cc_arg :
3434 return cc_arg [- 1 ].split ("=" , 1 )[1 ]
@@ -38,48 +38,97 @@ def get_compiler_type():
3838
3939
4040def get_gsl_config ():
41- """Return dictionary with paths to GSL library."""
42- gslcfgpaths = [Path (p ) / "gsl-config" for p in ([MYDIR ] + os .environ ["PATH" ].split (os .pathsep ))]
43- gslcfgpaths = [p for p in gslcfgpaths if p .is_file ()]
41+ """
42+ Determine the GSL include and library directories by trying in order:
43+ 1) CONDA_PREFIX,
44+ 2) GSL_PATH,
45+ 3) gsl-config (for Unix-like systems).
46+ Raises EnvironmentError if none are found.
47+ """
4448 rv = {"include_dirs" : [], "library_dirs" : []}
45- if not gslcfgpaths :
46- warnings .warn (f"Cannot find gsl-config in { MYDIR } nor in system PATH." )
47- return rv
48- gslcfg = gslcfgpaths [0 ]
49- txt = gslcfg .read_text ()
50- mprefix = re .search (r"(?m)^prefix=(.+)" , txt )
51- minclude = re .search (r"(?m)^[^#]*\s-I(\S+)" , txt )
52- mlibpath = re .search (r"(?m)^[^#]*\s-L(\S+)" , txt )
53- if not mprefix :
54- raise RuntimeError (f"Cannot find 'prefix=' line in { gslcfg } ." )
55- p = Path (mprefix .group (1 ))
56- rv ["include_dirs" ].append (str (minclude .group (1 ) if minclude else p / "include" ))
57- rv ["library_dirs" ].append (str (mlibpath .group (1 ) if mlibpath else p / "lib" ))
58- return rv
59-
60-
61- def get_gsl_config_win ():
62- """Return dictionary with paths to GSL library on Windows."""
49+
50+ # 1. Check using CONDA_PREFIX.
51+ conda_prefix = os .environ .get ("CONDA_PREFIX" , "" )
52+ if conda_prefix :
53+ if os .name == "nt" :
54+ inc = Path (conda_prefix ) / "Library" / "include"
55+ lib = Path (conda_prefix ) / "Library" / "lib"
56+ else :
57+ inc = Path (conda_prefix ) / "include"
58+ lib = Path (conda_prefix ) / "lib"
59+ if inc .is_dir () and lib .is_dir ():
60+ rv ["include_dirs" ].append (str (inc ))
61+ rv ["library_dirs" ].append (str (lib ))
62+ return rv
63+ else :
64+ warnings .warn (
65+ f"CONDA_PREFIX is set to { conda_prefix } , " "but GSL not found at those paths. Proceeding..."
66+ )
67+
68+ # 2. Check using GSL_PATH.
6369 gsl_path = os .environ .get ("GSL_PATH" , "" )
6470 if gsl_path :
6571 inc = Path (gsl_path ) / "include"
6672 lib = Path (gsl_path ) / "lib"
67- else :
68- conda_prefix = os .environ .get ("CONDA_PREFIX" )
69- if conda_prefix :
70- inc = Path (conda_prefix ) / "Library" / "include"
71- lib = Path (conda_prefix ) / "Library" / "lib"
73+ if inc .is_dir () and lib .is_dir ():
74+ rv ["include_dirs" ].append (str (inc ))
75+ rv ["library_dirs" ].append (str (lib ))
76+ return rv
7277 else :
7378 raise EnvironmentError (
74- "Neither GSL_PATH nor CONDA_PREFIX environment variables are set. "
75- "Please ensure GSL is installed and GSL_PATH is correctly set."
79+ f"GSL_PATH={ gsl_path } is set, but { inc } or { lib } not found. " "Please verify your GSL_PATH."
7680 )
77- return {"include_dirs" : [str (inc )], "library_dirs" : [str (lib )]}
81+
82+ # 3. Try using the gsl-config executable (only on Unix-like systems).
83+ if os .name != "nt" :
84+ path_dirs = os .environ .get ("PATH" , "" ).split (os .pathsep )
85+ gslcfg_paths = [Path (p ) / "gsl-config" for p in path_dirs if p ]
86+ gslcfg_paths = [p for p in gslcfg_paths if p .is_file ()]
87+ if gslcfg_paths :
88+ gslcfg = gslcfg_paths [0 ]
89+ txt = gslcfg .read_text ()
90+ prefix_match = re .search (r"(?m)^prefix=(.+)" , txt )
91+ include_match = re .search (r"(?m)^[^#]*\s-I(\S+)" , txt )
92+ lib_match = re .search (r"(?m)^[^#]*\s-L(\S+)" , txt )
93+ if prefix_match :
94+ prefix_path = Path (prefix_match .group (1 ))
95+ inc_dir = include_match .group (1 ) if include_match else (prefix_path / "include" )
96+ lib_dir = lib_match .group (1 ) if lib_match else (prefix_path / "lib" )
97+ rv ["include_dirs" ].append (str (inc_dir ))
98+ rv ["library_dirs" ].append (str (lib_dir ))
99+ return rv
100+ else :
101+ raise RuntimeError (f"Cannot parse 'prefix=' from { gslcfg } ." )
102+ else :
103+ warnings .warn (
104+ "No gsl-config found in PATH. GSL may not be installed or not in PATH. "
105+ "Proceeding without GSL configuration."
106+ )
107+
108+ # 4. Nothing found: raise error.
109+ raise EnvironmentError (
110+ "Unable to locate GSL:\n "
111+ "1) CONDA_PREFIX not set or no GSL there\n "
112+ "2) GSL_PATH not set or invalid\n "
113+ "3) gsl-config not available\n "
114+ "Please set GSL_PATH or use a conda environment with GSL."
115+ )
78116
79117
80118class CustomBuildExt (build_ext ):
81119 def run (self ):
120+ # Retrieve the GSL library directories and append them to each extension.
121+ gsl_cfg = get_gsl_config ()
122+ lib_dirs = gsl_cfg .get ("library_dirs" , [])
123+ for ext in self .extensions :
124+ # Add gsl lib for linking.
125+ ext .library_dirs .extend (lib_dirs )
126+ # Embed RPATH flags, runtime linking without LD_LIBRARY_PATH.
127+ ext .extra_link_args = ext .extra_link_args or []
128+ for lib in lib_dirs :
129+ ext .extra_link_args .append (f"-Wl,-rpath,{ lib } " )
82130 super ().run ()
131+ # Avoid dll error
83132 gsl_path = (
84133 Path (os .environ .get ("GSL_PATH" ))
85134 if os .environ .get ("GSL_PATH" )
@@ -88,46 +137,44 @@ def run(self):
88137 bin_path = gsl_path / "bin"
89138 dest_path = Path (self .build_lib ) / "diffpy" / "pdffit2"
90139 dest_path .mkdir (parents = True , exist_ok = True )
91-
92140 for dll_file in bin_path .glob ("gsl*.dll" ):
93141 shutil .copy (str (dll_file ), str (dest_path ))
94142
95143
96- # ------------------------ ----------------------------------------------------
144+ # Compile and link options ----------------------------------------------------
97145
98- # Compile and link options
99146os_name = os .name
100- if os_name == "nt" :
101- gcfg = get_gsl_config_win ()
102- else :
103- gcfg = get_gsl_config ()
147+ gcfg = get_gsl_config ()
104148
149+ # On macOS, dynamic linking may not be needed
105150if sys .platform == "darwin" :
106151 libraries = []
107152else :
108153 libraries = ["gsl" ]
109154
110155include_dirs = [MYDIR ] + gcfg ["include_dirs" ]
111- library_dirs = [ ]
156+ library_dirs = gcfg [ "library_dirs" ]
112157define_macros = []
113158extra_objects = []
114159extra_compile_args = []
115160extra_link_args = []
116161
117-
118162compiler_type = get_compiler_type ()
119163if compiler_type in ("unix" , "cygwin" , "mingw32" ):
120164 extra_compile_args = ["-std=c++11" , "-Wall" , "-Wno-write-strings" , "-O3" , "-funroll-loops" , "-ffast-math" ]
121- extra_objects += [
165+ # Check for static GSL libraries and add them if found.
166+ static_libs = [
122167 os .path .join (p , "libgsl.a" ) for p in gcfg ["library_dirs" ] if os .path .isfile (os .path .join (p , "libgsl.a" ))
123168 ]
169+ if static_libs :
170+ extra_objects += static_libs
171+ # Use static linking: remove "-lgsl" to avoid dynamic linking conflicts.
172+ libraries = []
124173elif compiler_type == "msvc" :
125174 define_macros += [("_USE_MATH_DEFINES" , None )]
126175 extra_compile_args = ["/EHs" ]
127- library_dirs += gcfg ["library_dirs" ]
128- # add optimization flags for other compilers if needed
129176
130- # Define extension arguments
177+ # Extension keyword arguments.
131178ext_kws = {
132179 "include_dirs" : include_dirs ,
133180 "libraries" : libraries ,
@@ -139,8 +186,8 @@ def run(self):
139186}
140187
141188
142- # Define extensions
143189def create_extensions ():
190+ """Create the list of Extension objects for the build."""
144191 ext = Extension ("diffpy.pdffit2.pdffit2" , glob .glob ("src/extensions/**/*.cc" ), ** ext_kws )
145192 return [ext ]
146193
@@ -153,5 +200,3 @@ def create_extensions():
153200if __name__ == "__main__" :
154201 setup_args ["ext_modules" ] = create_extensions ()
155202 setup (** setup_args )
156-
157- # End of file
0 commit comments