Qbs Documentation

Contents

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

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

---helloworld.qbs---
       import qbs.base 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 a product we want to build. In this case an application. This is just a short cut for writing

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

The name obviously is the name of the product and in this case is also the name of the produced executable. 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 (see below for an explanation of qbs modules). This is necessary to let qbs know that we have a C++ project and want to compile main.cpp with a C++ compiler.

Reusing qbs project file code

QML-like inheritance is also working in qbs.

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

   Product {
       property string craziness: "low"
   }

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

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

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

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

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

Modules

So what are these "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
   }

What we see here is a bunch of properties that can be set for the cpp module. These are used to control the behaviour of your C++ toolchain. Also we have things like FileTaggers and Rules. These are covered later.

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

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

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

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

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

       // THIS IS WORKING
       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 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: you have a file that's only going to be compiled on a certain platform. This is how you do it:

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

That odd qbs.targetOS thingy is the property target of the module qbs. The module qbs is always implicitly loaded. Its main properties are:

PropertyTypeDefaultDescription
buildVariantstring"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.
hostOSstringplatform-dependentThe host operating system. Currently "windows", "linux" or "mac".
targetOSstringplatform-dependentThe target operating system. Currently "windows", "linux" or "mac".

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

So if you want to select files by build variant, this is how you do it:

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

Setting properties for a build variant works like this:

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

or more QML style:

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

File tags and taggers

The qbs tool itself knows nothing about C++ files or its file extensions. All source file in a product are handled equally. There's the concept of a file tag, which is basically a marker or a type that can be assigned to an artifact.

An artifact can have multiple file tags. Use the Group item to group files with the same file tags (and/or set of properties).

Example:

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

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

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

This construct means that every source file that matches the glob *.cpp (and hasn't 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 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.

It's possible to use file taggers to tag files and specify custom file tags in addition:

       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

A rule looks at the pool of artifacts (in the beginning it's just the set of source files of the project) and chooses the ones that match its input file tags. Then it creates output artifacts in the build graph that have other filenames and file tags. It also creates a transformer which is basically a script that, well, 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, which can be provided along with your project. Or you can put a rule directly into your project file.

Rules reference: Rules.