Skip to content

Fix Windows build errors when using GGML_BACKEND_DL=1 (LNK2019/CMake errors) #8

@cynodesmus

Description

@cynodesmus

I just learned about your project yesterday, but I ran into problems compiling it in Windows.
Ultimately, the project compiled successfully.
I hope this solution is useful.

Description:
Currently, the project fails to compile on Windows when trying to build a portable version with dynamic backend loading (-DGGML_BACKEND_DL=1). There are two main issues:
CMake Error: target_link_libraries fails because it tries to link MODULE_LIBRARY targets (like ggml-cuda) directly.
Linker Error (LNK2019): ggml_backend_cpu_set_n_threads is not exported by ggml.dll in DL mode, causing unresolved external symbols.
Proposed Fixes:

  1. CMakeLists.txt modification:
    Update the link_ggml_backends macro to check the target type before linking. Only link if it's not a MODULE_LIBRARY:
# Shared compile options and ggml linkage
macro(link_ggml_backends target)
    target_include_directories(${target} PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/src
    )
    target_include_directories(${target} SYSTEM PRIVATE
        ${CMAKE_CURRENT_SOURCE_DIR}/ggml/include
    )
    if(MSVC)
        target_compile_options(${target} PRIVATE /W4 /wd4100 /wd4505)
    else()
        target_compile_options(${target} PRIVATE -Wall -Wextra -Wshadow -Wconversion
                              -Wno-unused-parameter -Wno-unused-function -Wno-sign-conversion)
    endif()
    target_link_libraries(${target} PRIVATE ggml)
    if(TARGET ggml-base)
        target_link_libraries(${target} PRIVATE ggml-base)
    endif()
    foreach(backend blas cuda metal vulkan)
        if(TARGET ggml-${backend})
            get_target_property(CURRENT_BACKEND_TYPE ggml-${backend} TYPE)
            if (NOT CURRENT_BACKEND_TYPE STREQUAL "MODULE_LIBRARY")
                 target_link_libraries(${target} PRIVATE ggml-${backend})
            endif()
            string(TOUPPER ${backend} BACKEND_UPPER)
            target_compile_definitions(${target} PRIVATE ACESTEP_HAVE_${BACKEND_UPPER})
            if(backend STREQUAL "cuda")
                find_package(CUDAToolkit QUIET)
                if(CUDAToolkit_FOUND)
                    target_link_libraries(${target} PRIVATE CUDA::cudart)
                endif()
            endif()
        endif()
    endforeach()
endmacro()
  1. backend.h modification:
    To support CPU thread management in DL mode without linking errors, replace direct ggml_backend_cpu_set_n_threads calls with the Registry/Device API:
// Remove #include "ggml-cpu.h"
// Use this inside backend_init:
    int n_threads = (int)std::thread::hardware_concurrency() / 2;
    if (n_threads < 1) n_threads = 1;
    // [GGML] If best backend is already CPU, reuse it (avoid 2 CPU instances
    // where only one gets the thread count)
    char params[64];
    snprintf(params, sizeof(params), "n_threads=%d", n_threads);

    bool best_is_cpu = (strcmp(ggml_backend_name(bp.backend), "CPU") == 0);
    if (best_is_cpu) {
        bp.cpu_backend = bp.backend;
    } else {
        ggml_backend_dev_t cpu_dev = ggml_backend_dev_by_type(GGML_BACKEND_DEVICE_TYPE_CPU);
        if (cpu_dev) {
            bp.cpu_backend = ggml_backend_dev_init(cpu_dev, params);
        }
        
        if (!bp.cpu_backend) {
            bp.cpu_backend = ggml_backend_init_by_type(GGML_BACKEND_DEVICE_TYPE_CPU, NULL);
        }

        if (!bp.cpu_backend) {
            fprintf(stderr, "[Load] FATAL: failed to init CPU backend\n");
            exit(1);
        }
    }

This approach allows distributing a single set of binaries that dynamically load CUDA, Vulkan, or specific CPU variants (AVX2/AVX512) on the user's machine.

Note: These changes were developed using Gemini and verified through extensive local testing on Windows (MSVC 19.44, CUDA 12.4). The resulting binaries are fully portable and correctly initialize the CUDA/Vulkan/CPU backends at runtime.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions