diff --git a/debian/control b/debian/control index bd5f5ea0..365ffe3b 100644 --- a/debian/control +++ b/debian/control @@ -20,7 +20,6 @@ Build-Depends: debhelper-compat (= 13), libswscale-dev, libxrt-dev, ninja-build, - patchelf, pkg-config, rustc, uuid-dev diff --git a/debian/rules b/debian/rules old mode 100644 new mode 100755 index f93b7dad..574d2557 --- a/debian/rules +++ b/debian/rules @@ -18,26 +18,10 @@ override_dh_auto_build: cmake --build build override_dh_auto_install: - install -d debian/fastflowlm/opt/fastflowlm debian/fastflowlm/usr/bin DESTDIR=$(CURDIR)/debian/fastflowlm cmake --install build --prefix=/opt/fastflowlm - mv debian/fastflowlm/opt/fastflowlm/bin/flm debian/fastflowlm/opt/fastflowlm/flm rmdir --ignore-fail-on-non-empty debian/fastflowlm/opt/fastflowlm/bin - ln -sf /opt/fastflowlm/flm debian/fastflowlm/usr/bin/flm - - # Install model list - install -m 644 src/model_list.json debian/fastflowlm/opt/fastflowlm/model_list.json - - # Install xclbins - cp -r src/xclbins debian/fastflowlm/opt/fastflowlm/ - - # Remove include and lib directories - rm -rf debian/fastflowlm/opt/fastflowlm/include debian/fastflowlm/opt/fastflowlm/lib + install -d debian/fastflowlm/opt/fastflowlm debian/fastflowlm/usr/bin + ln -sf /opt/fastflowlm/bin/flm debian/fastflowlm/usr/bin/flm - # Install custom libraries and update their RPATH - install -m 644 src/lib/*.so debian/fastflowlm/opt/fastflowlm/ - patchelf --set-rpath '/opt/fastflowlm' debian/fastflowlm/opt/fastflowlm/flm - for lib in debian/fastflowlm/opt/fastflowlm/*.so; do \ - if [ -f "$$lib" ]; then \ - patchelf --set-rpath '/opt/fastflowlm' "$$lib"; \ - fi; \ - done + # Remove include directories + rm -rf debian/fastflowlm/opt/fastflowlm/include diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f81d9a1f..9b943bb6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -119,6 +119,7 @@ endif() target_compile_definitions(flm PUBLIC DISABLE_ABI_CHECK=1 + CMAKE_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}" ) if(WIN32) @@ -306,12 +307,29 @@ if(WIN32) COMMENT "Checking for remaining DLL dependencies" DEPENDS flm ) +else() + file(GLOB so_libs "${CMAKE_SOURCE_DIR}/lib/*.so") + install(FILES ${so_libs} DESTINATION lib) + set_target_properties(flm PROPERTIES + INSTALL_RPATH "$ORIGIN/../lib") endif() - -# ——————————————————————————————————————————————— -# Install the flm binary (for proper deployment) -# ——————————————————————————————————————————————— install(TARGETS flm RUNTIME DESTINATION bin BUNDLE DESTINATION . -) \ No newline at end of file +) + +install(FILES model_list.json DESTINATION share/flm) + +# xclbins, which are loaded by shared libraries need to be in location +# relative to the executable, so we install them to bin/xclbins +install(DIRECTORY xclbins DESTINATION bin) + +if(NOT WIN32 AND NOT CMAKE_INSTALL_PREFIX STREQUAL "/usr" AND NOT CMAKE_INSTALL_PREFIX STREQUAL "/usr/local") + install(CODE " + message(STATUS \"Creating symlink for flm\") + file(MAKE_DIRECTORY \"$ENV{DESTDIR}/usr/local/bin\") + execute_process(COMMAND \"${CMAKE_COMMAND}\" -E create_symlink + \"${CMAKE_INSTALL_PREFIX}/bin/flm\" + \"$ENV{DESTDIR}/usr/local/bin/flm\")" + ) +endif() diff --git a/src/Makefile b/src/Makefile index 0116722a..8c3d4796 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,7 +3,7 @@ OUT_DIR := out PWSH := powershell.exe -.PHONY: all clean run serve +.PHONY: all clean run serve install ifeq ($(OS),Windows_NT) @@ -29,26 +29,34 @@ clean: @$(PWSH) "Remove-Item -Recurse -Force '$(BUILD_DIR)'" @$(PWSH) "Remove-Item -Recurse -Force '$(OUT_DIR)'" +install: + @echo "Installing using CMake..." + @cd $(BUILD_DIR) && cmake --install . --prefix $(PREFIX) + else all: @mkdir -p $(BUILD_DIR) @mkdir -p $(OUT_DIR) - @cd $(BUILD_DIR) && ${PWSH} -Command "cmake .." - @cd $(BUILD_DIR) && ${PWSH} -Command "cmake --build . --config Release" + @cd $(BUILD_DIR) && cmake .. + @cd $(BUILD_DIR) && cmake --build . --config Release run: - @cp lib/*.dll $(OUT_DIR) + @cp lib/*.so $(OUT_DIR) @cp model_list.json $(OUT_DIR) - @cd $(OUT_DIR) && ${PWSH} -Command "./flm.exe run gpt-oss -a 0" + @cd $(OUT_DIR) && ./flm run gpt-oss -a 0 serve: - @cp lib/*.dll $(OUT_DIR) + @cp lib/*.so $(OUT_DIR) @cp model_list.json $(OUT_DIR) - @cd $(OUT_DIR) && ${PWSH} -Command "./flm.exe serve -a 1 -e 1" + @cd $(OUT_DIR) && ./flm serve -a 1 -e 1 clean: @rm -rf $(BUILD_DIR) @rm -rf $(OUT_DIR) +install: + @echo "Installing using CMake..." + @cd $(BUILD_DIR) && cmake --install . --prefix $(PREFIX) + endif diff --git a/src/common/utils.cpp b/src/common/utils.cpp new file mode 100644 index 00000000..2380d76e --- /dev/null +++ b/src/common/utils.cpp @@ -0,0 +1,163 @@ +/// \file utils.cpp +/// \brief utils class +/// \author FastFlowLM Team +/// \date 2025-06-24 +/// \version 0.9.24 +/// +/// \note This file contains some utility functions for the FastFlowLM project. +#include "utils/utils.hpp" +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + +namespace utils { + +std::string find_model_list() { + std::string install_prefix = CMAKE_INSTALL_PREFIX; + + // 1. Check FLM_CONFIG_PATH environment variable + const char* env_path = std::getenv("FLM_CONFIG_PATH"); + if (env_path && *env_path) { + if (std::filesystem::exists(env_path)) { + return env_path; + } + } + + // 2. Check for install prefix + std::string installed_path = install_prefix + "/share/flm/model_list.json"; + if (std::filesystem::exists(installed_path)) { + return installed_path; + } + + // 3. Check relative to executable + std::string exe_dir = get_executable_directory(); + std::string exe_relative_path = exe_dir + "/model_list.json"; + if (std::filesystem::exists(exe_relative_path)) { + return exe_relative_path; + } + + // 4. Check current working directory + if (std::filesystem::exists("model_list.json")) { + return "model_list.json"; + } + + // If not found, throw an error + throw std::runtime_error("model_list.json not found. Please set FLM_CONFIG_PATH or place it next to the executable."); +} + +std::string get_executable_directory() { +#ifdef _WIN32 + char buffer[MAX_PATH]; + GetModuleFileNameA(NULL, buffer, MAX_PATH); + std::string exe_path(buffer); + size_t last_slash = exe_path.find_last_of("/"); + if (last_slash != std::string::npos) { + return exe_path.substr(0, last_slash); + } + return "."; +#else + char buffer[PATH_MAX] = {0}; + ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); + if (len > 0) { + buffer[len] = '\0'; + std::string exe_path(buffer); + size_t last_slash = exe_path.find_last_of("/"); + if (last_slash != std::string::npos) { + return exe_path.substr(0, last_slash); + } + } + return "."; +#endif +} + +std::string get_user_documents_directory() { +#ifdef _WIN32 + char buffer[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, 0, buffer))) { + return std::string(buffer); + } + // Fallback to current directory if Documents folder cannot be found + return "."; +#else + const char* home = std::getenv("HOME"); + if (home && *home) { + return std::string(home) + "/.config"; + } + return "."; +#endif +} + +int get_server_port(int user_port) { + if (user_port > 0 && user_port <= 65535) { + return user_port; + } + else { +#ifdef _WIN32 + char* port_env = nullptr; + size_t len = 0; + if (_dupenv_s(&port_env, &len, "FLM_SERVE_PORT") == 0 && port_env != nullptr) { + try { + int port = std::stoi(port_env); + free(port_env); + if (port > 0 && port <= 65535) { + return port; + } + } + catch (const std::exception&) { + free(port_env); + // Invalid port number, use default + } + } +#else + const char* port_env = std::getenv("FLM_SERVE_PORT"); + if (port_env && *port_env) { + try { + int port = std::stoi(port_env); + if (port > 0 && port <= 65535) { + return port; + } + } + catch (const std::exception&) { + // Invalid port number, use default + } + } +#endif + } + + return 52625; // Default port +} + +std::string get_models_directory() { +#ifdef _WIN32 + char* model_path_env = nullptr; + size_t len = 0; + if (_dupenv_s(&model_path_env, &len, "FLM_MODEL_PATH") == 0 && model_path_env != nullptr) { + std::string custom_path(model_path_env); + free(model_path_env); + if (!custom_path.empty()) { + return custom_path; + } + } +#else + const char* model_path_env = std::getenv("FLM_MODEL_PATH"); + if (model_path_env && *model_path_env) { + return std::string(model_path_env); + } +#endif + // Fallback to Documents/flm/models on Windows or ~/.config/flm on Linux if environment variable is not set + std::string documents_dir = get_user_documents_directory(); +#ifdef _WIN32 + return documents_dir + "/flm/models"; +#else + return documents_dir + "/flm"; +#endif +} + +} // end of namespace utils diff --git a/src/include/utils/utils.hpp b/src/include/utils/utils.hpp index e53da3b4..ca791045 100644 --- a/src/include/utils/utils.hpp +++ b/src/include/utils/utils.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #ifdef _WIN32 #include #include @@ -342,49 +343,11 @@ inline bool check_file_exists(std::string name) { /// \brief get the user's Documents directory on Windows or config directory on Linux /// \return the user's Documents directory path on Windows, ~/.config on Linux -inline std::string get_user_documents_directory() { -#ifdef _WIN32 - char buffer[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, 0, buffer))) { - return std::string(buffer); - } - // Fallback to current directory if Documents folder cannot be found - return "."; -#else - const char* home = std::getenv("HOME"); - if (home && *home) { - return std::string(home) + "/.config"; - } - return "."; -#endif -} +std::string get_user_documents_directory(); ///@brief get_executable_directory gets the directory where the executable is located ///@return the executable directory path -inline std::string get_executable_directory() { -#ifdef _WIN32 - char buffer[MAX_PATH]; - GetModuleFileNameA(NULL, buffer, MAX_PATH); - std::string exe_path(buffer); - size_t last_slash = exe_path.find_last_of("/\\"); - if (last_slash != std::string::npos) { - return exe_path.substr(0, last_slash); - } - return "."; -#else - char buffer[PATH_MAX] = {0}; - ssize_t len = readlink("/proc/self/exe", buffer, sizeof(buffer) - 1); - if (len > 0) { - buffer[len] = '\0'; - std::string exe_path(buffer); - size_t last_slash = exe_path.find_last_of("/"); - if (last_slash != std::string::npos) { - return exe_path.substr(0, last_slash); - } - } - return "."; -#endif -} +std::string get_executable_directory(); template inline std::string path_join(fileName&&... args){ @@ -401,74 +364,16 @@ inline std::string path_join(fileName&&... args){ return path; } +///@brief find and return the path to model_list.json +///@return path to model_list.json +std::string find_model_list(); + ///@brief get_server_port gets the server port from environment variable FLM_SERVE_PORT ///@return the server port, default is 52625 if environment variable is not set -inline int get_server_port(int user_port) { - if (user_port > 0 && user_port <= 65535) { - return user_port; - } - else { -#ifdef _WIN32 - char* port_env = nullptr; - size_t len = 0; - if (_dupenv_s(&port_env, &len, "FLM_SERVE_PORT") == 0 && port_env != nullptr) { - try { - int port = std::stoi(port_env); - free(port_env); - if (port > 0 && port <= 65535) { - return port; - } - } - catch (const std::exception&) { - free(port_env); - // Invalid port number, use default - } - } -#else - const char* port_env = std::getenv("FLM_SERVE_PORT"); - if (port_env && *port_env) { - try { - int port = std::stoi(port_env); - if (port > 0 && port <= 65535) { - return port; - } - } - catch (const std::exception&) { - // Invalid port number, use default - } - } -#endif - } - - return 52625; // Default port -} +int get_server_port(int user_port); ///@brief get_models_directory gets the models directory from environment variable or defaults to Documents/flm/models on Windows or ~/.config/flm on Linux ///@return the models directory path -inline std::string get_models_directory() { -#ifdef _WIN32 - char* model_path_env = nullptr; - size_t len = 0; - if (_dupenv_s(&model_path_env, &len, "FLM_MODEL_PATH") == 0 && model_path_env != nullptr) { - std::string custom_path(model_path_env); - free(model_path_env); - if (!custom_path.empty()) { - return custom_path; - } - } -#else - const char* model_path_env = std::getenv("FLM_MODEL_PATH"); - if (model_path_env && *model_path_env) { - return std::string(model_path_env); - } -#endif - // Fallback to Documents/flm/models on Windows or ~/.config/flm on Linux if environment variable is not set - std::string documents_dir = get_user_documents_directory(); -#ifdef _WIN32 - return documents_dir + "/flm/models"; -#else - return documents_dir + "/flm"; -#endif -} +std::string get_models_directory(); } // end of namespace utils diff --git a/src/src/main.cpp b/src/src/main.cpp index 696a65e6..d1b83aed 100644 --- a/src/src/main.cpp +++ b/src/src/main.cpp @@ -240,7 +240,7 @@ int main(int argc, char* argv[]) { // Get the command, model tag, and force flag std::string exe_dir = utils::get_executable_directory(); - std::string config_path = exe_dir + "/model_list.json"; + std::string config_path = utils::find_model_list(); // Get the models directory from environment variable or default std::string models_dir = utils::get_models_directory();