@@ -5,6 +5,9 @@ function(resolve_pc_libs out_var root_target)
55 set (_result "" )
66 set (_visited "" )
77
8+ # List of imported targets to skip (handled separately for pkg-config)
9+ set (_skip_targets "BLAS::BLAS" "LAPACK::LAPACK" )
10+
811 function (_resolve target )
912 # Prevent infinite recursion
1013 if (target IN_LIST _visited)
@@ -13,6 +16,12 @@ function(resolve_pc_libs out_var root_target)
1316 list (APPEND _visited "${target} " )
1417 set (_visited "${_visited} " PARENT_SCOPE )
1518
19+ # Skip BLAS/LAPACK imported targets — they will be handled
20+ # separately via Requires.private / Libs.private
21+ if (target IN_LIST _skip_targets)
22+ return ()
23+ endif ()
24+
1625 if (TARGET "${target} " )
1726 # Recurse into PUBLIC/INTERFACE deps first
1827 get_target_property (deps "${target} " INTERFACE_LINK_LIBRARIES )
@@ -50,10 +59,151 @@ function(resolve_pc_libs out_var root_target)
5059 set (${out_var} "${_result} " PARENT_SCOPE )
5160endfunction ()
5261
62+ # Convert absolute library paths to -l flags
63+ # e.g. /usr/lib/libopenblas.so -> -lopenblas
64+ # /usr/lib/libopenblas.so.0.3 -> -lopenblas
65+ # /usr/lib/libopenblas.dll.a -> -lopenblas
66+ function (libs_to_linker_flags out_var libs )
67+ set (_flags "" )
68+ foreach (lib IN LISTS libs)
69+ if (IS_ABSOLUTE "${lib} " )
70+ # Extract the full filename, then strip everything from the first dot
71+ # onwards. Using NAME_WE would only strip the last extension, which
72+ # breaks for versioned libraries (e.g. libopenblas.so.0.3 -> libopenblas.so.0).
73+ get_filename_component (_name "${lib} " NAME )
74+ string (REGEX REPLACE "\\ ..*$" "" _name "${_name} " )
75+ # Strip leading "lib" prefix if present
76+ string (REGEX REPLACE "^lib" "" _name "${_name} " )
77+ list (APPEND _flags "-l${_name} " )
78+ else ()
79+ # Already a flag like -lopenblas or -lm
80+ list (APPEND _flags "${lib} " )
81+ endif ()
82+ endforeach ()
83+ list (REMOVE_DUPLICATES _flags)
84+ set (${out_var} "${_flags} " PARENT_SCOPE )
85+ endfunction ()
86+
5387resolve_pc_libs (PC_LIBS ${PROJECT_NAME} )
5488
5589string (REPLACE ";" " " PC_CONTENT "${PC_LIBS} " )
5690
91+ # --- Handle BLAS/LAPACK for pkg-config ---
92+ # Use Requires.private when a vendor .pc file exists,
93+ # otherwise fall back to Libs.private with -l flags.
94+ set (PC_REQUIRES_PRIVATE "" )
95+ set (PC_LIBS_PRIVATE "" )
96+
97+ if (BLAS_FOUND OR LAPACK_FOUND)
98+ find_package (PkgConfig QUIET )
99+
100+ # Known pkg-config names for common BLAS/LAPACK vendors.
101+ # We try the most specific names first, then fall back to generic ones.
102+ set (_blas_pc_names
103+ openblas # OpenBLAS
104+ blas-openblas # Debian/Ubuntu OpenBLAS
105+ mkl-dynamic-lp64-seq # Intel MKL (dynamic, LP64, sequential)
106+ mkl-dynamic-ilp64-seq # Intel MKL (dynamic, ILP64, sequential)
107+ mkl-dynamic-lp64-iomp # Intel MKL (dynamic, LP64, OpenMP)
108+ mkl-dynamic-ilp64-iomp # Intel MKL (dynamic, ILP64, OpenMP)
109+ mkl-static-lp64-seq # Intel MKL (static, LP64, sequential)
110+ mkl-static-ilp64-seq # Intel MKL (static, ILP64, sequential)
111+ blas # Generic BLAS
112+ blas-netlib # Netlib BLAS
113+ )
114+ set (_lapack_pc_names
115+ lapack # Generic LAPACK
116+ lapack-netlib # Netlib LAPACK
117+ )
118+
119+ # BLAS packages that are known to also provide LAPACK
120+ set (_blas_includes_lapack_list
121+ openblas
122+ blas-openblas
123+ mkl-dynamic-lp64-seq
124+ mkl-dynamic-ilp64-seq
125+ mkl-dynamic-lp64-iomp
126+ mkl-dynamic-ilp64-iomp
127+ mkl-static-lp64-seq
128+ mkl-static-ilp64-seq
129+ )
130+
131+ set (_blas_pc_found FALSE )
132+ set (_blas_includes_lapack FALSE )
133+ set (_lapack_pc_found FALSE )
134+
135+ if (PKG_CONFIG_FOUND)
136+ # --- Detect BLAS pkg-config package ---
137+ if (BLAS_FOUND)
138+ foreach (_pkg IN LISTS _blas_pc_names)
139+ pkg_check_modules (_BLAS_PC_${_pkg} QUIET "${_pkg} " )
140+ if (_BLAS_PC_${_pkg} _FOUND)
141+ list (APPEND PC_REQUIRES_PRIVATE "${_pkg} " )
142+ set (_blas_pc_found TRUE )
143+ if (_pkg IN_LIST _blas_includes_lapack_list)
144+ set (_blas_includes_lapack TRUE )
145+ endif ()
146+ message (STATUS "pkg-config: using '${_pkg} ' for BLAS" )
147+ break ()
148+ endif ()
149+ endforeach ()
150+ endif ()
151+
152+ # --- Detect LAPACK pkg-config package ---
153+ # Skip separate LAPACK detection if the BLAS package already includes LAPACK
154+ # (e.g. OpenBLAS, MKL). Standalone BLAS packages like netlib still need this.
155+ if (LAPACK_FOUND AND NOT _blas_includes_lapack)
156+ foreach (_pkg IN LISTS _lapack_pc_names)
157+ pkg_check_modules (_LAPACK_PC_${_pkg} QUIET "${_pkg} " )
158+ if (_LAPACK_PC_${_pkg} _FOUND)
159+ list (APPEND PC_REQUIRES_PRIVATE "${_pkg} " )
160+ set (_lapack_pc_found TRUE )
161+ message (STATUS "pkg-config: using '${_pkg} ' for LAPACK" )
162+ break ()
163+ endif ()
164+ endforeach ()
165+ endif ()
166+ endif ()
167+
168+ # --- Fallback: convert absolute paths to -l flags ---
169+ if (BLAS_FOUND AND NOT _blas_pc_found)
170+ libs_to_linker_flags (_blas_flags "${BLAS_LIBRARIES} " )
171+ list (APPEND PC_LIBS_PRIVATE ${_blas_flags} )
172+ message (STATUS "pkg-config: no .pc file found for BLAS, using linker flags: ${_blas_flags} " )
173+ endif ()
174+
175+ if (LAPACK_FOUND AND NOT _lapack_pc_found AND NOT _blas_includes_lapack)
176+ libs_to_linker_flags (_lapack_flags "${LAPACK_LIBRARIES} " )
177+ list (APPEND PC_LIBS_PRIVATE ${_lapack_flags} )
178+ message (STATUS "pkg-config: no .pc file found for LAPACK, using linker flags: ${_lapack_flags} " )
179+ endif ()
180+ endif ()
181+
182+ # Flatten lists to space-separated strings
183+ string (REPLACE ";" " " PC_REQUIRES_PRIVATE "${PC_REQUIRES_PRIVATE} " )
184+ string (REPLACE ";" " " PC_LIBS_PRIVATE "${PC_LIBS_PRIVATE} " )
185+
186+ # Handle absolute vs relative install paths for libdir
187+ if (IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR} " )
188+ set (PC_LIBDIR "${CMAKE_INSTALL_LIBDIR} " )
189+ else ()
190+ set (PC_LIBDIR "\$ {prefix}/${CMAKE_INSTALL_LIBDIR} " )
191+ endif ()
192+
193+ # Handle absolute vs relative install paths for includedir
194+ if (IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR} " )
195+ set (PC_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR} " )
196+ else ()
197+ set (PC_INCLUDEDIR "\$ {prefix}/${CMAKE_INSTALL_INCLUDEDIR} " )
198+ endif ()
199+
200+ # Handle absolute vs relative install paths for moduledir
201+ if (IS_ABSOLUTE "${CMAKE_INSTALL_MODULEDIR} " )
202+ set (PC_MODULEDIR "${CMAKE_INSTALL_MODULEDIR} " )
203+ else ()
204+ set (PC_MODULEDIR "\$ {prefix}/${CMAKE_INSTALL_MODULEDIR} " )
205+ endif ()
206+
57207configure_file (
58208 "${CMAKE_CURRENT_SOURCE_DIR} /config/template.pc"
59209 "${CMAKE_CURRENT_BINARY_DIR} /${PROJECT_NAME} .pc"
@@ -64,4 +214,3 @@ install(
64214 "${CMAKE_CURRENT_BINARY_DIR} /${PROJECT_NAME} .pc"
65215 DESTINATION "${CMAKE_INSTALL_LIBDIR} /pkgconfig"
66216)
67-
0 commit comments