2
votes

I have a cmake project which consists of my own static library and executable. The simplified project structure is:

Top level cmake:

cmake_minimum_required(VERSION 3.16)

project(mainproject 
    VERSION 0.0.1 
    DESCRIPTION ""
    LANGUAGES CXX
)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")

#include libs
include(LibsPath)

add_subdirectory(teststaticlib)
add_subdirectory(testexe)

cmake/LibsPath.cmake:

        set(CMAKE_PREFIX_PATH
            "C:/tesseract41_x64-static/leptonica_x64-windows-static"
            "C:/tesseract41_x64-static/tiff_x64-windows-static"
            "C:/tesseract41_x64-static/tesseract_x64-windows-static"
            "C:/tesseract41_x64-static/libpng_x64-windows-static"
            "C:/tesseract41_x64-static/libjpeg-turbo_x64-windows-static"
            "C:/tesseract41_x64-static/giflib_x64-windows-static"
            "C:/tesseract41_x64-static/libwebp_x64-windows-static"
            "C:/opencv4_x64-windows-static"
            "C:/protobuf_x64-windows-static"
            "C:/hdf5_x64-windows-static"
            "C:/szip_x64-windows-static"
    )

Basically the path for project direct dependencies - tesseract and opencv and their dependencies.

Cmake file for static library(teststaticlib/CMakeLists.txt):

cmake_minimum_required(VERSION 3.16)

project(teststaticlib)

set(CMAKE_GENERATOR "Ninja")
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake")

#enable unicode
add_definitions(-DUNICODE -D_UNICODE)

set(SOURCE_FILES
    #...source files
)
set(PRIVATE_HEADER_FILES 
    #... header files
)
set(PUBLIC_HEADER_FILES
    #... header files
)
add_library(teststaticlib STATIC ${SOURCE_FILES} ${PRIVATE_HEADER_FILES} ${PUBLIC_HEADER_FILES})
add_library(teststaticlib::teststaticlib ALIAS teststaticlib)
set_target_properties(teststaticlib PROPERTIES OUTPUT_NAME teststaticlib)
target_include_directories(teststaticlib 
    PRIVATE 
        ${CMAKE_CURRENT_SOURCE_DIR}/src
    PUBLIC
        ${CMAKE_CURRENT_SOURCE_DIR}/include
)

#precompiled headers
target_precompile_headers(teststaticlib PRIVATE src/pch.h)

#link libs
find_package(tesseract CONFIG REQUIRED)
find_package(opencv CONFIG REQUIRED core imgproc highgui)

include_directories(${Tesseract_INCLUDE_DIRS})
include_directories(${OpenCV_INCLUDE_DIRS})
target_link_libraries(teststaticlib 
    PRIVATE 
        ${Tesseract_LIBRARIES}
        ${OpenCV_LIBS}
)

So far everything is working great. teststaticlib.lib is building fine, without any errors. But as soon as I add any executable to the project and link teststaticlib to it:

testexe/CMakeLists.txt

cmake_minimum_required(VERSION 3.16)

project(testexe)

set(SOURCE_FILES
    main.cpp
)
add_executable(testexe ${SOURCE_FILES})

target_link_libraries(testexe 
    PRIVATE 
        teststaticlib::teststaticlib
)

I get this error when building:

ninja: error: 'C:/tesseract41_x64-static/leptonica_x64-windows-static/debug/lib/gif.lib', needed by 'debug/mainproject/testexe.exe', missing and no known rule to make it

Why do I get this error and how do I fix it? Why is cmake looking for my direct dependencies' sub-dependencies (gif in this case is a dependency of either tesseract of leptonica) inside leptonica folder when their path is different as you can see from LibsPath.cmake? I know that this is not tess or leptonica specific problem, because if I change those libraries to any other, the pattern remains and I still get the same error but with other libs. So there must be something wrong in my cmake files, but I can't figure where the error is.

Edit: Tried to change my library linking to PUBLIC and adding same dependencies to exe. Even tries linking sub-dependency gif to exe - still get the same error.

teststaticlib/CMakeLists.txt:

target_link_libraries(teststaticlib 
PUBLIC 
    ${Tesseract_LIBRARIES}
    ${OpenCV_LIBS}
)

testexe/CMakeLists.txt

    cmake_minimum_required(VERSION 3.16) 
project(testexe)

set(SOURCE_FILES
    main.cpp
)
add_executable(testexe ${SOURCE_FILES})

    find_package(tesseract CONFIG REQUIRED)
    find_package(opencv CONFIG REQUIRED core imgproc highgui)
    find_package(gif REQUIRED)
    include_directories(${Tesseract_INCLUDE_DIRS})
    include_directories(${OpenCV_INCLUDE_DIRS})
    include_directories(${GIF_INCLUDE_DIRS})

target_link_libraries(testexe 
    PRIVATE 
        ${GIF_LIBRARIES}
        ${Tesseract_LIBRARIES}
        ${OpenCV_LIBS}
        basicemul::basicemul
)
1
Because you are building a static library, it doesn't "link" the dependencies. You need to list them explicitly for the executable as well, if the executable needs to use them. One fix may be as simple as changing your target_link_libraries(teststaticlib PRIVATE ${Tesseract_LIBRARIES} ${OpenCV_LIBS}) call to PUBLIC. - squareskittles
@squareskittles I tried that, didn't help. Edited the main post. Also my executable in this case is a simple main.cpp file with empty main function, doesn't use any functions from teststaticlib. - estw272
The error itself says this library is "missing", did you check to see if C:/tesseract41_x64-static/leptonica_x64-windows-static/debug/lib/gif.lib actually exists? - squareskittles
@squareskittles I did, and no it doesn't exist. My question is why is cmake looking for gif at that specific path when the path for GIF lib that is added to CMAKE_PREFIX_PATH is "C:/tesseract41_x64-static/giflib_x64-windows-static". And why is it looking for GIF lib there only when linking teststaticlib to exe and not when building teststaticlib itself. - estw272
@estw272 Seems like you have found the problem. Your Cmake files are fine, It's normal and recommended that debug and release use different linking, and that debug build try to find corresponding debug library. Either install the debug version of the lib, or manually hack the cmake files that come with the lib. Also, personally I prefer setting Tesseract_DIR directly rather than using CMAKE_PREFIX_PATH. You can also create Tesseract_DIR as system environment variable. - X. Sun

1 Answers

3
votes

Following might be helpful, Quote from "mastering cmake by kitware":

CMAKE_PREFIX_PATH: This specifies a path that will be used by the FIND_XXX() commands. It contains the "base" directories for the FIND_XXX() commands to append appropriate sub-directories to. FIND_PROGRAM() adds /bin to each of the directories in the path; FIND_LIBRARY() appends /lib to each of the directories in the path

Because of this I generally simply make an library import script of some kind and use PATHS/HINTS to find_library() command to point into the correct directory. To my best of knowledge most robust way to import library targets is to not use find_library() as-is but use cmake INSTALL() command generated import scripts from that library if available. See find_library() docs.

Also linking static libs is bit weird. They get compiled but nothing actually gets linked to them until final binary is produced. PRIVATE libs are meant that the linked symbols are sealed inside the static lib and the dependency won't be carried over any further. PUBLIC then simply adds the lib to LINK_INTERFACE_LIBRARIES/LINK_LIBRARIES target property and the dependencies are carried over until final binary is produced. (shared lib or executable)

Note that I have *nix background and I have might gotten few details wrong if building on windows.