@@ -40,6 +40,9 @@ endif()
4040set (CMAKE_CXX_STANDARD_REQUIRED ON )
4141set (CMAKE_CXX_EXTENSIONS OFF )
4242
43+ # Option to build fat library with all dependencies bundled
44+ option (FAT_LIBRARY "Build fat library with all dependencies statically linked" ON )
45+
4346# macOS specific: Find OpenMP from Homebrew
4447if (APPLE )
4548 # Find libomp installed via Homebrew
@@ -64,7 +67,7 @@ if(APPLE)
6467 endif ()
6568endif ()
6669
67- # Find OpenMP (optional on some systems)
70+ # Find OpenMP
6871find_package (OpenMP QUIET )
6972if (OpenMP_CXX_FOUND)
7073 set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS} " )
@@ -77,62 +80,193 @@ endif()
7780find_package (JNI REQUIRED)
7881include_directories (${JNI_INCLUDE_DIRS} )
7982
80- # Find FAISS
81- # FAISS can be installed via Homebrew: brew install faiss
82- # Or via conda: conda install -c pytorch faiss-cpu
83- # Or built from source: https://github.com/facebookresearch/faiss
84- find_package (faiss QUIET )
85- if (NOT faiss_FOUND)
86- # Try to find FAISS using pkg-config
87- find_package (PkgConfig)
88- if (PkgConfig_FOUND)
89- pkg_check_modules(FAISS faiss)
90- endif ()
91-
92- if (NOT FAISS_FOUND)
93- # Manual search for FAISS - check build directory structure first
94- # FAISS build directory has: build/faiss/libfaiss.so and headers in source dir
95- find_path (FAISS_INCLUDE_DIR faiss/Index.h
96- HINTS
97- $ENV{FAISS_HOME}
98- $ENV{FAISS_HOME} /include
99- $ENV{FAISS_BUILD_DIR} /../
100- /root/faiss/faiss
101- /usr/local/include
102- /opt/homebrew/include
103- )
104- find_library (FAISS_LIBRARY faiss
105- HINTS
106- $ENV{FAISS_BUILD_DIR} /faiss
107- $ENV{FAISS_HOME} /build /faiss
108- $ENV{FAISS_HOME} /lib
109- /root/faiss/faiss/build /faiss
110- /usr/local/lib
111- /usr/lib
112- /usr/lib64
113- /opt/homebrew/lib
114- )
115-
116- if (FAISS_INCLUDE_DIR AND FAISS_LIBRARY)
117- set (FAISS_FOUND TRUE )
118- set (FAISS_INCLUDE_DIRS ${FAISS_INCLUDE_DIR} )
119- set (FAISS_LIBRARIES ${FAISS_LIBRARY} )
120- message (STATUS "Found FAISS include dir: ${FAISS_INCLUDE_DIR} " )
121- message (STATUS "Found FAISS library: ${FAISS_LIBRARY} " )
122- else ()
123- message (FATAL_ERROR "FAISS not found. Please install FAISS:\n "
124- " brew install faiss (macOS)\n "
125- " conda install -c pytorch faiss-cpu\n "
126- "Or set FAISS_HOME environment variable to the FAISS source/install directory." )
127- endif ()
128- endif ()
83+ # Find FAISS include directory
84+ find_path (FAISS_INCLUDE_DIR faiss/Index.h
85+ HINTS
86+ $ENV{FAISS_HOME}
87+ $ENV{FAISS_HOME} /include
88+ $ENV{FAISS_BUILD_DIR} /../
89+ /root/faiss/faiss
90+ /usr/local/include
91+ /opt/homebrew/include
92+ )
93+
94+ if (FAISS_INCLUDE_DIR)
95+ include_directories (${FAISS_INCLUDE_DIR} )
96+ message (STATUS "Found FAISS include dir: ${FAISS_INCLUDE_DIR} " )
97+ else ()
98+ message (FATAL_ERROR "FAISS headers not found. Set FAISS_HOME environment variable." )
12999endif ()
130100
131- if (faiss_FOUND)
132- # Using CMake config from installed FAISS
133- set (FAISS_LIBRARIES faiss)
101+ # Collect all static libraries for fat library build
102+ set (STATIC_LIBS_TO_BUNDLE "" )
103+
104+ if (FAT_LIBRARY AND NOT APPLE )
105+ message (STATUS "" )
106+ message (STATUS "========== Building FAT library with all dependencies ==========" )
107+ message (STATUS "" )
108+
109+ # Find static FAISS library
110+ find_library (FAISS_STATIC_LIB
111+ NAMES libfaiss.a faiss.a
112+ HINTS
113+ $ENV{FAISS_BUILD_DIR} /faiss
114+ $ENV{FAISS_HOME} /build /faiss
115+ $ENV{FAISS_HOME} /lib
116+ /root/faiss/faiss/build /faiss
117+ ${HOME} /faiss/build /faiss
118+ /usr/local/lib
119+ /usr/lib
120+ /usr/lib64
121+ /usr/lib/x86_64-linux-gnu
122+ /usr/lib/aarch64-linux-gnu
123+ NO_DEFAULT_PATH
124+ )
125+ if (NOT FAISS_STATIC_LIB)
126+ find_library (FAISS_STATIC_LIB NAMES libfaiss.a)
127+ endif ()
128+
129+ if (FAISS_STATIC_LIB)
130+ message (STATUS "Found static FAISS: ${FAISS_STATIC_LIB} " )
131+ list (APPEND STATIC_LIBS_TO_BUNDLE ${FAISS_STATIC_LIB} )
132+ else ()
133+ message (FATAL_ERROR "Static FAISS library (libfaiss.a) not found!\n "
134+ "Build FAISS from source with:\n "
135+ " git clone https://github.com/facebookresearch/faiss.git\n "
136+ " cd faiss\n "
137+ " cmake -B build -DBUILD_SHARED_LIBS=OFF -DFAISS_ENABLE_GPU=OFF -DFAISS_ENABLE_PYTHON=OFF .\n "
138+ " cmake --build build -j$(nproc)\n "
139+ "Then set: export FAISS_HOME=$(pwd) FAISS_BUILD_DIR=$(pwd)/build" )
140+ endif ()
141+
142+ # Common library search paths for both x86_64 and aarch64
143+ set (LIB_SEARCH_PATHS
144+ /usr/local/lib
145+ /usr/lib
146+ /usr/lib64
147+ /usr/lib/x86_64-linux-gnu
148+ /usr/lib/aarch64-linux-gnu
149+ /opt/OpenBLAS/lib
150+ )
151+
152+ # GCC library search paths
153+ set (GCC_SEARCH_PATHS
154+ /usr/lib/gcc/x86_64-linux-gnu
155+ /usr/lib/gcc/aarch64-linux-gnu
156+ /usr/lib64/gcc/x86_64-linux-gnu
157+ /usr/lib64/gcc/aarch64-linux-gnu
158+ /usr/lib/gcc
159+ )
160+
161+ # Find static OpenBLAS
162+ find_library (OPENBLAS_STATIC_LIB
163+ NAMES libopenblas.a openblas.a
164+ HINTS ${LIB_SEARCH_PATHS}
165+ NO_DEFAULT_PATH
166+ )
167+ if (NOT OPENBLAS_STATIC_LIB)
168+ find_library (OPENBLAS_STATIC_LIB NAMES libopenblas.a)
169+ endif ()
170+
171+ if (OPENBLAS_STATIC_LIB)
172+ message (STATUS "Found static OpenBLAS: ${OPENBLAS_STATIC_LIB} " )
173+ list (APPEND STATIC_LIBS_TO_BUNDLE ${OPENBLAS_STATIC_LIB} )
174+ else ()
175+ message (STATUS "Static OpenBLAS not found, will try dynamic linking" )
176+ endif ()
177+
178+ # Find static LAPACK (optional)
179+ find_library (LAPACK_STATIC_LIB
180+ NAMES liblapack.a lapack.a
181+ HINTS ${LIB_SEARCH_PATHS}
182+ NO_DEFAULT_PATH
183+ )
184+ if (NOT LAPACK_STATIC_LIB)
185+ find_library (LAPACK_STATIC_LIB NAMES liblapack.a)
186+ endif ()
187+
188+ if (LAPACK_STATIC_LIB)
189+ message (STATUS "Found static LAPACK: ${LAPACK_STATIC_LIB} " )
190+ list (APPEND STATIC_LIBS_TO_BUNDLE ${LAPACK_STATIC_LIB} )
191+ endif ()
192+
193+ # Find static BLAS (sometimes needed separately)
194+ find_library (BLAS_STATIC_LIB
195+ NAMES libblas.a blas.a
196+ HINTS ${LIB_SEARCH_PATHS}
197+ )
198+ if (BLAS_STATIC_LIB)
199+ message (STATUS "Found static BLAS: ${BLAS_STATIC_LIB} " )
200+ list (APPEND STATIC_LIBS_TO_BUNDLE ${BLAS_STATIC_LIB} )
201+ endif ()
202+
203+ # Find static gfortran runtime (needed by LAPACK/BLAS)
204+ find_library (GFORTRAN_STATIC_LIB
205+ NAMES libgfortran.a
206+ HINTS ${GCC_SEARCH_PATHS}
207+ PATH_SUFFIXES 13 12 11 10 9 8 7
208+ )
209+ if (GFORTRAN_STATIC_LIB)
210+ message (STATUS "Found static gfortran: ${GFORTRAN_STATIC_LIB} " )
211+ list (APPEND STATIC_LIBS_TO_BUNDLE ${GFORTRAN_STATIC_LIB} )
212+ endif ()
213+
214+ # Find static OpenMP/GOMP
215+ find_library (GOMP_STATIC_LIB
216+ NAMES libgomp.a
217+ HINTS ${GCC_SEARCH_PATHS}
218+ PATH_SUFFIXES 13 12 11 10 9 8 7
219+ )
220+ if (GOMP_STATIC_LIB)
221+ message (STATUS "Found static GOMP: ${GOMP_STATIC_LIB} " )
222+ list (APPEND STATIC_LIBS_TO_BUNDLE ${GOMP_STATIC_LIB} )
223+ endif ()
224+
225+ # Find static quadmath (needed by gfortran on x86_64)
226+ find_library (QUADMATH_STATIC_LIB
227+ NAMES libquadmath.a
228+ HINTS ${GCC_SEARCH_PATHS}
229+ PATH_SUFFIXES 13 12 11 10 9 8 7
230+ )
231+ if (QUADMATH_STATIC_LIB)
232+ message (STATUS "Found static quadmath: ${QUADMATH_STATIC_LIB} " )
233+ list (APPEND STATIC_LIBS_TO_BUNDLE ${QUADMATH_STATIC_LIB} )
234+ endif ()
235+
236+ message (STATUS "" )
237+ message (STATUS "Static libraries to bundle: ${STATIC_LIBS_TO_BUNDLE} " )
238+ message (STATUS "" )
239+
240+ set (FAT_BUILD TRUE )
134241else ()
135- include_directories (${FAISS_INCLUDE_DIRS} )
242+ # Find shared FAISS library for dynamic linking
243+ find_library (FAISS_SHARED_LIB
244+ NAMES faiss libfaiss.so
245+ HINTS
246+ $ENV{FAISS_BUILD_DIR} /faiss
247+ $ENV{FAISS_HOME} /build /faiss
248+ $ENV{FAISS_HOME} /lib
249+ /root/faiss/faiss/build /faiss
250+ /usr/local/lib
251+ /usr/lib
252+ /usr/lib64
253+ /opt/homebrew/lib
254+ )
255+
256+ if (FAISS_SHARED_LIB)
257+ message (STATUS "Found shared FAISS: ${FAISS_SHARED_LIB} " )
258+ else ()
259+ # Try CMake config
260+ find_package (faiss QUIET )
261+ if (faiss_FOUND)
262+ set (FAISS_SHARED_LIB faiss)
263+ message (STATUS "Found FAISS via CMake config" )
264+ else ()
265+ message (FATAL_ERROR "FAISS library not found. Install or build FAISS first." )
266+ endif ()
267+ endif ()
268+
269+ set (FAT_BUILD FALSE )
136270endif ()
137271
138272# Create the shared library
@@ -141,16 +275,76 @@ add_library(paimon_faiss_jni SHARED
141275)
142276
143277# Link libraries
144- target_link_libraries (paimon_faiss_jni
145- ${FAISS_LIBRARIES}
146- ${JNI_LIBRARIES}
147- )
148-
149- # Link OpenMP if found
150- if (OpenMP_CXX_FOUND)
151- target_link_libraries (paimon_faiss_jni OpenMP::OpenMP_CXX)
152- elseif (APPLE AND LIBOMP_PREFIX)
153- target_link_libraries (paimon_faiss_jni "${LIBOMP_PREFIX} /lib/libomp.dylib" )
278+ if (FAT_BUILD)
279+ message (STATUS "Linking all dependencies into fat library..." )
280+
281+ # For proper fat library linking, we need to:
282+ # 1. Use -Wl,--whole-archive to include ALL symbols from static libs
283+ # 2. Use -Wl,--no-whole-archive after the static libs
284+ # 3. Link system libraries normally
285+
286+ # Build the whole-archive link line
287+ set (WHOLE_ARCHIVE_LIBS "" )
288+ foreach (static_lib ${STATIC_LIBS_TO_BUNDLE} )
289+ list (APPEND WHOLE_ARCHIVE_LIBS ${static_lib} )
290+ endforeach ()
291+
292+ # Use generator expressions for proper whole-archive linking
293+ target_link_libraries (paimon_faiss_jni PRIVATE
294+ -Wl,--whole-archive
295+ ${WHOLE_ARCHIVE_LIBS}
296+ -Wl,--no -whole-archive
297+ ${JNI_LIBRARIES}
298+ pthread
299+ m
300+ dl
301+ )
302+
303+ # If static OpenBLAS wasn't found, link dynamically
304+ if (NOT OPENBLAS_STATIC_LIB)
305+ target_link_libraries (paimon_faiss_jni PRIVATE openblas)
306+ endif ()
307+
308+ # If static GOMP wasn't found, link dynamically
309+ if (NOT GOMP_STATIC_LIB AND OpenMP_CXX_FOUND)
310+ target_link_libraries (paimon_faiss_jni PRIVATE gomp)
311+ endif ()
312+
313+ # If static gfortran wasn't found but LAPACK is used, link dynamically
314+ if (NOT GFORTRAN_STATIC_LIB AND LAPACK_STATIC_LIB)
315+ target_link_libraries (paimon_faiss_jni PRIVATE gfortran)
316+ endif ()
317+
318+ # Set RPATH to $ORIGIN for any remaining shared deps
319+ set_target_properties (paimon_faiss_jni PROPERTIES
320+ BUILD_WITH_INSTALL_RPATH TRUE
321+ INSTALL_RPATH "$ORIGIN"
322+ )
323+
324+ # Hide internal symbols to reduce size and avoid symbol conflicts
325+ set_target_properties (paimon_faiss_jni PROPERTIES
326+ C_VISIBILITY_PRESET hidden
327+ CXX_VISIBILITY_PRESET hidden
328+ VISIBILITY_INLINES_HIDDEN ON
329+ )
330+
331+ # Strip unnecessary symbols to reduce size
332+ add_custom_command (TARGET paimon_faiss_jni POST_BUILD
333+ COMMAND ${CMAKE_STRIP} --strip-unneeded $<TARGET_FILE:paimon_faiss_jni> 2>/dev/null || true
334+ COMMENT "Stripping unnecessary symbols from fat library"
335+ )
336+ else ()
337+ # Dynamic linking
338+ target_link_libraries (paimon_faiss_jni
339+ ${FAISS_SHARED_LIB}
340+ ${JNI_LIBRARIES}
341+ )
342+
343+ if (OpenMP_CXX_FOUND)
344+ target_link_libraries (paimon_faiss_jni OpenMP::OpenMP_CXX)
345+ elseif (APPLE AND LIBOMP_PREFIX)
346+ target_link_libraries (paimon_faiss_jni "${LIBOMP_PREFIX} /lib/libomp.dylib" )
347+ endif ()
154348endif ()
155349
156350# Set output directory
@@ -162,6 +356,8 @@ set_target_properties(paimon_faiss_jni PROPERTIES
162356if (APPLE )
163357 set_target_properties (paimon_faiss_jni PROPERTIES
164358 SUFFIX ".dylib"
359+ BUILD_WITH_INSTALL_RPATH TRUE
360+ INSTALL_RPATH "@loader_path;/opt/homebrew/lib;/usr/local/lib"
165361 )
166362elseif (UNIX )
167363 set_target_properties (paimon_faiss_jni PROPERTIES
0 commit comments