Sometimes we need to provide or use a third-party C/C++ dynamic-link libraries. This blog aims to teach how to build, install and use a C/C++ dynamic-link library.

Build and use manually

First, this is our construction:

1
2
3
4
.
├── hello.cc
├── hello.h
└── main.cc

Now we have a source file called hello.cc and a head file called hello.h:

1
2
3
4
5
6
7
// hello.cc

#include <iostream>

void hello() {
std::cout << "Hello form dynamic-link libaray!" << std::endl;
}
1
2
3
4
5
6
// hello.h

#ifndef HELLO_H
#define HELLO_H
void hello();
#endif

Compile source code to object(.o) file

1
g++ hello.cc -c -fPIC
  • -c: only compile the source code and not link it with other files;
  • -fPIC: the flag tells the complier to generate PIC(position-independent code), this is common in building dynamic library.
1
g++ hello.o -shared -o libhello.so
  • -shared: to create a shared library;
  • -o libxxx.so: to name the output file as libxxx.so.

Use the shared library

There is a source code called main.cc use this shared library:

1
2
3
4
5
6
#inlcude "hello.h"

int main() {
hello();
return 0;
}

Now we can compile and link it to libhello.so:

1
g++ main.cc -o main.out -L . -l hello
  • -L: tells the linker to look for library files in which directory, now it is current directory.
  • -l xxx: tells the linker to link with the library file libxxx.so.

After we run command above, we can get a executable file called main.out, so we can execute it:

1
./main.out

If met error that can’t load shared library like this:

1
./main.out: error while loading shared libraries: libhello.so: cannot open shared object file: No such file or directory

It’s related with default LD_LIBRARY_PATH, export it as current directory:

1
export LD_LIBRARY_PATH=`pwd`

Then we can get the sequence as we hope:

1
Hello form dynamic-link libaray!

Install header file

There is a new problem: if we use this shared library in other directories, source code can’t find needed head file like:

1
2
3
4
main.cc:1:10: fatal error: hello.h: No such file or directory
1 | #include "hello.h"
| ^~~~~~~~~
compilation terminated.

In order to make it convenient to include header file, we need to copy our head file to default search path, such as usr/local/include or usr/include.

1
sudo cp ~/shared-lib-demo/hello.h /usr/local/include

and run:

1
g++ main.cc -o main.out -L ../shared-lib-demo/ -l hello

Similar as we copy header file to default header file search path, we can also copy libhello.so to default library search path or add a link which point to libxxx.so in LD_LIBRARY_PATH, so that we don’t need to use -L ../shared-lib-demo/.

Then we can get what we want:

1
2
./main
Hello form dynamic-link libaray!

Build and use with CMake

Dynamic-link library construction:

1
2
3
4
5
6
7
.
├── CMakeLists.txt
├── include
│   └── hello.h
├── src
│   └── hello.cc
└── test.cc

hello.h and hello.cc are as same as above, and test.cc is similar to main.cc.

The differences between building manually and building with CMake is that we just need to write a CMakeLists.txt rather than run command line by line. Of course, we should install Cmake and GNU make before we use it.

Let’s take a consideration about what we need do to build and use a dynamic-link library:

  • Adding source code to shared library;
  • Including header directories when write code that use the library;
  • Linking new project with our library.

Build source code for shared library

In order to link it conveniently, we can install our library so that we can use it directly.

So the CMakeLists.txt looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# set version requirement and project name
cmake_minimum_required(VERSION 3.16)
project(hello_lib)

# set CXX standard
set(CMAKE_CXX_STANDARD 17)

# add a library called hello_lib, SHARED means dynamic-link library
add_library(hello_lib SHARED src/hello.cc)
# let new project who use this library can find header needed
target_link_directories(hello_lib PUBLIC include)

# add executalble file and link it with library
add_executable(test.out test.cc)
target_link_libraries(test.out hello_lib)

# install the library target and the header files
install(TARGETS hello_lib LIBRARY DESTINATION lib)
install(FILES include/hello.h DESTINATION include)

For test it, we can make a directory called build and cd build, run:

1
2
cmake ..    # out source compile
make

Then you well get a executable called test.out which can let you run and test it.

To install, just run make install after run make.

Use installed library in new project

New project directory:

1
2
3
.
├── CMakeLists.txt
└── main.cc

main.cc:

1
2
3
4
5
6
#include "hello.h"

int main() {
hello();
return 0;
}

It’s no doubt that we need tell the new project that it should depend the shard library, so write it in cmake:

1
2
3
4
5
6
7
8
9
10
cmake_minimum_required(VERSION 3.16)
project(cmake_dynamic_lib_test)

set(CMAKE_CXX_STANDARD 17)

add_executable(main.out main.cc)

# find installed lib, link it to executalble file
find_library(HELLO_LIB hello_lib)
target_link_libraries(main.out ${HELLO_LIB})

Run

1
2
3
4
mkdir build
cd build
cmake ..
make

You can get main.out as we expect.