How to add a logic to .pro file based on configuration? - qt

In my application (qmake based) I have 2 configuration, let's say CONF1 and CONF2.
Each configuration defines "Additional arguments" at Project/Build settings/Build step tab:
DEFINES+=CONF1
and
DEFINES+=CONF2
So in C++ code I can add some specified logic for specified build configuration:
#if defined CONF1
logo->setPixmap(QPixmap("conf1.png"));
#else
logo->setPixmap(QPixmap("conf2.png"));
#endif
Also I need to define icon for the application executable.
So in .pro file I've added:
win32 {
RC_ICONS = logo.ico
}
But the problem that I need different icons for different configuration.
I've tried:
contains(DEFINES, CONF1) {
RC_ICONS = conf1.ico
}
else {
RC_ICONS = conf2.ico
}
but that doesn't work. It looks that contains works only for variables defined inside .pro file only.
So my question - how can I add different settings (icons in my case) for different configuration?

As far as I'm aware qmake can't evaluate variables set in the DEFINES list, but only qmake variables.
However, you can use a qmake variable to perform both tasks at the same time. Just assign the "conf" value to your variable, evaluate that variable to add it to the DEFINES list and then test its value using qmake functions (e.g. equals).
As an example:
Add the following to your additional qmake arguments (including quotes):
"MYCONF = CONF1"
Then use these directives in your .pro file:
DEFINES += $${MYCONF}
equals(MYCONF, "CONF1") {
RC_ICONS = conf1.ico
} else {
RC_ICONS = conf2.ico
}

Related

QMake: test functions do not work as expected

In QtCreator 4.2.0 I try to use one *.pro file for building binaries for multiple hardware configurations.
In Build & Run => Build Settings => Build Enviroment I define the enviroment variable TARGET like as follows:
Build Settings A: Variable TARGET has Value bbb
Build Settings B: Variable TARGET has Value desktop
In the pro-file I use the following test functions:
equals($$TARGET,"bbb")
{
message("setting include paths for bbb"))
message($$TARGET)
}
equals($$TARGET,"laptop")
{
message("setting include paths for laptop.")
message($$TARGET)
}
contains($$TARGET,"*bbb*")
{
message("setting include paths for bbb"))
message($$TARGET)
}
contains($$TARGET,"*laptop*")
{
message("setting include paths for laptop.")
message($$TARGET)
}
And I get this output when running qmake:
Project MESSAGE: setting include paths for bbb
Project MESSAGE: bbb
Project MESSAGE: setting include paths for laptop.
Project MESSAGE: bbb
Project MESSAGE: setting include paths for bbb
Project MESSAGE: bbb
Project MESSAGE: setting include paths for laptop.
Project MESSAGE: bbb
Project MESSAGE: setting include paths for bbb
This makes no sense to me an I can't figure what I'm doing wrong here. Why are the parts after testing fro laptop executed?
By the way, I solved my problem by using Scopes. This works perfect for me:
CONFIG += $$(TARGET_HW)
desktop {
message("setting include paths for laptop.")
}
cetec {
message("setting include paths for cetec."))
}
But I'm still interested in the correct way of using test functions.
I provide the correct syntax for the first test, as an example:
equals(TARGET,"bbb") {
message("setting include paths for bbb"))
message($$TARGET)
}
Please notice:
The curly brace is on the same line of the test.
The variable tested has no dollar signs, just the variable name
The opening brace must written on the same line as the condition (https://doc.qt.io/qt-5/qmake-language.html#scope-syntax).
There are so many issues in your question, that there is place for another answer, adding to the previous correct ones:
as #daru says, you need to open the brace in the same line as the test function.
as #p-a-o-l-o says, contains and equals syntax require variable names as first arguments, without $$.
TARGET is an internal variable, that contains the base name of the project file by default. It becomes the name of the executable or library you are building.
You may use an environment variable named TARGET, but then you should assign it to a qmake variable name with another name.
sample code:
TGT=$$(TARGET)
equals(TGT,"bbb") {
message("$$TGT equals bbb"))
message(TGT=$$TGT)
}
equals(TGT,"laptop") {
message("$$TGT equals laptop")
message(TGT=$$TGT)
}
contains(TGT,"bbb") {
message("$$TGT contains bbb"))
message(TGT=$$TGT)
}
contains(TGT,"top") {
message("$$TGT contains top")
message(TGT=$$TGT)
}

