The Linux Page

Failing cmake find_library(), returning just a path instead of the path and library name

Forest Trail

Introduction

The CMake search algorithms are fairely simple, but it feels like each time I try to use them I make a small mistake and something goes wrong.

First there are three very important variables that are used in the search. Note that there are many other variables that can be used in the search which are listed on this page. But 99.9% of the time, these three are enough:

  1. CMAKE_MODULE_PATH

    This variable defines where to search for modules, but only if the prefix matches. Without a matching prefix, it somehow gets ignored.
     
  2. CMAKE_PREFIX_PATH

    This variable is actually the one which makes things really work well. In most cases, once I set that one path, that's when things start working as expected.
     
  3. CMAKE_INSTALL_PREFIX

    This is where I install files while working. That is, instead of trying to install under /usr/include or /usr/local/include or some other magical place, I like to have a distribution folder in under my own /home/alexis folder. That way I can at least partially verify that a project installs its header files (i.e. the next project that depends on it will fail a #include if the file was not installed in the distribution folder.

Now, let's see what happened to me with the find_library() on that day...

Find Library

Today I spent hours trying to find out why one of my find_library() would fail.

I fixed the search implementation, to make it cleaner as a result, which is not a bad thing, but the problem was a -D on a cmake call that would define the value ahead of time. That is...

There is the search I used:

find_library(
    ADVGETOPT_LIBRARY
        advgetopt

    PATHS
        ${ADVGETOPT_LIBRARY_DIR}
        ENV ADVGETOPT_LIBRARY
)

IMPORTANT NOTES:

  • In this case, the find_library() sets the result of the search in the variable named ADVGETOPT_LIBRARY
  • The "advgetopt" is the name of the library being searched
  • The PATHS option is used after many other standard cmake searches (THIS IS QUITE IMPORTANT)
  • The ${ADVGETOPT_LIBRARY_DIR} is a variable defined with -D command line option or a set() command within your cmake files
  • The ENV ADVGETOPT_LIBRARY is a variable defined in your shell environment (maybe: export ADVGETOPT_LIBRARY=/some/path/here)
    • You may also write $ENV{ADVGETOPT_LIBRARY} in which case it replaces the value instead of using the ENV extension
  • Using HINTS instead of PATHS changes the order in which the paths are checked; HINTS are checked early, PATHS are checked later

This looks just fine and it should work, easy. I even added a test in my main CMakeLists.txt to see what would happen:

find_library(
    TEST_LIBRARY
        advgetopt

    PATHS
        ENV ADVGETOPT_LIBRARY
)
message(STATUS "TEST library = [${TEST_LIBRARY}]")

And sure enough, I would get the correct path and filename in my TEST_LIBRARY variable.

So why would the other one fail so badly?!

At some point I thought, I should re-read the documentation, just in case (you know, after hours of research!) and, while reading, something caught my eye:

Once one of the calls succeeds the result variable will be set and stored in the cache so that no call will search again.

There we go. If the variable is already defined, the call does nothing since the system is considered happy. In other words, if you already defined that parameter, then it won't be necessary to re-define it.

I have a master CMakeLists.txt which calls each sub-directory and has a set of -D definitions that look like this:

ConfigureMakeProject( PROJECT_NAME ve
    COMPONENT non-free
    CONFIG_ARGS
        -DCMAKE_PREFIX_PATH:PATH="${SNAP_DIST_PATH}"
        -DCMAKE_INSTALL_PREFIX:PATH="${SNAP_DIST_PATH}"
        -DADVGETOPT_INCLUDE_DIR:PATH="${SNAP_INCLUDE_DIR}"
        -DADVGETOPT_LIBRARY:PATH="${SNAP_LIBRARY_DIR}"
        ...snip...

Here I show you the one with the incorrect -D. See it?

Yes. I do:

-DADVGETOPT_LIBRARY:PATH="<some path>"

Therefore, the cmake variable ADVGETOPT_LIBRARY is defined by that one line of code. This is added to the cmake cache and as mentioned above, once in the cache, the find_...() commands do nothing.

Once I thought I was on the right track, I tested the variable before calling the find_library() and could see that the path was already defined in that variable.

message(STATUS "The ADVGETOPT_LIBRARY var = [${ADVGETOPT_LIBRARY}]")

This message() command would print the path to the library folder, kind of like more or less expected, and  that was definitely wrong.

I'm not 100% sure how I will fix the final version, but for now I added _DIR to the name in the -D definition.

-DADVGETOPT_LIBRARY_DIR:PATH="${SNAP_LIBRARY_DIR}"

And everything started to work as expected.

If you're a beginner and want to learn moreabout cmake, you may want to look into getting a book: Mastering CMake. (Note that I'm an Amazon affiliate and usually make a commission when you make such a purchase which makes me happy and allows me to support my website here.)