Good day.
I need to access specific QBS variables inside my Qt code.
An example is the Application's name, organisation or even the flavour, all variables specified like this in my application qbs file.
import qbs
Project {
// These variables should be available in C++ code.
name: "my_app_name"
organization: "Organisation_Name"
flavour:"AppFlavour"
minimumQbsVersion: "1.7.1"
CppApplication {
files: [
]
Depends { name: "Qt"; submodules: ['core', 'network'] }
cpp.cxxLanguageVersion: "c++11"
cpp.defines: [
"QT_DEPRECATED_WARNINGS",
]
consoleApplication: true
Group {
name: "source"
files: [
"qconfigurationmanager.cpp",
]
}
Group {
name: "header"
files: [
"qconfigurationmanager.h",
]
}
Group { // Properties for the produced executable
fileTagsFilter: "application"
qbs.install: true
}
}
}
Looking at the Qt documentation for QBS, I did not find any reference to using QBS variables in Qt code.
This is the only link of using QBS variables, but only within the QBS file
I would like to do this:
QString appflavour = Qbs.get("flavour")
How can I do this?
A possible option is to use a DEFINES and obtain the data through a macro:
import qbs
Project {
minimumQbsVersion: "1.7.1"
property string name: "my_app_name"
property string organization: "Organisation_Name"
property string flavour:"AppFlavour"
CppApplication {
Depends { name: "Qt"; submodules: ['core', 'network']}
cpp.cxxLanguageVersion: "c++11"
consoleApplication: true
cpp.defines: [
"QT_DEPRECATED_WARNINGS",
"name=" + project.name,
"organization=" + project.organization,
"flavour=" + project.flavour
]
...
#define QUOTE_(x) #x
#define QUOTE(x) QUOTE_(x)
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
QString appname = QUOTE(name);
QString organization = QUOTE(organization);
QString appflavour = QUOTE(flavour);
qDebug()<< appname << organization << appflavour;
...
Output:
"my_app_name" "Organisation_Name" "AppFlavour"
Related
Given unnamed JSON document:
[
{
},
{
},
]
Qt 5.10+ has operator[] for QJsonDocument, so we can address any of them by index:
json_doc[1];
How does one do the same in older versions of Qt?
In your particular example the Json document is represented by a Json array. You might get it like:
if (document.isArray() {
auto a = document.array();
// TODO: check the array size before
auto v = a[0];
}
I was trying to create a style plugin for my project, the plugin seems being loaded, but why QStyleFactory::keys() didn't return my key?
By setting QT_DEBUG_PLUGINS to 1, I got following message :
Found metadata in lib .../styles/libstyles.so, metadata=
{
"IID": "this.is.my.style",
"MetaData": {
"Keys": [
"mystyle"
]
},
"className": "MyStylePlugin",
"debug": true,
"version": 329986
}
in my main():
QApplication app(argc, argv);
QApplication::setStyle(QStyleFactory::create("mystyle"));
qDebug() << QStyleFactory::keys();
The last qDebug statement prints:
Got keys from plugin meta data ()
("Windows", "Fusion") <= Shouldn't "mystyle" also show up here?
That's because your IID should be ""org.qt-project.Qt.QStyleFactoryInterface" not "this.is.my.style". If you change the IID, the plugin is not recognize as a style plugin by Qt.
Here is an extract of Qt code where the keys are detected:
QString iid = library->metaData.value(QLatin1String("IID")).toString();
if (iid == QLatin1String(d->iid.constData(), d->iid.size())) {
QJsonObject object = library->metaData.value(QLatin1String("MetaData")).toObject();
metaDataOk = true;
QJsonArray k = object.value(QLatin1String("Keys")).toArray();
for (int i = 0; i < k.size(); ++i)
keys += d->cs ? k.at(i).toString() : k.at(i).toString().toLower();
}
if (qt_debug_component())
qDebug() << "Got keys from plugin meta data" << keys;
You can see that on the 2nd line if the IID from you plugin does not match the expected IID (d->iid), the code will not bother to try to read the MetaData.
I want to make a module to use the QtRO repc compiler to produce .h files from .rep files.
I coded the module but when I try to load it in an application product it does not load and disable the product.
the modules are in C:\Users\User\qt\qbs
Qbs Module replica.qbs:
import qbs
Module {
property bool source: true
FileTagger {
patterns: "*.rep"
fileTags: ["rep"]
}
Rule {
inputs: ["rep"]
Artifact {
fileTags: ["txt_output"]
}
prepare: {
var cmd = new Command();
cmd.program = "repc.exe";
if source {
cmd.arguments = ["-i", "rep", "-o", "source", input.filePath];
} else {
cmd.arguments = ["-i", "rep", "-o", "replica", input.filePath];
}
console.log("repc on : ", input.filePath);
return [cmd];
}
}
}
product.qbs:
import qbs
Application {
name: "ServiceExposer"
Depends { name: "cpp" }
Depends { name: "Qt.core" }
Depends { name: "Qt.remoteobjects" }
Depends { name: "replica" }
files: [
"main.cpp",
"service_exposer.rep"
]
}
project.qbs:
import qbs
Project {
references: ["ServiceExposer/ServiceExposer.qbs"]
qbsSearchPaths: "C:\Users\User\qt\qbs"
}
I don't see where I made the mistake.
Thank you in advance for your help.
If it's a header file, why do you give it the "cpp" tag? Shouldn't it be "hpp"?
What is the reason you are putting the file into the source directory? Do you plan on adding it to your repository? Normally, build artifacts (no matter whether they are binaries or human-readable files) should be located inside the build directory as not to "pollute" the source tree.
You did not mention in what way the module does not work for you now, so it's hard to diagnose the problem. You should mention what you expected to happen and what happened instead (giving the concrete error message, if there is one).
I managed to make it work after digging a little more in the doc and source code, I share with you the working module.
This module when imported if there are any .rep files (QtRO (remote objects)) module remote object definition) in your project, it will invoke the repc compiler and compile them and put the resulting .h file in your source directory.
Still not complete, I didn't find a way to manipulate the files property of the Product Item to add the .h to it automatically.
import qbs
import qbs.FileInfo
Module {
FileTagger {
patterns: ["*.rep"]
fileTags: ["repc-rep"]
}
Rule {
inputs: ["repc-rep"]
Artifact {
filePath: repc_" + FileInfo.baseName(input.fileName) + "_source.h"
fileTags: ["cpp"]
}
prepare: {
var cmd = new Command();
cmd.description = "repc " + input.fileName;
cmd.program = "repc.exe"
cmd.arguments = ["-i", "rep", "-o", "source", input.filePath, output.filePath];
var cmd2 = new JavaScriptCommand();
cmd2.silent = true;
cmd2.sourceCode = function() {
File.copy(output.filePath, FileInfo.path(input.filePath) + "/" + output.fileName);
}
return [cmd, cmd2];
}
}
}
In order to this module to work, the repc.exe must be in your path.
Any suggestion are welcomed.
I cannot figure out why uglify does not want concat string as input or output ...
This works :
uglify: {
dev_uglify_js: {
files: {
'my_file.min.js': ['my_file.js']
}
}
}
For example, this does not works :
uglify: {
dev_uglify_js: {
files: {
'my'+'_file.min.js': ['my_file.js']
}
}
}
Do you have any idea why ?
The output error is "SyntaxError: Unexpected token".
The real insterest here is to concatenate a timestamp to the file name.
But just with 2 strings it does not work so ...
Thanks for your help !
In JavaScript, an object key cannot be declared dynamically. This is not a problem with grunt or uglify - it's a language constraint.
myObject = { 'a' + 'b' : 'b' } // NOPE!
However, any object property can be accessed via square brackets. For example:
myObject = { 'banana': 'boat' }
myObject.banana // boat
myObject['banana'] // boat!
Therefore, you can add properties after the object is already created, using the square brackets syntax.
myObject = {}
myObject[ 'a' + 'b' ] = 'b' // Yes
myObject.ab // b
The Gruntfile example
In your Gruntfile, you're bound to, at some point, call something like grunt.config.init or grunt.initConfig. This is usually done inline:
grunt.initConfig({
uglify: {} // properties ...
});
However, initConfig simply receives an object. You can define it and manipulate it as much as you need before calling this function. So, for example:
var config = { uglify: {} };
config.uglify['such'+'dynamic'+'very'+'smarts'] = {};
grunt.initConfig(config);
Similar questions:
How do I create a dynamic key to be added to a JavaScript object variable
How do I add a property to a JavaScript object using a variable as the name?
I want to use Qbs to compile an existing project. This project already contains a code-transformation-tool (my_tool) that is used heavily in this project.
So far I have (simplified):
import qbs 1.0
Project {
Application {
name: "my_tool"
files: "my_tool/main.cpp"
Depends { name: "cpp" }
}
Application {
name: "my_app"
Group {
files: 'main.cpp.in'
fileTags: ['cpp_in']
}
Depends { name: "cpp" }
Rule {
inputs: ["cpp_in"]
Artifact {
fileName: input.baseName
fileTags: "cpp"
}
prepare: {
var mytool = /* Reference to my_tool */;
var cmd = new Command(mytool, input.fileName, output.fileName);
cmd.description = "Generate\t" + input.baseName;
cmd.highlight = "codegen";
return cmd;
}
}
}
}
How can I get the reference to my_tool for the command?
This answer is based on an email from Qbs author Joerg Bornemann who allowed me to cite it here.
The property usings of Rule allows to add artifacts from products dependencies to the inputs.
In this case we are interested in "application" artifacts.
The list of applications could then be accessed as input.application.
Application {
name: "my_app"
Group {
files: 'main.cpp.in'
fileTags: ['cpp_in']
}
Depends { name: "cpp" }
// we need this dependency to make sure that my_tool exists before building my_app
Depends { name: "my_tool" }
Rule {
inputs: ["cpp_in"]
usings: ["application"] // dependent "application" products appear in inputs
Artifact {
fileName: input.completeBaseName
fileTags: "cpp"
}
prepare: {
// inputs["application"] is a list of "application" products
var mytool = inputs["application"][0].fileName;
var cmd = new Command(mytool, [inputs["cpp_in"][0].fileName, output.fileName]);
cmd.description = "Generate\t" + input.baseName;
cmd.highlight = "codegen";
return cmd;
}
}
}
Unfortunately the usings property in a Rule is deprecated since QBS 1.5.0. At the moment I have the same requirement. Using a product artifact in a non multiplex Rule.
The problem with a multiplex Rule is, if a single file in the input set changes, all input artifacts will be re-processed. Which is rather time consuming in my case.