5

I am learning to build a library using CMake. The code structure for building library is like below:

include:  
   Test.hpp       
   ITest.hpp     // interface
src:
   Test.cpp
   ITest.cpp

In CMakeLists.txt, the sentences I used to build library is :

file(GLOB SRC_LIST "src/iTest.cpp" "src/Test.cpp" "include/Test.hpp"
        "include/iTest.hpp"  "include/deadreckoning.hpp")
add_library(test SHARED ${SRC_LIST})
target_link_libraries( test ${OpenCV_LIBS})  // link opencv libs to libtest.so

Then I wrote another test file (main.cpp), copy and paste the library under the same directory, link library and call functions inside the library. This CMakeLists.txt is

cmake_minimum_required(VERSION 2.8)
project(myapp)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -pthread -O3 -Wall -ftree-vectorize -ffast-math -funroll-loops")

add_executable(myapp main.cpp)
target_link_libraries(myapp "/home/labUser/test_lib/libtest.so")

The main.cpp compiles and runs succussfully if I don't include the header files inside the library:

#include <iostream>
using namespace std;

int main(){
    cout << "hello world" << endl;
    return -1;
}

But when I include the header file #include "ITest.hpp", it has error:

fatal error: iTest.hpp: No such file or directory
  #include "iTest.hpp"   
compilation terminated.

I don't understand why it happens. I think I have already linked the library successfully because when I run main.cpp without including header file, it doesn't give any "linking" error. And I think apparently the header files are inside the library. Why I can't include it? Can anyone help me figure this out?

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
debug_all_the_time
  • 564
  • 1
  • 5
  • 18

1 Answers1

8

You have a couple of issues here.

Propagating headers to users of your target:

Whilst you've added the include file to your library target, you need to let consumers of your library target know how to find the header.

As such, when your app myapp links against your library target test, you need to tell cmake to add ./include to myapp's include search path.

There is a special cmake variable, ${CMAKE_CURRENT_LIST_DIR} which resolves to the path to the directory in which the current CMakeLists.txt being processed is.

In your instance, that is the parent folder of both src and include.

./                    <-- ${CMAKE_CURRENT_LIST_DIR} is this directory
+--- CMakeLists.txt
+--- src/
|    +---Test.cpp
|    +---ITest.cpp
+--- include/
     +---Test.hpp
     +---ITest.hpp

In order to tell cmake to add a path to its include search path you use target_include_directories

For this the path will then be ${CMAKE_CURRENT_LIST_DIR}/include

So the syntax you'd be looking for is:

target_include_directories(test PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)

Note that this means you don't have to add "include/iTest.hpp" and "include/Test.hpp" to your SRC_LIST glob, as the compiler will be able to find them from the above target_include_directories

Linking to your test library:

Now that you've created your library and added the include directories, to actually use it in your app, you should again use target_link_libraries, but don't specify the path to the generated .so file, instead refer to the name of the library target you created, test

target_link_libraries(myapp test)

Now myapp will know how to find Test.hpp because it will get that information from the "dependency link" you've created between myapp and test

As such, assuming the following directory structure, the following CMakeLists.txt files may work

src/
+--- library/
|    +--- < sources for your shared library >
+--- app/
     +--- < sources for your application >

src/CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project(myapp)

add_subdirectory(library)
add_subdirectory(app)

src/library/CMakeLists.txt

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} 
    -std=c++11 
    -pthread 
    -O3 
    -Wall 
    -ftree-vectorize 
    -ffast-math 
    -funroll-loops")

find_package(OpenCV REQUIRED)

add_library(test SHARED "src/iTest.cpp src/Test.cpp")
target_link_libraries(test ${OpenCV_LIBS})  // link opencv libs to libtest.so
target_include_directories(test PUBLIC ${CMAKE_CURRENT_LIST_DIR}/include)

src/app/CMakeLists.txt

add_executable(myapp main.cpp)
target_link_libraries(myapp test)
Burak
  • 2,251
  • 1
  • 16
  • 33
Steve Lorimer
  • 27,059
  • 17
  • 118
  • 213
  • 2
    Thanks for the answer! I tried to modify the library CMakeLists but I got the error when I compile: target_include_directories called with invalid arguments Do you know why? Do I need to specify where ${CMAKE_CURRENT_LIST_DIR} is? – debug_all_the_time Nov 01 '17 at 17:45
  • @KathyLee looks like you're using an old version of cmake. Are you able to upgrade to a newer version? (cmake 2.8 is very old) – Steve Lorimer Nov 01 '17 at 17:49
  • @KathyLee if you can't upgrade, try use `${CMAKE_CURRENT_SOURCE_DIR}` instead, I believe this variable exists in 2.8 – Steve Lorimer Nov 01 '17 at 17:51
  • @KathyLee for something this small you could just add `include_directories(include)` – afakih Nov 01 '17 at 18:02
  • 1
    @afakih that's not recommended, and for someone trying to learn cmake, teaching best practice is probably a good idea – Steve Lorimer Nov 01 '17 at 18:07
  • @SteveLorimer I have updated cmake to 3.2 but both commands fail. So I just copy-paste the usage of target_include_directories from https://cmake.org/cmake/help/v3.3/command/target_include_directories.html and it works! Thank you! One more question, can I use this library in another program? Since in your answer, you include both "test" library and "myapp" binary in same CMakeLists.txt. However, I would like to call this library in another program using another CMakeLists.txt under different directory. I used the code above but still cannot find header file. How can I change CMakeLists.txt? – debug_all_the_time Nov 01 '17 at 18:50
  • @KathyLee apologies, I forgot the `PUBLIC` keyword in `target_include_directories`. I've updated the answer. In order to pull in the `CMakeLists.txt` file in which your `test` library is defined, add `add_subdirectory(XXX)` to your `myapp` `CMakeLists.txt` file, where XXX is the directory in which the `test` `CMakeLists.txt` file is located – Steve Lorimer Nov 01 '17 at 18:53
  • @KathyLee updated the answer with a directory and CMakeLists layout which might help – Steve Lorimer Nov 01 '17 at 18:57
  • @SteveLorimer Thank you Steve! Your answer runs successfully! But this answer is to run library and app "at the same time" if compile src/CMakeLists.txt. Is it possible that I run the library first and save the library under one directory and then I run app with linking the lib? For example, after running the answer above, libtest.so is located in src/build/library. If I wrote another stand-alone app outside src, how can I link it to the library and let this app find all included header file? – debug_all_the_time Nov 01 '17 at 21:57
  • @SteveLorimer I found out a way to deal with my latest question. Maybe in CMakeLists.txt, I can use `INCLUDE_DIRECTORIES` to include the header files in library. Then `add_executable` and `target_link_libraries(myNewApp /home/labUser/Downloads/src/build/library/libmyLib.so")` I hope this is a correct way to use the library. Thanks! – debug_all_the_time Nov 02 '17 at 16:40
  • @KathyLee Read [this](https://stackoverflow.com/questions/15175318/cmake-how-to-build-external-projects-and-include-their-targets) and [this](https://stackoverflow.com/questions/16398937/cmake-and-finding-other-projects-and-their-dependencies) for help with your latest request – Steve Lorimer Nov 02 '17 at 16:43
  • @SteveLorimer Thanks! – debug_all_the_time Nov 02 '17 at 16:46