Search code examples
c++cmakestatic-linking

How to link to a library in a non-standard location with CMake?


I have some libs that are in non-standard locations. This is from OpenFrameworks and the libs are inside:

/Users/me/packages/builds/x86_64/of-0.9.3-osx-release/libs/openFrameworksCompiled/lib/osx/openFrameworks.a
...
/Users/me/packages/builds/x86_64/of-0.9.3-osx-release/libs/boost/lib/osx/boost_system.a
...
etc.

When linking those in my CMake, I am using the following approach:

set(LIB_OF ${OF_DIRECTORY}/libs/openFrameworksCompiled/lib/osx/openFrameworks.a)
...
set(LIB_BOOST_MAIN ${OF_DIRECTORY}/libs/boost/lib/osx/boost.a)
...
set(LIB_CAIRO1 ${OF_DIRECTORY}/libs/cairo/lib/osx/cairo-script-interpreter.a)
...
set(LIB_FREETYPE ${OF_DIRECTORY}/libs/freetype/lib/osx/freetype.a)
...

Then I merge those variables into one big variable:

set(OF_CORE_LIBS
    ${LIB_OF}
    ...
    ${LIB_BOOST_MAIN}
    ...
    ${LIB_CAIRO1}
    ...
    ${LIB_FREETYPE}
    ...
)

So eventually all the libs are concatenated and passed into the linker:

link_directories (${OF_CORE_LIBS})

Then I use:

target_link_libraries(${APP_NAME} ${OF_CORE_LIBS})

to link them.

However, I am getting the following warning during the linking stage:

Linking CXX executable ../bin/3DPrimitiveExample
ld: warning: -L path '/Users/me/packages/builds/x86_64/of-0.9.3-osx-release/libs/openFrameworksCompiled/lib/osx/openFrameworks.a' is not a directory
...
(All the libs are listed in this way)

How can I tell CMake to look into those directories, even if they are not called blah/lib but blah/lib/osx? I could change the structure of the package but then I do not want to modify the way it has been designed.

I know the answer has something to do with the PROPERTIES or the default CMAKE variables but the help is still a bit cryptic for me. I am guessing it is the set_target_properties() but not sure about the syntax. (I am on cmake 2.8.12)

UPDATE(1): So I have changed my approach in the following way:

set(LIB_OF ${OF_DIRECTORY}/libs/openFrameworksCompiled/lib/)
set(LIB_FREEIMAGE ${OF_DIRECTORY}/libs/FreeImage/lib/)
set(LIB_BOOST ${OF_DIRECTORY}/libs/boost/lib/)
set(LIB_CAIRO ${OF_DIRECTORY}/libs/cairo/lib/)
set(LIB_FMODEX ${OF_DIRECTORY}/libs/fmodex/lib/)
set(LIB_FREETYPE ${OF_DIRECTORY}/libs/freetype/lib/)
set(LIB_GLEW ${OF_DIRECTORY}/libs/glew/lib/)
set(LIB_OPENSSL ${OF_DIRECTORY}/libs/openssl/lib/)
set(LIB_POCO ${OF_DIRECTORY}/libs/poco/lib/)
set(LIB_RTAUDIO ${OF_DIRECTORY}/libs/rtAudio/lib/)
set(LIB_TESS ${OF_DIRECTORY}/libs/tess2/lib/)

Once those location variables are created, they are merged once again:

set(OF_CORE_LIBS
    ${LIB_OF}
    ${LIB_FREEIMAGE}
    ${LIB_BOOST}
    ${LIB_CAIRO}
    ${LIB_FMODEX}
    ${LIB_FREETYPE}
    ${LIB_GLEW}
    ${LIB_GLFW} 
    ${LIB_OPENSS}
    ${LIB_POCO}
    ${LIB_RTAUDIO}
    ${LIB_TESS}
)

So when I verbose it, I get the following:

OF_CORE_LIBS: /Users/me/packages/builds/x86_64/of-0.9.3-osx-release/libs/openFrameworksCompiled/lib/;/Users/me/packages/builds/x86_64/of- 0.9.3-osx-release/libs/FreeImage/lib/;/Users/me/packages/builds/x86_64/of-0.9.3-osx-release/libs/boost/lib/; (etc)

Then I pass those folders to the linker:

link_directories (${OF_CORE_LIBS})

Now the part that is wrong is this:

target_link_libraries(${APP_NAME} ${OF_CORE_LIBS})

Clearly, I cannot pass the folders directly here? I need lib names. Do I need another list with the lib names at this stage?

UPDATE(2):

For the lib search folders:

set(OF_CORE_LIB_DIRS
    ${LIB_OF}
    ${LIB_FREEIMAGE}
    ${LIB_BOOST}
    ${LIB_CAIRO}
    ${LIB_FMODEX}
    ${LIB_FREETYPE}
    ${LIB_GLEW}
    ${LIB_GLFW} 
    ${LIB_OPENSS}
    ${LIB_POCO}
    ${LIB_RTAUDIO}
    ${LIB_TESS}
)

For the actual libs:

set(OF_CORE_LIBS
    openFrameworks
    freeimage
    boost_system
    boost_filesyste
    freeimage
    cairo-script-interpreter
    cairo
    pixman-1
    fmodex
    freetype
    glew
    ssl
    crypto
    PocoCrypto
    PocoData
    PocoDataSQLite
    PocoJSON
    PocoMongoDB
    PocoXML
    PocoNet
    PocoNetSSL
    PocoUtil
    PocoZip
    PocoFoundation
    rtAudio
    tess2
)

And at linking stage:

link_directories (${OF_CORE_LIB_DIRS})
add_executable(${APP_NAME} ${SOURCE_FILES})
target_link_libraries(${APP_NAME} ${OF_CORE_LIBS})

Now I am getting the following error:

Linking CXX executable ../bin/3DPrimitiveExample.app/Contents/MacOS/3DPrimitiveExample
ld: library not found for -lopenFrameworks

This is the directory bit:

set(LIB_OF ${OF_DIRECTORY}/libs/openFrameworksCompiled/lib/osx)

And the file is in there:

openFrameworks.a

Further Questions:

The answer below helped me to understand the difference between lib folders and the actual lib names. However, there is still one question answered: How to tell CMake to accept and link arbitrary lib names?

In the question above, the libs are called just with their names, for example, openFrameworks.a should be called libopenFrameworks.a in order to be picked up by the linker, but it is not. That, I shall ask under a different question.


Solution

  • So eventually all the libs are concatenated and passed into the linker:

    link_directories (${OF_CORE_LIBS})
    

    No they're not. From the manual

    link_directories

    Specify directories in which the linker will look for libraries.

    link_directories(directory1 directory2 ...)

    Specify the paths in which the linker should search for libraries.

    So you've specified the directories in which the linker should search for the libraries you want to link. Now you want to specify that the linker should link a library, say, libfoo.a with your_executable.

    Again, the manual:

    target_link_libraries(your_executable foo)
    

    You tell the linker where to look and also what to look for.

    Later

    I think I am doing exactly what you have described in your answer, right?

    No, you're not.

    You are giving a list of files (apparently they are static libraries, although they lack the conventional lib prefix) to link_directories. It requires a list of directories in which the linker is to look for the libraries that you specify with target_link_libraries.

    That is why the linker says:

    ld: warning: -L path '/Users/me/packages/builds/x86_64/of-0.9.3-osx-release/\
    libs/openFrameworksCompiled/lib/osx/openFrameworks.a' is not a directory
    

    It is a file. Correct this and then:

    target_link_libraries(your_executable foo)
    

    will cause the linker to look for a library libfoo.{so|a} in each of the link_directories.