Config Module

In the previous chapter, we added some properties to our main Project file. While this is a perfect approach for public properties of the project, sometimes we want to add some private properties for better tuning. Of course, we could put everything in the Project file, but that would make it very convoluted. Also, accessing the top-level project all the way from products makes things strongly tied.

Let's create a file named module.qbs and put it into the qbs/modules/config/install directory, near the qbs/imports directory:

// qbs/modules/config/install/module.qbs
ConfigInstall {
}

Here we inherit the ConfigInstall item in order to re-use its properies. Since custom qbsSearchPaths are searched first, now our version will be used instead of built-in one.

First, let's change the default value of the config.install.importLibraries in order to install import libraries on Windows:

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

We could extend the config.install module with additional properties, but that could create collisions when someone tries to include the source code of your project into their project. Thus, we will create a new config.myproject module and use the same approach with the base item as Qbs does for the config.install module.

Let's create a new item and add a new property to it:

// qbs/imports/ConfigMyProject.qbs
Module {
    property bool installPublicHeaders: false

We added the installPublicHeaders property that will allow us to configure whether we need to install public headers for our library.

Now let's use the item in a module:

// qbs/modules/config/myproject/module.qbs
ConfigMyProject {
}

Now we can use our module in the MyLibrary.qbs item:

// qbs/imports/MyLibrary.qbs
DynamicLibrary {
    version: project.version

    property pathList publicHeaders

    Depends { name: "config.myproject" }

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

We pull in the new module using the Depends item, similar to how we pulled in the cpp module dependency earlier.

We add a new publicHeaders property that will contain the list of headers we want to install.

Finally, we create a Group item that installs headers listed by the publicHeaders property to the installpaths.include directory.

Note that we use the installpaths module - it is pulled in by the DynamicLibrary item and is available in all items that inherit that item.

Now we can rewrite the lib/lib.qbs to use the new property:

// lib/lib.qbs
MyLibrary {
    name: "mylib"
    files: [ "lib.c" ]
    publicHeaders: [ "lib.h" ]
    Depends { name: 'cpp' }
    cpp.defines: ['CRUCIAL_DEFINE']
}

Instead of the config.myproject.installPublicHeaders property, we could also use the property in the Project item similar to how we did in the previous chapter. There is no strong preference which way to use, however using modules for configuration is more flexible as it is also possible to set properties of other modules as described below.

Qbs modules have the feature to automatically export properties of other modules. Those exported properties are merged in the resulting product. We can use this feature to set the cpp.rpaths in our module rather than in products:

import qbs.FileInfo
// qbs/imports/ConfigMyProject.qbs
Module {
    property bool installPublicHeaders: false

    Depends { name: "cpp" }
    Depends { name: "installpaths" }
    Depends { name: "config.install" }

    property bool enableRPath: true
    property stringList libRPaths: {
        if (enableRPath && cpp.rpathOrigin && product.installDir) {
            return [
                FileInfo.joinPaths(
                    cpp.rpathOrigin,
                    FileInfo.relativePath(
                        FileInfo.joinPaths('/', product.installDir),
                        FileInfo.joinPaths('/', config.install.dynamicLibrariesDirectory)))];
        }
        return [];
    }

    cpp.rpaths: libRPaths
}

Here, we inject the dependency on the cpp module and calculate the libRPaths property. This is a relative path from the product.installDir (which is either "bin" or "lib", depending on product type) to dynamicLibrariesDirectory (which is "lib" by default). Finally, we set cpp.rpaths to this property. This way, those rpaths will be automatically exported to all products that depend on the config.myproject module.

Let's change whether to install public headers when building our project:

$ qbs modules.config.myproject.installPublicHeaders:true
...
$ ls default/install-root/usr/local/include/
lib.h

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