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", "lib.h", "lib_global.h", ] version: "1.0.0" install: true 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 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 install: true cpp.rpaths: { if (!cpp.rpathOrigin) return []; return [ FileInfo.joinPaths( cpp.rpathOrigin, FileInfo.relativePath( FileInfo.joinPaths("/", product.installDir), FileInfo.joinPaths("/", "lib"))) ]; } }
© 2022 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.