Convenience Items | Qbs Manual

- Qbs Manual
- Convenience Items
- Qbs 3.1.1

Dynamic Library ◦ Autotest

# Convenience Items

As you might have noticed, we are repeating ourselves when setting the same properties in our products - we set version, cpp.rpaths, and so on. For a single application and a library, that is not a big deal, but what if we have a dozen libraries? Luckily, this can be achieved using item inheritance - we move the common code to the base item and in the real product we will only set what is specific to that product (for example, the list of files).

First, we need to tell Qbs where to look for our new base items. This can be achieved using the qbsSearchPaths property. We set this property to `"qbs"` so that Qbs will search our items in the `qbs` directory located in the project directory:

     Project {
        name: "My Project"
        minimumQbsVersion: "2.0"
        references: [
            "app/app.qbs",
            "lib/lib.qbs"
        ]
        qbsSearchPaths: "qbs"
     }

Note: This directory has a pre-defined structure: base items should be located under the `imports` subdirectory. See Custom Modules and Items for details.

Let's create a base item for all our applications and move common code there:

     // qbs/imports/MyApplication.qbs

     import qbs.FileInfo

     CppApplication {
         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")))
             ];
         }
     }

As you see, we managed to extract most of the code here, and our application file now only contains what's relevant to it:

     // app/app.qbs

     MyApplication {
         Depends { name: "mylib" }
         name: "My Application"
         targetName: "myapp"
         files: "main.c"
     }

Now let's do the same for our library:

     // qbs/imports/MyLibrary.qbs

     DynamicLibrary {
         version: "1.0.0"

         Depends { name: 'cpp' }
         property string libraryMacro: name.replace(" ", "_").toUpperCase() + "_LIBRARY"
         cpp.defines: [libraryMacro]
         cpp.sonamePrefix: qbs.targetOS.contains("darwin") ? "@rpath" : undefined

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

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

Here, we introduce a helper property, `libraryMacro`, with a default value calculated based on the capitalized product name. Since the name of out library product is `"mylib"`, this property will expand to `"MYLIB_LIBRARY"`. We can also override the default value for the macro in products that inherit our item like this:

     MyLibrary {
         libraryMacro: "SOME_OTHER_LIBRARY_MACRO"
     }

Let's take a look at the refactored library file:

     // lib/lib.qbs

     MyLibrary {
         name: "mylib"
         files: [
             "lib.c",
             "lib.h",
             "lib_global.h",
         ]
         cpp.defines: base.concat(["CRUCIAL_DEFINE"])
     }

We managed to extract the reusable parts to common base items leaving the actual products clean and simple.

Unfortunately, item inheritance comes with a price - when both parent and child items set the same property (cpp.defines in our case), the value in the child item wins. To work around this, the special base value exists - it gives access to the base item's value of the current property and makes it possible to extend its value rather than override it. Here, we concatenate the list of defines from the base item `["MYLIB_LIBRARY"]` with a new list, specific to this product (namely, `['CRUCIAL_DEFINE']`).

Dynamic Library ◦ Autotest