How to get the actual qmake build destination within the project file? - qt

I'm trying to determine the build directory in the qmake project file, but failed in all my experiments so far :-(
At first I had a very plain foo.pro as QtCreator generates it for a plain Qt5 gui app with a few source files. Then I added an EXTRA_BINFILES list with some data files. They must be copied in the same directory as the executable foo. Without the copy stuff, it looks like this:
QT += core gui xml webkitwidgets widgets
TARGET = foo
TEMPLATE = app
EXTRA_BINFILES += \
foobar.png \
baz.png
SOURCES += \
main.cpp \
# ...
HEADERS += \
# ...
FORMS += \
# ...
When I build that, I get the foo executable (or foo.exe if you want). Mostly straightforward so far. Now I want to copy the EXTRA_BINFILES alongside this executable. The open question is how to get the destination directory. My best idea so far is adding this:
for(FILE, EXTRA_BINFILES) {
QMAKE_POST_LINK += $$quote($${QMAKE_COPY} $$shell_path($${PWD}/$${FILE}) $$shell_path($${OUT_PWD})$$escape_expand(\n\t))
}
This uses the OUT_PWD variable, which automatically points to where the Makefile is generated. This is nice for some scenarios. However, I have to deal with two different scenarios:
Directly compiling from within QtCreator with mostly out-of-the-box build configs. It creates a new build-foo-desktop-release directory, creates the Makefile there and builds the executable there. In this scenario, everything works fine.
Building from command-line with qmake -makefile /my/projects/foo/foo.pro and make in a temporary fresh build directory. This way it creates the Makefile in directly in that build directory but compiles the executable into a release subdirectory`. This obviously breaks my copy code.
For some reasons, it is not an option to get rid of one of those scenarios. I have to deal with both of them within the same project file. It is also not an option to make very technical/tricky things in the project file. It has to remain mostly as 'straightforward' as it is. Overriding some of qmake's own variables in the qmake command-line call is also probably not an option. This is because of the broader context, which is too extensive to explain here.
Is there an option to get the correct path in both scenarios? Something like OUT_PWD but for the executable itself?
Unfortunately, DESTDIR is empty (and as mentioned, it is not an option to forcefully set it). DESTDIR_TARGET is empty as well (otherwise I could combine it with dirname, which would be barely non-tricky enough).
Any hints?

sub_dir = $$_PRO_FILE_PWD_
sub_dir ~= s,^$$re_escape($$PWD),,
PROJECT_BUILD_TREE = $$clean_path($$OUT_PWD)
PROJECT_BUILD_TREE ~= s,$$re_escape($$sub_dir)$,,

Related

Automatically rebuild dependencies in Qt Creator

Qt Creator (4.6.1) is driving me nuts. My application is split into 3 parts:
the app
the library
a unit tests app
When I change a file within the library and rebuild the application, the compiler does not recompile the library but links with the old version of the library.
Also, when I change the library, recompile it and then compile the app, no compilation takes place because it uses the cached app.
Is there a setting to change that? Here's my project file:
TEMPLATE = subdirs
SUBDIRS += \
app \
lib_mylib \
tests
app.depends = lib_mylib
tests.depends = lib_mylib
The lib is built as a static library:
TEMPLATE = lib
TARGET = mylib
CONFIG += staticlib
I know it's a little late, but I would like to give an a little more extensive answer why this happens and how exactly the other solutions help.
A working solution will be: You use either b.depends += a as you did before or CONFIG += ordered and add PRE_TARGETDEPS += ... to b. (Side note: ordered is not recommended, as it can drastically slow down your builds and is generally considered bad practice)
TL;DR: The reason why this special combination is needed: The app.depends = lib_mylib in the subdirs project ensure that the library is always built before building the app is started, and the PRE_TARGETDEPS ensures that the app is actually beeing rebuild everytime the library changed.
Long explanation:
To understand why that works, we need to understand how qmake handles subdirs. qmake is a Makefile generator, which means it will only create makefiles. So all dependency ordering must be done using the methods make prodives. To understand what happens, we must understand how make works, first.
In make, dependencies are relatively simple:
some_target: dep1 dep2 dep3
some_command
means that if you want to create some_target, make will create dep1, dep2 and dep3 first, in an unspecified order. Once all 3 are finished, some_command is executed.
However, make will optimize this for files. Considering the following:
hello.txt:
echo "creating hello"
echo "hello" > hello.txt
hello2.txt: hello.txt
echo "creating hello2"
echo "hello2" > hello2.txt
Running make will create the both files and print both messages. Running it a second time will do nothing. The reason here is that make keeps tracks of already created files and file changes. Since hello.txt already exists, it is not created again. And since hello.txt has not changed, there is no need to create hello2.txt again. If you now externally change the contents of hello.txt and run make again, hello2.txt will be created anew and you will see the message.
Now with subdirs projects, this gets a little more complicated, as we now need dependencies between multiple different makefiles! This is typically resolved by recursive make calls. For your example, qmake creates the following code (simplified):
lib_mylib: FORCE
$(MAKE) lib_mylib/Makefile
app: lib_mylib FORCE
$(MAKE) app/Makefile
This code will, as expected, create lib_mylib first (blocking, i.e. the lib_mylib will only finish once the whole lib has been built) and after that create app. The FORCE depenency ensures that this command is always run, even if the target already exists.
With those basics in mind, we can now reconstruct what happens for qmake. Using the b.depends += a will generate code as above - it makes shure that all dependencies are built in the correct order, but nothing else! Using the ordered config will simply automatically create those depends rules, so there is no logical difference in how they work.
However, this is not enough to actually rebuild app when lib_mylib changes. It only ensures that lib_mylib is built before make starts building app.
In order to rebuild app, we use the PRE_TARGETDEPS - this adds a dependency as shown before to the make target in the apps makefile
app.exe: mylib.lib:
#linker code
This means that every time lib_mylib changes, app is now rebuilt as well. Using this without the ordered config however can fail, as it is possible that make would first try to build app (which either does nothing, as lib has not changed, or will fail if lib does not already exists) and after that rebuild lib_mylib. Running make a second time will the rebuild app as well - but thats rather inconvenient.
So, thats why we need to combine those two. We need to control the order in which the different makefiles are executed and references the created artifacts from the other makefile - and thats exactly what those commands do.
I have used CONFIG += ordered, DEPENDPATH and PRE_TARGETDEPS to get rid of the same problems. It works for me on linux and on win with MSVC. Try it.
in your project pro file add:
CONFIG += ordered
P.S.: your lib should be listed first. Like :
SUBDIRS += \
lib \
app \
tests
in your exe .pro file add this with correct paths:
DEPENDPATH += $$PWD/../lib
PRE_TARGETDEPS += $$OUT_PWD/../lib/liblib.a
More options and flags is to be found here
Regardless the long and understandable explanation I've tried with
TEMPLATE = subdirs
CONFIG += ordered
SUBDIRS += \
dynamiclib \
staticlib \
testlibs
for my rather small and short project and it worked for me.

Unable to customise the build directory for Qt Creator/qmake

I've got problem trying to specify the build directory (the directory that is to store all the files prior to copying them to the DESTDIR path).
I've got the following values in my .pro file:
DESTDIR = E:/Development/project/build/core/debug
OUT_PWD = E:/Development/project/build/core/debug
OBJECTS_DIR = $$DESTDIR/.obj
MOC_DIR = $$DESTDIR/.moc
RCC_DIR = $$DESTDIR/.qrc
UI_DIR = $$DESTDIR/.ui
Now, all the files eventually end up in that location, however during build, the compiler is always using the "E:/Development/build/MinGW_32bit-Debug/src/core" folder (note the missing project path). This is annoying, because I want to use the /Project/build directory as this location (which is not tracked in my git repo).
Ideally, I'd like this path to be: E:\Development\project\build\src\core\debug.
The reason I want to do this is that the build process has the same location to include the compiled libs from (it's a subdirs project).
I've had a look in the Tools > Options > Build & Run > General settings, and the default build directory is: build/build-%{CurrentProject:Name}-%{CurrentKit:FileSystemName}-%{CurrentBuild:Name}
I've had a look in my project.pro.user file, and found the following line:
<value type="QString" key="ProjectExplorer.BuildConfiguration.BuildDirectory">E:/Development/build/MinGW_32bit-Debug</value>
However I'm unable to change this value. If I edit this line in the file directly, as soon as I open Qt Creator again, the change has reverted back.
Is this a Qt Creator thing, or is it a qmake thing? Would I better off using a different build system such as CMake?
The build directory is "specified" by starting qmake or cmake in the build directory. There's no point to setting it in the .pro file itself.
Qt Creator stores the build directories for a project in the .user file. Any changes made to this file outside of Qt Creator, while the project is open in the Creator, will be lost. Creator loads the file when opening the project, or creates a new one if it doesn't exist.
When the Creator starts the build by invoking qmake or cmake, it starts that process in the build directory. That's also how you should be building the project manually from the command line.
Finally, it makes very little sense to override the destinations of the intermediate build results. They are somewhere within the build directory, and that's all that matters. You're not using these files directly for anything anyway.
The customary way to build a qmake project:
mkdir project-build
cd project-build
qmake ~/project-src
make -j
The build folder should not be within the source tree!
I've recently started keeping them in $TEMP / %TEMP%: manually purging the stale builds of all sort of test projects got old after a while :)

How to use qmllint in Qt Creator?

qmllint is a syntax checker for QML files written by KDAB which is shipped as a plugin with Qt 5.4. It's usage is based on command line like:
$ qmllint myFile.qml
Is it possible to use it directly in Qt Creator?
QtCreator
You can actually set custom commands in QtCreator to be run without cluttering your qmake file manually because that will effect all the other people in your project, too.
So, if you want to make sure that you only do it for yourself and not clutter it for others, using QtCreator's shiny GUI, I would suggest to follow this:
Projects (left pane)
Build & Run
Build Steps
Add Build Step
Here is the screen how exactly you can set up the command with the corresponding arguments:
With QtCreator's GUI, you can easily change the order with the same concept without touching your project file should you prefer that. There are use cases for that like:
You would not want to run any steps, not even qmake, before the qml file is properly validated
You only have C++ files, so there is no such a thing as "linkage".
etc.
qmake
There are other "generic" approaches useful outside QtCreator, although you asked about this IDE, like putting the command into variables like:
QMAKE_PRE_LINK
QMAKE_PRE_LINK = qmllint $$PWD/path/to/myFile.qml
QMAKE_POST_LINK
QMAKE_POST_LINK = qmllint $$PWD/path/to/myFile.qml
System command execution from your qmake project file
system("qmllint $$PWD/path/to/myFile.ml")
Adding custom targets with QMAKE_EXTRA_TARGETS
qmllinttarget.commands = qmllint $$PWD/path/to/myFile.qml
QMAKE_EXTRA_TARGETS += qmllinttarget
I believe the point in this question is not to have a check for a single file with known name, but to run qmllint on all qml files of a project. Ideally this should be done before building anything, because a build with erroneous qml files is likely to have no real value.
Extending lpapp's answer and playing around with qmake a bit, I came to this solution:
ALL_PWD_QML_FILES = $$files($${_PRO_FILE_PWD_}/*.qml , true)
# a command that creates an empty file with a given name.
win32 {
MY_TOUCH_CMD = copy NUL
} else {
MY_TOUCH_CMD = touch
}
qmllint.output = .qmllint/${QMAKE_FILE_BASE}.qmllint
qmllint.input = ALL_PWD_QML_FILES
qmllint.commands = qmllint ${QMAKE_FILE_NAME} && $${MY_TOUCH_CMD} ${QMAKE_FILE_OUT}
qmllint.CONFIG += no_link recursive target_predeps
QMAKE_EXTRA_COMPILERS += qmllint
This assumes that all qml files are either in the same directory as the .pro file or in subdirectories.
It will run qmllint on all qml files before the actual build, but only if any qml file has changed since a previous build.
Tested on Windows with Qt 5.11 and MSVC.
You can use QMAKE_POST_LINK variable in your .pro file like :
QMAKE_POST_LINK = qmllint $$PWD/QMLFiles/myFile.qml
This runs qmllint on your QML file when you build your project.

What the right variable to use here, that represents the TARGET field in the .pro file

I would like to add a custom command, that will work on the generated binary file (The target field in *.pro file),
But what should I use here, in the Command arguments
I'm afraid this is not possible. QtCreator only handles source and build directory. The QtCreator documentation says:
The following Qt Creator variables are available:
%{buildDir}
%{sourceDir}
Note that the target even doesn't have to be in the build directory. The build directory is where qmake is ran, typically resulting in the target being put there, because in the .pro file one typically specifies TARGET = projectName.
Further note that the QtCreator build steps configuration only works within QtCreator. This should not be used when your custom build steps are needed for other people working without QtCreator (they should only run qmake and make to build your application).
This being said and assuming that you want to define a post-build step, you should look for a solution to define such in the .pro file (by using the $${TARGET} variable) so that qmake will put your buildstep into the Makefile after the linking step.
If you want to execute a command after linkage, let's say call a custom script (batch script on Windows, otherwise a bourne shell script) with the TARGET as an argument, add the following to your .pro file:
win32 {
poststep.commands = #myScript.bat $${TARGET}
}
!win32 {
poststep.commands = #./myScript.sh $${TARGET}
}
QMAKE_EXTRA_TARGETS += poststep

Codeblocks with Qt - something terribly wrong

After creating qt project in codeblocks and running it I'm getting:
Anyone knows how to resolve it?
Thanks
This looks to me like you're building against one version of Qt and linking against another at runtime. Run the QtSDK Maintenance Tool and remove any versions of Qt Desktop that you don't need. You may then need to repoint Codeblocks at the correct headers.
I'm guessing that if you're running from within Codeblocks, you've had to explicitly specify which dlls to use when you run your newly built app. If so, make sure that those are the correct versions (i.e. replace them with dlls from QT INSTALL DIR\Desktop\4.7.x\mingw\bin [though I'm not on my work PC at the moment, so this path may be slightly wrong. Just make sure you're in the correct 4.7.x folder]).
To be honest though, if you're running from Windows, why not use QtCreator? Aside from slightly lacking in terms of GDB integration, it's pretty good and you'd find problems like this are harder to come across.
This is (esp on Windows) a common problem. When installing the Qt SDK, you'll get at least 2 .dll's with the same name but in different versions. It happens, that you link against the intended (dev-)lib but at runtime the version from the Designer/Creator is used.
The easiest way to avoid this, is to deploy the right version of the dll's together with your binaries (.exe and stuff) in a separate folder. This can be achived by modifying your build script. It depends on your build system which is usually qmake/.pro or cmake/CMakeLists.txt.
As for CMake, given an environment variable MYQTDLLDIR containing the path to the files to be deployed you can use something like that:
configure_file($ENV{MYQTDLLDIR}/QtCore4.dll ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
configure_file($ENV{MYQTDLLDIR}/QtGui4.dll ${CMAKE_CURRENT_BINARY_DIR} COPYONLY)
documentation stripped from cmake --help-full:
configure_file Copy a file to another location and modify its
contents.
configure_file(
[COPYONLY] [ESCAPE_QUOTES] [#ONLY])
Copies a file to file and substitutes variable
values referenced in the file content. If is a relative
path it is evaluated with respect to the current source directory.
The must be a file, not a directory. If is a
relative path it is evaluated with respect to the current binary
directory. If names an existing directory the input file
is placed in that directory with its original name.
This command replaces any variables in the input file referenced as
${VAR} or #VAR# with their values as determined by CMake. If a
variable is not defined, it will be replaced with nothing. If
COPYONLY is specified, then no variable expansion will take place. If
ESCAPE_QUOTES is specified then any substituted quotes will be C-style
escaped. The file will be configured with the current values of CMake
variables. If #ONLY is specified, only variables of the form #VAR#
will be replaces and ${VAR} will be ignored. This is useful for
configuring scripts that use ${VAR}. Any occurrences of #cmakedefine
VAR will be replaced with either #define VAR or /* #undef VAR */
depending on the setting of VAR in CMake. Any occurrences of
#cmakedefine01 VAR will be replaced with either #define VAR 1 or
#define VAR 0 >depending on whether VAR evaluates to TRUE or FALSE in
CMake
As for qmake you could use INSTALLS (used when make install is called) or execute a "plain command" after linking. Using INSTALLS:
mytarget.path = /output/path
mytarget.files += /path/to/QtCore4.dll
mytarget.files += /path/to/QtGui4.dll
INSTALLS += mytarget
qmake using command execution:
win32 {
EXTRA_BINFILES += \
$${MYQTDLLDIR}/QtCore4.dll \
$${MYQTDLLDIR}/QtGui4.dll
EXTRA_BINFILES_WIN = $${EXTRA_BINFILES}
EXTRA_BINFILES_WIN ~= s,/,\\,g
DESTDIR_WIN = $${DESTDIR}
DESTDIR_WIN ~= s,/,\\,g
for(FILE,EXTRA_BINFILES_WIN){
QMAKE_POST_LINK +=$$quote(cmd /c copy /y $${FILE} $${DESTDIR_WIN}$$escape_expand(\n\t))
}
}

Resources