- Qbs Manual
- Dynamic Library
- Qbs 3.3.0

Static Library ◦ Convenience Items

# Dynamic Library

Let's now turn our static library into a dynamic library. It is a bit trickier with dynamic libraries since several things should be tweaked. First, we need to be able to mark methods (or classes) in our library as exported. Second, we need to tell our application where to look for our library at run time using rpaths.

Some compilers, like MSVC, require us to mark which symbols we want to export or import. The canonical way would be to define some helper macros. Let's start with a header that defines those helper macros:

     // lib/lib_global.h

     #ifndef LIB_GLOBAL_H
     #define LIB_GLOBAL_H

     #if defined(_WIN32) || defined(WIN32)
     #define MYLIB_DECL_EXPORT __declspec(dllexport)
     #define MYLIB_DECL_IMPORT __declspec(dllimport)
     #else
     #define MYLIB_DECL_EXPORT __attribute__((visibility("default")))
     #define MYLIB_DECL_IMPORT __attribute__((visibility("default")))
     #endif

     #if defined(MYLIB_LIBRARY)
     #define MYLIB_EXPORT MYLIB_DECL_EXPORT
     #else
     #define MYLIB_EXPORT MYLIB_DECL_IMPORT
     #endif

     #endif // LIB_GLOBAL_H

This header defines the `MYLIB_EXPORT` macro that expands either to an "export" or to an "import" directive based on the presence of the `MYLIB_LIBRARY` macro. We can use this macro to mark a function as follows:

     // lib/lib.h
     #include "lib_global.h"

     MYLIB_EXPORT const char *get_string();

The modified library product may look like this:

     // lib/lib.qbs

     DynamicLibrary {
         name: "mylib"
         files: ["lib.c"]
         publicHeaders: ["lib.h", "lib_global.h"]
         version: "1.0.0"

         Depends { name: "cpp" }
         cpp.defines: ["MYLIB_LIBRARY", "CRUCIAL_DEFINE"]
         cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined

         Export {
             Depends { name: "cpp" }
             cpp.includePaths: [exportingProduct.sourceDirectory]
         }

         Depends { name: "bundle" }
         bundle.isBundle: false
     }

The most important change is that we changed the item type from StaticLibrary to DynamicLibrary. We moved the `"lib.h"` header into the publicHeaders property. That way, the header will be installed with the library to the `<qbs.installPrefix>/include/my project/mylib/` directory. If you do not want to ship headers with your library, simply put those into the files property like we did in the previous chapter. We also added the `MYLIB_LIBRARY` to the list of defines. We do this only when building the library but we are not exporting it - that way the users of our library will have methods marked for import rather than export.

Finally, we set cpp.sonamePrefix to `"@rpath"`. This is required only for Apple platforms, see Run-Path Dependent Libraries for details.

It is also required to set cpp.rpaths in our application file. Since the library is installed to the `lib` directory and the application is installed to the `bin` directory, we need to tell the loader to look in the `lib` directory. The FileInfo.relativePath method can help us:

     cpp.rpaths: {
         if (!cpp.rpathOrigin)
             return [];
         return [
             FileInfo.joinPaths(
                 cpp.rpathOrigin,
                 FileInfo.relativePath(
                     FileInfo.joinPaths("/", product.installDir),
                     FileInfo.joinPaths("/", "lib")))
         ];
     }

On Linux, this expression would be equivalent to this: `cpp.rpaths: ["$ORIGIN/../lib"]`. Don't forget to `import qbs.FileInfo` in order to be able to use the jsextension-fileinfo.html extension.

To make the example complete, here's how the full `app/app.qbs` file should look like:

     // app/app.qbs
     import qbs.FileInfo

     CppApplication {
         Depends { name: "mylib" }
         name: "My Application"
         targetName: "myapp"
         files: "main.c"
         version: "1.0.0"

         consoleApplication: true

         cpp.rpaths: {
             if (!cpp.rpathOrigin)
                 return [];
             return [
                 FileInfo.joinPaths(
                     cpp.rpathOrigin,
                     FileInfo.relativePath(
                         FileInfo.joinPaths("/", product.installDir),
                         FileInfo.joinPaths("/", "lib")))
             ];
         }
     }