premake5 precompiled headers not working

(this is using Premake5 alpha binary available for download on website)
I'm trying to port my existing VS solution over to using premake5.
It uses MS style precompiled headers(stdafx.h/stdafx.cpp).
When I specify this is my test project:
pchheader "stdafx.h"
pchsource "stdafx.cpp"
It does set the project to using precompiled headers, but it is not setting stdafx.cpp to generate precompiled headers(/Yc). Instead all the files in the project are trying to use(/Yu) and nobody is generating the PCH. So it does not build..
I'm guessing this does works somehow, what black magic am I missing here?
Here is my entire premake5 file for reference
-- premake5.lua
solution "Cloud"
configurations { "Debug", "Release", "Final" }
platforms { "Win32_AVX2", "Win64_AVX2"}
location "premake"
flags{"MultiProcessorCompile", "ExtraWarnings", "FatalCompileWarnings", "FatalLinkWarnings", "FloatFast"}
startproject "Cloud"
vectorextensions "AVX2"
filter { "platforms:Win32" }
system "Windows"
architecture "x32"
filter { "platforms:Win64" }
system "Windows"
architecture "x64"
filter "configurations:Debug"
defines { "DEBUG" }
flags { "Symbols" }
filter "configurations:Release"
defines { "NDEBUG" }
flags{"Symbols"}
optimize "Speed"
filter "configurations:Final"
defines { "NDEBUG" }
flags{"LinkTimeOptimization"}
optimize "Speed"
group "app"
--primary executable
project "Cloud"
location "../src_test/cloud"
kind "ConsoleApp"
language "C++"
targetdir "..//%{cfg.buildcfg}"
pchheader "stdafx.h"
pchsource "stdafx.cpp"
vpaths{
{["src/pch/*"] = "../src_test/cloud/stdafx.*"},
{["src/*"] = "../src_test/cloud/**.cpp"},
{["module/*"] = "../src_test/cloud/Module*.h"},
{["core/*"] = "../src_test/cloud/Core*.h"},
{["headers*"] = "../src_test/cloud/*.h"},
--{["src_c/*"] = "../src_test/cloud/**.c"}
}
files { "../src_test/cloud/*.h", "../src_test/cloud/*.c", "../src_test/cloud/*.cpp", "../src_test/cloud/*.hpp" }
One related question: how do I disable precompiled header usage on specific files within a project? Some of my files will not build if the PCH is included, so I have manually disabled them in the existing solution/projects.
Thanks!
It's probably because pchsource requires a file path (like files) Since your stdafx.cpp is not in the same directory as your script, Premake does not find it. Try using pchsource "../src_test/cloud/stdafxcpp" instead, it should fix the problem.
Also I see that you don't add "../src_test/cloud/" as an include directory, so that means that your pch header will be included using relative path, right ? If so, you'll need to update pchheader to reflect that. Due to the way Visual Studio works, you need to set pchheader as it appears in your cpp files. E.g. if in your cpp files you have #include "../src_test/cloud/stdafx.h" you need to use this in Premake: pchheader "../src_test/cloud/stdafx.h".
And finally, to deactivate precompiled headers on certain files, you can use filters:
-- deactivate precompiled headers for C files
filter "files:**.c"
flags { "NoPCH" }
I am changing a script from premake 4 to premake 5 and I had some troubles to deactivate pch on a specific file with :
filter "files:myfile.cpp"
flags { "NoPCH" }
It was disabling pch on the whole project and not on a specific file.
But it was because pchsource and pchheader were defined after the filtering.
When we flag a file with no PCH, pchsource and pchheader have to be defined first.

