Using Qbs Building Applications with Qbs

# Language Introduction

Qbs uses project files (*.qbs) to describe the contents of a project. A project contains one or more products. A product is the target of a build process, typically an application, library or maybe a tar ball.

## The Obligatory Hello World Example

Qbs project files are written using a QML dialect. A very simple C++ hello world project looks like this:

    ---helloworld.qbs---
           import qbs 1.0

           Application {
               name: "helloworld"
               files: "main.cpp"
               Depends { name: "cpp" }
           }

The import statement gives us access to some built-in types and specifies the used language version.

Application describes the product we want to build. In this case, an application. This is just a shortcut for writing

           Product {
               type: "application"
               // ...
           }

The name is the name of the product. In this case it is also the name of the produced executable (on Windows, the ".exe" extension is added by default). In the property files, we specify the source files for our product. Unlike QML, the right-hand side can be either a string or a string list. A single string is converted to a stringlist containing just one element.

Depends adds the dependency to the module cpp. This is necessary to let Qbs know that we have a C++ project and want to compile main.cpp with a C++ compiler. For more information about Qbs modules, see Modules.

## Reusing Qbs Project File Code

QML-like inheritance works also in Qbs.

       ---CrazyProduct.qbs---
       import qbs 1.0

       Product {
           property string craziness: "low"
       }

       ---hellocrazyworld.qbs---
       import "CrazyProduct.qbs" as CrazyProduct

       CrazyProduct {
           craziness: "enormous"
           name: "hellocrazyworld"
           // ...
       }

You can put JS code into separate `.js` files and then import them.

       ---helpers.js---
       function planetsCorrectlyAligned()
       {
           // implementation
       }

       ---myproject.qbs---
       import qbs 1.0
       import "helpers.js" as Helpers

       Product {
           name: "myproject"
           Group {
               condition: Helpers.planetsCorrectlyAligned()
               file: "magic_hack.cpp"
           }
           // ...
       }

## Modules

A module is a collection of properties and language items that are used for building a product if the product depends on (or loads) the module.

For example, the cpp module looks like this (simplified):

       Module {
           name: "cpp"
           property string warningLevel
           property string optimization
           property bool debugInformation
           property path precompiledHeader
           // ...
           FileTagger {
               pattern: "*.cpp"
               fileTags: ["cpp"]
           }
           Rule {...}  // compiler
           Rule {...}  // application linker
           Rule {...}  // static lib linker
           Rule {...}  // dynamic lib linker
       }

The properties that can be set for the cpp module are used to control the behavior of your C++ tool chain. In addition, you can use FileTaggers and Rules that are explained later.

As soon as your product depends on a module, it can set the properties of the module. You specify the optimization level for your product (and all build variants) like this:

    ---helloworld.qbs---
           import qbs 1.0

           Application {
               name: "helloworld"
               files: ["main.cpp"]
               cpp.optimization: "ludicrousSpeed"
               Depends { name: "cpp" }
           }

A module can implicitly depend on other modules. For example, the `Qt.core` module depends on `cpp`. But to set the properties of a module you must explicitly import it.

           // THIS DOES NOT WORK
           Application {
               name: "helloworld"
               files: ["main.cpp"]
               Depends { name: "Qt.core" }
               cpp.optimization: "ludicrousSpeed"
               // ERROR! We do not know about "cpp" here,
               // though "Qt.core" depends on "cpp".
           }

           // THIS WORKS
           Application {
               name: "helloworld"
               files: ["main.cpp"]
               Depends { name: "Qt.core" }
               Depends { name: "cpp" }
               cpp.optimization: "ludicrousSpeed"
           }

### Different Properties for a Single File

Not only the product, but all the source files of the product can have their own set of module properties. For example, assume you have some files that are known to crash your compiler if you turn on optimizations. You want to turn off optimizations for just these files and this is how you do it:

           Application {
               name: "helloworld"
               files: "main.cpp"
               Group {
                   files: ["bad_file.cpp", "other_bad_file.cpp"]
                   cpp.optimization: "none"
               }
               Depends { name: "cpp" }
           }

### Selecting Files by Properties

