211 lines
7.3 KiB
CMake
211 lines
7.3 KiB
CMake
# External dependencies CMakeLists.txt - Fixed version with proper BLAS handling
|
|
|
|
include(FetchContent)
|
|
|
|
# Set policies for FetchContent
|
|
if(POLICY CMP0135)
|
|
cmake_policy(SET CMP0135 NEW)
|
|
endif()
|
|
|
|
# Fetch libsvm
|
|
FetchContent_Declare(
|
|
libsvm
|
|
GIT_REPOSITORY https://github.com/cjlin1/libsvm.git
|
|
GIT_TAG v332
|
|
)
|
|
FetchContent_MakeAvailable(libsvm)
|
|
|
|
# Fetch liblinear
|
|
FetchContent_Declare(
|
|
liblinear
|
|
GIT_REPOSITORY https://github.com/cjlin1/liblinear.git
|
|
GIT_TAG v249
|
|
)
|
|
FetchContent_MakeAvailable(liblinear)
|
|
|
|
# Build libsvm as OBJECT library
|
|
if(EXISTS "${libsvm_SOURCE_DIR}/svm.cpp")
|
|
set(LIBSVM_SOURCES "${libsvm_SOURCE_DIR}/svm.cpp")
|
|
|
|
add_library(libsvm_objects OBJECT ${LIBSVM_SOURCES})
|
|
|
|
# Set properties for the object library
|
|
target_include_directories(libsvm_objects
|
|
PUBLIC
|
|
${libsvm_SOURCE_DIR}
|
|
)
|
|
|
|
# Set C++ standard for libsvm
|
|
target_compile_features(libsvm_objects PRIVATE cxx_std_17)
|
|
|
|
# Make the include directory available to parent scope
|
|
set(LIBSVM_INCLUDE_DIR ${libsvm_SOURCE_DIR} PARENT_SCOPE)
|
|
|
|
message(STATUS "libsvm built successfully as object library")
|
|
else()
|
|
message(WARNING "libsvm source files not found")
|
|
# Create empty object library
|
|
add_library(libsvm_objects OBJECT)
|
|
set(LIBSVM_INCLUDE_DIR "" PARENT_SCOPE)
|
|
endif()
|
|
|
|
# Build liblinear with BLAS as a static library
|
|
set(LIBLINEAR_SOURCES)
|
|
set(BLAS_SOURCES)
|
|
|
|
# Check for main liblinear source files
|
|
if(EXISTS "${liblinear_SOURCE_DIR}/linear.cpp")
|
|
list(APPEND LIBLINEAR_SOURCES "${liblinear_SOURCE_DIR}/linear.cpp")
|
|
endif()
|
|
|
|
# Check for optimization files (tron.cpp, newton.cpp, etc.)
|
|
if(EXISTS "${liblinear_SOURCE_DIR}/tron.cpp")
|
|
list(APPEND LIBLINEAR_SOURCES "${liblinear_SOURCE_DIR}/tron.cpp")
|
|
elseif(EXISTS "${liblinear_SOURCE_DIR}/newton.cpp")
|
|
list(APPEND LIBLINEAR_SOURCES "${liblinear_SOURCE_DIR}/newton.cpp")
|
|
endif()
|
|
|
|
# Check for BLAS files in blas directory
|
|
if(EXISTS "${liblinear_SOURCE_DIR}/blas")
|
|
# Explicitly add the required BLAS source files
|
|
set(BLAS_FILES
|
|
"${liblinear_SOURCE_DIR}/blas/daxpy.c"
|
|
"${liblinear_SOURCE_DIR}/blas/ddot.c"
|
|
"${liblinear_SOURCE_DIR}/blas/dnrm2.c"
|
|
"${liblinear_SOURCE_DIR}/blas/dscal.c"
|
|
)
|
|
|
|
# Check which BLAS files actually exist and add them
|
|
foreach(blas_file ${BLAS_FILES})
|
|
if(EXISTS ${blas_file})
|
|
list(APPEND BLAS_SOURCES ${blas_file})
|
|
message(STATUS "Adding BLAS file: ${blas_file}")
|
|
endif()
|
|
endforeach()
|
|
else()
|
|
message(WARNING "BLAS directory not found in liblinear source")
|
|
endif()
|
|
|
|
# Create liblinear with BLAS
|
|
if(LIBLINEAR_SOURCES)
|
|
# First create BLAS library if we have BLAS sources
|
|
if(BLAS_SOURCES)
|
|
add_library(blas_functions STATIC ${BLAS_SOURCES})
|
|
target_include_directories(blas_functions
|
|
PUBLIC ${liblinear_SOURCE_DIR}/blas
|
|
)
|
|
|
|
# Set C language for BLAS
|
|
set_target_properties(blas_functions PROPERTIES
|
|
LINKER_LANGUAGE C
|
|
POSITION_INDEPENDENT_CODE ON
|
|
)
|
|
|
|
# Ensure symbols are exported
|
|
if(CMAKE_C_COMPILER_ID MATCHES "GNU|Clang")
|
|
target_compile_options(blas_functions PRIVATE -fvisibility=default)
|
|
endif()
|
|
endif()
|
|
|
|
# Create liblinear object library
|
|
add_library(liblinear_objects OBJECT ${LIBLINEAR_SOURCES})
|
|
|
|
# Set properties for the object library
|
|
target_include_directories(liblinear_objects
|
|
PUBLIC
|
|
${liblinear_SOURCE_DIR}
|
|
)
|
|
|
|
# Add blas directory if it exists
|
|
if(EXISTS "${liblinear_SOURCE_DIR}/blas")
|
|
target_include_directories(liblinear_objects
|
|
PUBLIC ${liblinear_SOURCE_DIR}/blas
|
|
)
|
|
endif()
|
|
|
|
# Set C++ standard for liblinear
|
|
target_compile_features(liblinear_objects PRIVATE cxx_std_17)
|
|
|
|
# Compiler specific flags
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|
target_compile_options(liblinear_objects PRIVATE -w) # Suppress warnings
|
|
endif()
|
|
|
|
# Make the include directories available to parent scope
|
|
set(LIBLINEAR_INCLUDE_DIR ${liblinear_SOURCE_DIR} PARENT_SCOPE)
|
|
if(EXISTS "${liblinear_SOURCE_DIR}/blas")
|
|
set(LIBLINEAR_BLAS_INCLUDE_DIR ${liblinear_SOURCE_DIR}/blas PARENT_SCOPE)
|
|
endif()
|
|
|
|
# Export BLAS library target to parent scope if it exists
|
|
if(TARGET blas_functions)
|
|
set(LIBLINEAR_BLAS_TARGET blas_functions PARENT_SCOPE)
|
|
endif()
|
|
|
|
message(STATUS "liblinear built with sources: ${LIBLINEAR_SOURCES}")
|
|
if(BLAS_SOURCES)
|
|
message(STATUS "BLAS functions built as separate static library")
|
|
endif()
|
|
else()
|
|
# Create minimal liblinear implementation as object library
|
|
message(WARNING "liblinear source files not found, creating minimal implementation")
|
|
|
|
# Create a minimal linear.cpp file
|
|
set(MINIMAL_LIBLINEAR_DIR "${CMAKE_CURRENT_BINARY_DIR}/minimal_liblinear")
|
|
file(MAKE_DIRECTORY ${MINIMAL_LIBLINEAR_DIR})
|
|
|
|
# Create minimal header
|
|
file(WRITE "${MINIMAL_LIBLINEAR_DIR}/linear.h"
|
|
"#pragma once\n"
|
|
"extern \"C\" {\n"
|
|
"struct feature_node { int index; double value; };\n"
|
|
"struct problem { int l, n; double *y; struct feature_node **x; double bias; };\n"
|
|
"struct parameter { int solver_type; double eps, C; };\n"
|
|
"struct model { struct parameter param; int nr_class, nr_feature; double *w; int *label; double bias; };\n"
|
|
"struct model* train(const struct problem *prob, const struct parameter *param);\n"
|
|
"double predict(const struct model *model_, const struct feature_node *x);\n"
|
|
"void free_and_destroy_model(struct model **model_ptr_ptr);\n"
|
|
"}\n"
|
|
)
|
|
|
|
# Create minimal implementation
|
|
file(WRITE "${MINIMAL_LIBLINEAR_DIR}/linear.cpp"
|
|
"#include \"linear.h\"\n"
|
|
"#include <cstdlib>\n"
|
|
"#include <cstring>\n"
|
|
"struct model* train(const struct problem *prob, const struct parameter *param) {\n"
|
|
" auto model = (struct model*)malloc(sizeof(struct model));\n"
|
|
" memset(model, 0, sizeof(struct model));\n"
|
|
" model->nr_feature = prob->n;\n"
|
|
" model->nr_class = 2;\n"
|
|
" model->w = (double*)calloc(prob->n, sizeof(double));\n"
|
|
" return model;\n"
|
|
"}\n"
|
|
"double predict(const struct model *model_, const struct feature_node *x) {\n"
|
|
" return 1.0; // Dummy prediction\n"
|
|
"}\n"
|
|
"void free_and_destroy_model(struct model **model_ptr_ptr) {\n"
|
|
" if (*model_ptr_ptr) {\n"
|
|
" free((*model_ptr_ptr)->w);\n"
|
|
" free(*model_ptr_ptr);\n"
|
|
" *model_ptr_ptr = nullptr;\n"
|
|
" }\n"
|
|
"}\n"
|
|
)
|
|
|
|
add_library(liblinear_objects OBJECT "${MINIMAL_LIBLINEAR_DIR}/linear.cpp")
|
|
target_include_directories(liblinear_objects PUBLIC ${MINIMAL_LIBLINEAR_DIR})
|
|
target_compile_features(liblinear_objects PRIVATE cxx_std_17)
|
|
|
|
set(LIBLINEAR_INCLUDE_DIR ${MINIMAL_LIBLINEAR_DIR} PARENT_SCOPE)
|
|
endif()
|
|
|
|
# Print summary
|
|
message(STATUS "External libraries configured:")
|
|
message(STATUS " - libsvm: ${libsvm_SOURCE_DIR}")
|
|
message(STATUS " - liblinear: ${liblinear_SOURCE_DIR}")
|
|
if(TARGET blas_functions)
|
|
message(STATUS " - BLAS functions built as separate static library")
|
|
endif()
|
|
message(STATUS " - Using combination of OBJECT and STATIC libraries")
|