How to check the selected version of Qt in a .pro file?

I have multiple versions of Qt installed, and I need to compile my project with all of them.
Using a pro file, I could not find in the documentation how to do a conditional compilation.
Ideally, this is what I would like to do:
QT_VERSION = 5 # this can be 4, set manually
if(QT_VERSION == 5) {
QT += widgets
}
if(QT_VERSION == 4) {
QT += gui
}
Naturally, the if() command does not exist in pro files.
Is there a better way to do the same thing?
You can use conditional functions and scopes here:
QT_VERSION = 5 # this can be 4, set manually
equals(QT_VERSION, 5){
QT += widgets
}
equals(QT_VERSION, 4) {
QT += gui
}
However, there are a few things that you need to pay attention to in your original code:
Explicitly defining the Qt version is not necessary, and it can make you get a headache if you forgot to change that in the .pro file. Instead, qmake automatically defines a variable QT_MAJOR_VERSION for you.
Using equals will work in this case. However, as noted below, equals performs a string comparison. However, it is better to use greaterThan and lessThan because your code will automatically stop working when you try to compile it with Qt 6 (somewhere in the future).
Adding gui to the QT is not needed, as it is included by default.
So, your code should be:
greaterThan(QT_MAJOR_VERSION, 4) {
QT += widgets
}
Here are some undocumented qmake gems:
defined(func, type)
Returns true if func is defined; type must be either test or replace, to match defineTest or defineReplace.
equals(var1, var)
(also works as isEqual).
Returns true if var1 is equal to var2 (string comparison).
lessThan(var1, var2)`
Returns true if var1 is less than var2 (as an integer).
greaterThan(var1, var2)
Returns true if var1 is greater than var2 (as an integer).
inFile(file, var, val)
Returns true if a variable var is defined in the specified file. Additionally, it can test to see if it has the requested value.
load(string)
Something of a cross between include() and CONFIG += [feature]. load(foo) will look for a file called "foo.prf" in the standard feature path, and execute its contents immediately. Features that are contained within CONFIG are executed last, after the ".pro" file has finished processing. Like include(), it will return true if the file was found.
You can make checks in one line like this:
equals(QT_MAJOR_VERSION, 5):!lessThan(QT_MINOR_VERSION, 5) {
QT += bluetooth
} else {
message(Qt $$QT_VERSION Bluetooth not supported.)
}
!lessThan there stands for greater or equal.
Since Qt 5.10, there is versionAtLeast and versionAtMost test functions.
Usage example:
!versionAtLeast(QT_VERSION, 5.11.2):error("Use at least Qt version 5.11.2")
P.S.: Posting this answer, since simple googling "qmake check Qt version" doesn't brings these references (but this post does).
This is a simple test to do. This is what we have been doing in QtSerialPort and also some other modules inside the Qt framework:
lessThan(QT_MAJOR_VERSION, 5) {
...
} else {
...
}
Similar and common conditions are:
contains(QT_MAJOR_VERSION, 5): ...
or:
greaterThan(QT_MAJOR_VERSION, 4): ...
Here you can find another QtSerialPort example we have been doing in there.

Qt Creator and conditional build

In our project, we added some source and header files if a MACRO is defined. We do this like that, in the .pro file:
contains(DEFINES, MY_DEF) {
message("Support MY_DEF")
INCLUDEPATH += \
my_include_dir
SOURCES += \
source1.cpp \
source2.cpp
HEADERS += \
my_include_dir/header1.h \
my_include_dir/header2.h
FORMS += \
myform.ui
}
This works fine during the build. The files are not compiled if MY_DEF is not defined. MY_DEF is defined like that:
DEFINES += MY_DEF
Curiously, Qt Creator always display the files in the project tree, whereas MY_DEF is defined or not. If not defined, they are not used for the build, but they still are displayed and editable, searches can scan them, etc... Is it a bug of Qt Creator?
This is not a big issue, just a little annoying, because we don't know clearly if a file is part of the project or not.
It's intentional even. There's a special "accumulating" parsing mode to collect all files that are mentioned in .pro files (essentially the same that's used to collect "translatable strings") for display in the project tree. Otherwise things like "Replace in all files in a project" would yield different results depending on the platform or the context it is run in. [And it's not half of qmake that's included, but close to all of it...]
This seems to be an issue with QtCreator and how it reads the .pro files - it doesn't seem to actually fully parse the files, opting instead to just pick out certain bits. I've got the same issue with files that are only included on one platform or another - in QtCreator, they always show up.
I expect that the reason is either that they don't want to re-implement half of qmake just to get the file lists, OR that there are situations where trying to parse it 'correctly' would get the wrong answer, and they've chosen to be predictably wrong instead of randomly wrong.
In addition to the conditional includes in QMake, I add #ifdef around such conditional source code. That way I also see it visually drop out of compilation when the conditions are not met. It's not as good as having the files drop out entirely from the project tree, but it's better than allowing them to still appear like they are part of the build when editing them if they are not applicable.
Just for the sake of completeness and answer correctness. Probably someone else needs this example of root .pro file with conditional source tree:
TEMPLATE = subdirs
SUBDIRS = device
CONFIG -= debug_and_release
_SANDBOX_DIR = $$dirname(PWD)
_PLAYER_PRO = $${_SANDBOX_DIR}/player/player.pro
SUBDIRS = device
device.subdir = $${_SANDBOX_DIR}/proxy/libproxy
contains(QMAKE_PLATFORM, android) {
unset(_PLAYER_PRO)
} else {
SUBDIRS += player
player.file = $${_PLAYER_PRO}
player.depends = device
}
SUBDIRS += app
app.subdir = $${_SANDBOX_DIR}/display/display
app.depends = device
contains(SUBDIRS, player) {
app.depends += player
}

how to define a config in pro file?

how to define a config in pro file ?
by default, we have two config, debug and release. I want to add 2 other config but not in pro.user ! in pro file.
Your question is a bit unclear. It sounds like maybe you're currently building with "debug" and "release", from the command line, and you want to add your own build variants similar to that.
If that's the case... the mechanism for this is addExclusiveBuilds. Here is an example. I wouldn't recommend to mess around with it if you aren't comfortable reading qmake code.
TEMPLATE = app
SOURCES = main.cpp
# Adds two build variants.
# One of them builds the app with optimal compiler flags,
# the other one builds the app with support for collecting coverage data.
# For the first one, CONFIG will contain `optimized' and a Makefile.Optimized will be generated.
# For the second, CONFIG will contain `coverage' and a Makefile.Coverage will be generated.
# There will also be a top-level Makefile which invokes both the sub-makefiles.
addExclusiveBuilds(optimized, Optimized, coverage, Coverage)
CONFIG(optimized, coverage|optimized) {
message(I am in the optimized build variant)
QMAKE_CXXFLAGS += -O3
TARGET = myapp-optimized
}
else:CONFIG(coverage, coverage|optimized) {
message(I am in the coverage build variant)
QMAKE_CXXFLAGS += --coverage
QMAKE_LFLAGS += --coverage
TARGET = myapp-coverage
}
else {
message(I am in the glue project which contains the build variants)
# This will cause a `make' to build both optimized and coverage
# variants by default.
CONFIG += build_all
}
If I understand what you are saying, you add what you want to the CONFIG variable:
CONFIG += user_setting
...
user_setting: message( "compiling with user_setting" )
See the qmake manual where it talks about the CONFIG variable, especially near the end of the section.

Resources