2024-01-29

OctoMY™ Mandate in Toki Pona!

I completely forgot to post this on the day. In honour of OctoMY™'s anniversary, I have coaxed ChatGPT-4  into translating the OctoMY™ mandate into Toki Pona!


2024-01-07

OctoMY™ Anniversary 2024!

 


OctoMY™ has pretty much been in hibernation through 2023 with the exception of some updates:

  • Qt version was upgraded from Qt5 to Qt6
  • Build system was converted from qmake to Qbs
  • Website was upgraded from google sites to a custom site

There was also a few small updates behind the scenes...

We are still on track with the original plan of securing funding for the project going forward.

Happy 2024!

2023-12-10

New website

 Google shut down their classic google sites and the work of migrating to a new home-grown website paid off!


The content is pretty much the same, but the infrastructure is better aligned with our future plans for the project.


OctoMY™ website has been updated!


2023-02-24

Qbs tutorial part 5/5: Apps

The apps

In the last part we looked at how to select files with the group feature in Qbs , and in this last part of the series we will look at how to build our apps with src/apps.qbs.

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

The final part of the puzzle is our app.qbs definition which looks like this:

OctoMYApp {
    name: "Agent"
    useCombinedLibs: true
    property string path2:path
}

Again, I hope you put two and two together to realize that OctoMYApp is defined in ./integrations/qbs/OctoMYApp.qbs and represents a reusable component that is included in all our app.qbs files for centralized management for convenience.

If we follow the white rabbit again, ./integrations/qbs/OctoMYApp.qbs looks like this:

import qbs.FileInfo

Application {
    name: "OctoMYApp"
    property string appDir: FileInfo.cleanPath(path2) + FileInfo.pathSeparator()
    property string srcDir: FileInfo.cleanPath(FileInfo.joinPaths(project.sourceDirectory, "src")) + FileInfo.pathSeparator()
    property string libsDir: FileInfo.cleanPath(FileInfo.joinPaths(srcDir, "libs")) + FileInfo.pathSeparator()
    property bool useCombinedLibs: true
    
    // Add dependency on all Qt components that we need
    OctoMYQtDepends {}

    // Add dependency on cpp
    Depends{
        name: "cpp"
    }

    // Enumerate OctoMY lib folders
    OctoMYLibProbe {
        id: octomyLibs
        searchPath: libsDir
    }

    //"QT_DISABLE_DEPRECATED_BEFORE=0x060000" // disables all the APIs deprecated before Qt 6.0.0
    // Tell cpp to look for header files in all the lib folders
    cpp.includePaths: base.concat(octomyLibs.libFolders)

    // Tell cpp all the auto-generated defines for OctoMY libraries
    cpp.defines: base.concat(octomyLibs.libDefines)

    // Tell cpp what version and stdlib we want
    cpp.cxxLanguageVersion: "c++20"
    cpp.cxxStandardLibrary: "libc++"

    install: true
    installDir: qbs.targetOS.contains("qnx") ? FileInfo.joinPaths("/tmp", name, "bin") : base

    OctoMYFiles{
        name: "app_sources"
        prefix: appDir
    }
    // We DON'T use combined libs, include all lib sources directly here
    OctoMYFiles{
        condition: !useCombinedLibs
        name: "lib_sources"
        prefix: libsDir
    }
    // We DO use combined libs, depend on combined lib here
    Depends { 
        condition: useCombinedLibs
        name: "combined"
    }
}

This time we use a bunch of helpers, some of which you already know:
  • OctoMYQtDepends: Our helper to include dependency on all the Qt modules that OctoMY™ needs
  • OctoMYLibProbe: Our helper to enumeratelibrary folders
  • OctoMYFiles: Our helper to list source files in a project that we care about (this is equivalent to tile fil Group in our StaticLibrary above).
Please note the user defined property we made called useCombinedLibs. It will include two different blocks depending on it's value. If it is true, then we have a Depends{ name: "combined"} and if it is not true we include a file group OctoMYFiles that basically lists all the source files of all our libraries in one big list.

This allows us to choose wether we want to build libraries separately and depnd on them or build the sources of the libraries directly into the app.



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

I hope this series was useful, and that it helped you see the many benefits and few shortcommings of Qbs so that you may implement it in your own projects.

Good luck!


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

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/