2023-02-24

Qbs tutorial part 4/5: File Groups

 




Selecting files with Groups





In the last part we went into the sub-project file src/libs.qbs in detail, looking at how libs were built. But we left out one important part, namely the Group type in Qbs. This will be the focus of this part! But first our file tree as a reference:

.
├── OctoMY.qbs
├── src
│   ├── agent
│   │   ├── AgentMain.cpp
│   │   ├── AgentMain.hpp
│   │   ├── app.qbs
│   │   └── README.md
│   ├── apps.qbs
│   ├── combined
│   │   └── lib.qbs
│   ├── libs
│   │   ├── libagent
│   │   │   ├── agent
│   │   │   │   ├── Agent.cpp
│   │   │   │   ├── Agent.hpp
│   │   │   │   └── AgentWindow.ui
│   │   │   ├── lib.qbs
│   │   │   └── README.md
│   ├── libs.qbs
├── test
│   ├── common_test
│   │   ├── Common_test.hpp
│   │   ├── CommsTester.cpp
│   │   ├── CommsTester.hpp
│   │   ├── mock
│   │   │   ├── MockCourier.cpp
│   │   │   └── MockCourier.hpp
│   │   ├── resources
│   │   │   ├── icons
│   │   │   │   ├── profile.svg
│   │   │   │   ├── stress.svg
│   │   │   │   └── test.svg
│   │   │   └── test_resources.qrc
│   │   ├── Utility_test.cpp
│   │   └── Utility_test.hpp
│   ├── testLogHandler
│   │   ├── TestLogHandler.cpp
│   │   ├── TestLogHandler.hpp
│   │   └── test.qbs
│   ├── testTryToggle
│   │   ├── TestTryToggle.cpp
│   │   ├── TestTryToggle.hpp
│   │   └── test.qbs
│   └── tests.qbs
└── integration
   └── qbs
     └── imports
            ├── OctoMYApp.qbs
            ├── OctoMYLibProbe.qbs
            ├── OctoMYAutoLib.qbs
            ├── OctoMYTestProbe.qbs
            ├── OctoMYAutoTest.qbs
            ├── OctoMYFiles.qbs
            ├── OctoMYQtDepends.qbs
            └── utils.js


Groups are maybe the most important of all the types in Qbs. This is Qbs' very convenient way of enumerating a named set of files used for input to a build. If we look at the StaticLibrary in the last part, we found this:

// Enumerate source files for this library
    Group{
        name: FileInfo.fileName(path2)+"_sources"
        prefix: fulldir
        files: [
            "**/*.cpp",
            "**/*.hpp",
            "**/*.c",
            "**/*.h",
            "**/*.ui",
            "**/*.qrc"
        ]
    }

Groups are named using the name property which comes in very handy as you will see the names inside QtCreator.

The files property is where you specify the files. In a simple project you can just list files by fulll filename, but Groups also support an intuitive set of glob style wildcards. From the documentation:

When specifying files, you can use the wildcards "*", "?" and "[]", which have their usual meaning. By default, matching files are only picked up directly from the parent directory, but you can tell Qbs to consider the whole directory tree. It is also possible to exclude certain files from the list. The pattern ** used in a pathname expansion context will match all files and zero or more directories and subdirectories.
The prefix property allows you to avoid repeating a long pathname by specifying it only once.

⚠️DANGER⚠️ Please note this very common and dangerous pitfall: the prefix parameter is string based and NOT file based! This is by design. This means that if you omit the directory separator at the end of your prefix then the result will be completely differnt. For example:

Group{
   name: "example1"
   prefix: "mydir/mysubdir"
  files:[ "a.cpp", "b.cpp", "c.cpp"
}
// Results in mydir/mysubdira.cpp, mydir/mysubdirb.cpp, mydir/mysubdirc.cpp which could be what you wanted, but maybe not

Group{
   name: "example2"
   prefix: "mydir/mysubdir/"
  files:[ "a.cpp", "b.cpp", "c.cpp"]
// Results in mydir/mysubdir/a.cpp, mydir/mysubdir/b.cpp, mydir/mysubdir/c.cpp which could be what you wanted, but maybe not

In our StaticLibrary, you will see two strange things in the Group. One strange thing is that we somehow just grab ALL the files of certain types:

files: [
    "**/*.cpp",
    "**/*.hpp",
    "**/*.c",
    "**/*.h",
    "**/*.ui",
    "**/*.qrc"
]

How is this possible?

This is another very powerful feature: Qbs will determine how to process a file from that file's type through the use of FileTaggers and Rules. You no longer have to manually segregate files by types like you had to in qmake through the SOURCES and HEADERS variables. Instead Qbs will simply identify what is a header and what is a source, a resource, a designer ui file, a qml file and so on. In fact, Qbs supports a quite large and growing set of languages, platforms and toolchains. At first this might not seem like a big deal, but it has many benefits.

For one, projects which mix languages and platforms now suddenly will have "nirvana" in the form of one build tool to build it all.

Furthermore, managing projects becomes more fluid. Simply list the filetypes you care about and let Qbs figure out the rest.

Another benefit is that all the rules for building to the different platforms and toolchians is defined in  Qbs syntax, so you can very well extend it to suit your own needs, for example if you create your own domain spesific languages or encouter archaic or exotic toolchains that you depend on in your build, you can create the necessary Rules, FileTaggers and so on to handle themin your Qbs build.

Final note on file Groups is that we use the globstar (**) wildcard. This means "zero or more path elements". We use this because each of the libraries in OctoMY™ will have an unknown number of possibly nested subfolders, and we want to grab sources from all of them. Very convenient!

Now we know how files are selected in Qbs🎉!

This concludes the fourth part in a series on using Qbs to our benefit  and joy in a non-trivial project, namely OctoMY™

In the next part we will look at app.qbs and how it pulls in the necesary dependencies to build our apps.

Illustrations borrowed from the amazing collection at https://ericjoyner.com/

No comments:

Post a Comment