The classical (pre-lighthouse) Qt case is that you have a file that is only going to be compiled on a certain platform. This is how you do it:

           Group {
               condition: qbs.targetOS.contains("windows")
               files: [
                   "harddiskdeleter_win.cpp",
                   "blowupmonitor_win.cpp",
                   "setkeyboardonfire_win.cpp"
               ]
           }
           Group {
               condition: qbs.targetOS.contains("linux")
               files: [
                   "harddiskdeleter_linux.cpp",
                   "blowupmonitor_linux.cpp",
                   "setkeyboardonfire_linux.cpp"
               ]
           }

In the above example, qbs.targetOS is a property of the target of the the qbs module. The qbs module is always implicitly loaded. Its main properties are:

Property | Type | Default | Description
--- | --- | --- | ---
buildVariant | string | "debug" | Name of the current build variant. By default, "debug" and "release" are valid values but the user can add more in a project file.
hostOS | stringlist | platform-dependent | The host operating system. May contain "windows", "linux", "osx", "darwin", "unix", etc.
targetOS | stringlist | platform-dependent | The target operating system. May contain "windows", "linux", "osx", "darwin", "unix", "ios", "android", "blackberry", "qnx", etc.

You can set these properties on the command line or by using a profile. The property qbs.buildVariant is handled in a special way.

       $ qbs                   # qbs.buildVariant:debug, profile:<default profile>
       $ qbs release           # qbs.buildVariant:release, profile:<default profile>
       $ qbs profile:Maemo     # qbs.buildVariant:debug, profile:Maemo
       $ qbs debug release     # builds two variants of the project

To select files by build variant:

           Group {
               condition: qbs.buildVariant == "debug"
               files: "debughelper.cpp"
           }

To set properties for a build variant:

           Properties {
               condition: qbs.buildVariant == "debug"
               cpp.debugInformation: true
               cpp.optimization: "none"
           }

Or, to use a more QML-like style:

           cpp.debugInformation: qbs.buildVariant == "debug" ? true : false
           cpp.optimization: qbs.buildVariant == "debug" ? "none" : "fast"

## File Tags and Taggers

Qbs itself knows nothing about C++ files or file extensions. All source files in a product are handled equally. However, you can assign file tags to an artifact to act as a marker or to specify a file type.

An artifact can have multiple file tags. For example, you can use the Group item to group files with the same file tags (or a set of properties).

           Product {
               Group {
                   files: ["file1.cpp", "file2.cpp"]
                   fileTags: ["cpp"]
               }
               Group {
                   files: "mydsl_scanner.l"
                   fileTags: ["flex", "foobar"]
               }
               // ...
           }

When you load the cpp module, you also load the following item:

           FileTagger {
               pattern: "*.cpp"
               fileTags: ["cpp"]
           }

This construct means that each source file that matches the pattern `*.cpp` (and has not explicitly set a file tag) gets the file tag `cpp`.

The above example can be simplified to

           Product {
               Depends: "cpp"
               files: ["file1.cpp", "file2.cpp"]
               Group {
                   files: "mydsl_scanner.l"
                   fileTags: ["flex", "foobar"]
               }
               // ...
           }

The FileTagger from the cpp module automatically assigns the `cpp` file tag to the source files. Groups that just contain the files property can be more simply expressed by using the files property of the product.

File tags are used by rules to transform one type of artifact into another. For instance, the C++ compiler rule transforms artifacts with the file tag `cpp` to artifacts with the file tag `obj`.

In addition, it is possible to use file taggers to tag files and specify custom file tags:

           Product {
               Depends: "cpp"
               files: ["main.cpp"]         // Gets the file tag "cpp" through a FileTagger item.
               Group {
                   overrideTags: false     // The overrideTags property defaults to true.
                   files: ["main.cpp"]
                   fileTags: ["foobar"]    // Gets the file tag "foobar" in addition to "cpp".
               }
               // ...
           }

## Rules

Qbs applies a rule to a pool of artifacts (in the beginning it is just the set of source files of the project) and chooses the ones that match the input file tags specified by the rule. Then it creates output artifacts in the build graph that have other filenames and file tags. It also creates a script that transforms the input artifact into the output artifact.

For examples of rules, see the share/qbs/modules directory in the Qbs repository.

You can define rules in your own module to be provided along with your project. Or you can put a rule directly into your project file.

For more information, see Rule Item.

Using Qbs Building Applications with Qbs