Configurable Library

In this chapter, we will make our library more configurable to be able to choose between static and dynamic builds.

We need to do some modifications to export macros in the lib/lib_global.h header:

// lib/lib_global.h
#if defined(MYLIB_STATIC_LIBRARY)
#define MYLIB_EXPORT
#else
#if defined(MYLIB_LIBRARY)
#define MYLIB_EXPORT MY_LIB_DECL_EXPORT
#else
#define MYLIB_EXPORT MY_LIB_DECL_IMPORT
#endif
#endif

Here' we added a new branch when MYLIB_STATIC_LIBRARY is defined. In that case, we make the MY_LIB_EXPORT macro empty.

Now, let's modify the qbs/imports/MyLibrary.qbs file as follows:

Library {
    version: project.version

    property pathList publicHeaders
    Depends { name: "cpp" }
    Depends { name: "config.myproject" }
    Depends { name: "installpaths" }

    Group {
        condition: publicHeaders.length > 0
        name: "Public Headers"
        prefix: product.sourceDirectory + "/"
        files: publicHeaders
        qbs.install: config.myproject.installPublicHeaders
        qbs.installDir: installpaths.include
    }

    readonly property string _nameUpper : name.replace(" ", "_").toUpperCase()
    property string libraryMacro: _nameUpper + "_LIBRARY"
    property string staticLibraryMacro: _nameUpper + "_STATIC_LIBRARY"
    cpp.defines: config.build.libraryType === "static" ? [staticLibraryMacro] : [libraryMacro]
    cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined

    Export {
        Depends { name: "cpp" }
        cpp.includePaths: [exportingProduct.sourceDirectory]
        cpp.defines: exportingProduct.config.build.libraryType === "static"
            ? [exportingProduct.staticLibraryMacro] : []
    }

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

First thing to notice is that we changed the type of the item from DynamicLibrary to the Library item which is a common base item for dynamic and static libraries.

Second, we change our export macro to be different in the static and dynamic builds to correspond with the changes we made to the lib_global.h:

readonly property string _nameUpper : name.replace(" ", "_").toUpperCase()
property string libraryMacro: _nameUpper + "_LIBRARY"
property string staticLibraryMacro: _nameUpper + "_STATIC_LIBRARY"
cpp.defines: config.build.libraryType === "static" ? [staticLibraryMacro] : [libraryMacro]

Note that in a static build we export the MYLIB_STATIC_LIBRARY macro so that the dependent products will know whether they link to a static or dynamic library:

Export {
    ...
    cpp.defines: exportingProduct.config.build.libraryType === "static"
        ? [exportingProduct.staticLibraryMacro] : []
}

Now we can build our project in dynamic or static mode:

$ qbs resolve modules.config.build.libraryType:static && qbs build
...
$ ls default/install-root/usr/local/lib/
    libmylib.1.0.0.so
    libmylib.1.0.so
    libmylib.1.so
    libmylib.so
$ rm -r default
$ qbs resolve modules.config.myproject.staticBuild:true modules.config.install.staticLibraries:true && qbs build
...
$ $ ls default/install-root/usr/local/lib/
    libmylib.a

Note: Qbs does not install static libraries by default, thats why we set the config.install.staticLibraries property to true to enforce installation of the static library. We can also change the default in the config.install module:

// qbs/modules/config/install/module.qbs
ConfigInstall {
    ...
    importLibraries: true
    staticLibraries: true
    ...
}

© 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.