PDA

View Full Version : qmake generates bogus Makefiles



dave2
23rd September 2015, 14:19
Hi,

I use Qt 4.7.3 on Linux, and I am facing weird qmake behavior. I have tried to narrow the problem down to the following minimal example:

I have a directory, qmake_example, which contains two files:


$ cat qmake_example.pro
CONFIG += debug_and_release

release: DESTDIR = release
debug: DESTDIR = debug

SOURCES += main.cpp

$ cat main.cpp
int main()
{
return 0;
}


As you can see:
1. I want to be able to build the project in debug and/or release mode.
2. I want to keep the generated binaries in build subdirectories separate from the source directory.

First I run qmake, which generates the expected Makefiles and build subdirectories:


$ ls
main.cpp qmake_example.pro
$ qmake
$ ls
debug Makefile Makefile.Release release
main.cpp Makefile.Debug qmake_example.pro


Then I run make to generate both debug and release builds:


$ make debug release # I also tried the equivalent 'make all'
<g++'s output lines, no warnings, no errors>


But then, surprise: the binary is generated only for debug:


$ ls debug/
main.o qmake_example
$ ls release/
main.o


The problem is in Makefile.Release, which contains (among other lines):


# Makefile for building: debug/qmake_example
[more lines]
DESTDIR = debug/
TARGET = debug/qmake_example
[more lines]
@$(CHK_DIR_EXISTS) debug/ || $(MKDIR) debug/
[more lines]


What am I doing wrong? How should I write my .pro file / invke qmake in order to have Makefile.Release refer to directory release instead of debug?

jefftee
24th September 2015, 00:52
Here's what I use in my qmake pro files and works for me:


CONFIG(debug, debug|release) {
DESTDIR = build/debug
} else {
DESTDIR = build/release
}

dave2
24th September 2015, 09:05
Thank you for your suggestion, which helped me discover the solution through a few quirks:

I change qmake_example.pro to:



$ cat qmake_example.pro
CONFIG += debug_and_release

CONFIG(debug, debug|release) {
DESTDIR = build/debug
} else {
DESTDIR = build/release
}

SOURCES += main.cpp

and start from a clean directory:


$ ls
main.cpp qmake_example.pro

Then:


$ qmake
$ ls
build main.cpp Makefile.Debug qmake_example.pro
debug Makefile Makefile.Release release
$ ls build/
debug release

First surprise: in addition to the expected build/debug and build/release, qmake also created directories ./debug and ./release.

Then:


$ make debug release
<g++'s output lines, no warnings, no errors>
$ ls build/debug/
qmake_example
$ ls build/release/
qmake_example
$ ls debug/
main.o
$ ls release/
main.o

Second surprise: the executable binaries did go to build/debug and build/release, but the object files went to ./debug and ./release!

New experiment, this time specifying OBJECTS_DIR:


$ cat qmake_example.pro
CONFIG += debug_and_release

CONFIG(debug, debug|release) {
DESTDIR = build/debug
} else {
DESTDIR = build/release
}

OBJECTS_DIR = $$DESTDIR

SOURCES += main.cpp

Starting from a clean state:


$ ls
main.cpp qmake_example.pro

qmake still creates directories build/debug, build/release, ./debug, and ./release:


$ qmake
$ ls
build main.cpp Makefile.Debug qmake_example.pro
debug Makefile Makefile.Release release

but this time at least the object files go to their correct directories:


$ make debug release
<g++'s output lines, no warnings, no errors>
$ ls build/debug/
main.o qmake_example
$ ls build/release/
main.o qmake_example
$ ls debug/
$ ls release/

Only remaining problem: two useless-but-still-created-by-qmake directories, ./debug and ./release.

But now, eliminating the intermediate directory ./build in DESTDIR:


$ cat qmake_example.pro
CONFIG += debug_and_release

CONFIG(debug, debug|release) {
DESTDIR = debug
} else {
DESTDIR = release
}

SOURCES += main.cpp

solves the problem:


$ ls
main.cpp qmake_example.pro
$ qmake
$ ls
debug Makefile Makefile.Release release
main.cpp Makefile.Debug qmake_example.pro
$ make debug release
<g++'s output lines, no warnings, no errors>
$ ls debug/
main.o qmake_example
$ ls release/
main.o qmake_example

The executable binary files and the object files finally go to their intended directories, and no extra directories are created.

Thank you again for pointing me in the right direction. That kind of experience, however, makes me doubt about qmake's quality.

Thank you for your suggestion, which helped me discover the solution through a few quirks:

I change qmake_example.pro to:



$ cat qmake_example.pro
CONFIG += debug_and_release

CONFIG(debug, debug|release) {
DESTDIR = build/debug
} else {
DESTDIR = build/release
}

SOURCES += main.cpp

and start from a clean directory:


$ ls
main.cpp qmake_example.pro

Then:


$ qmake
$ ls
build main.cpp Makefile.Debug qmake_example.pro
debug Makefile Makefile.Release release
$ ls build/
debug release

First surprise: in addition to the expected build/debug and build/release, qmake also created directories ./debug and ./release.

Then:


$ make debug release
<g++'s output lines, no warnings, no errors>
$ ls build/debug/
qmake_example
$ ls build/release/
qmake_example
$ ls debug/
main.o
$ ls release/
main.o

Second surprise: the executable binaries did go to build/debug and build/release, but the object files went to ./debug and ./release!

New experiment, this time specifying OBJECTS_DIR:


$ cat qmake_example.pro
CONFIG += debug_and_release

CONFIG(debug, debug|release) {
DESTDIR = build/debug
} else {
DESTDIR = build/release
}

OBJECTS_DIR = $$DESTDIR

SOURCES += main.cpp

Starting from a clean state:


$ ls
main.cpp qmake_example.pro

qmake still creates directories build/debug, build/release, ./debug, and ./release:


$ qmake
$ ls
build main.cpp Makefile.Debug qmake_example.pro
debug Makefile Makefile.Release release

but this time at least the object files go to their correct directories:


$ make debug release
<g++'s output lines, no warnings, no errors>
$ ls build/debug/
main.o qmake_example
$ ls build/release/
main.o qmake_example
$ ls debug/
$ ls release/

Only remaining problem: two useless-but-still-created-by-qmake directories, ./debug and ./release.

But now, eliminating the intermediate directory ./build in DESTDIR:


$ cat qmake_example.pro
CONFIG += debug_and_release

CONFIG(debug, debug|release) {
DESTDIR = debug
} else {
DESTDIR = release
}

SOURCES += main.cpp

solves the problem:


$ ls
main.cpp qmake_example.pro
$ qmake
$ ls
debug Makefile Makefile.Release release
main.cpp Makefile.Debug qmake_example.pro
$ make debug release
<g++'s output lines, no warnings, no errors>
$ ls debug/
main.o qmake_example
$ ls release/
main.o qmake_example

The executable binary files and the object files finally go to their intended directories, and no extra directories are created.

Thank you again for pointing me in the right direction. That kind of experience, however, makes me doubt about qmake's quality.

Thank you for your suggestion, which helped me discover the solution through a few quirks:

I change qmake_example.pro to:



$ cat qmake_example.pro
CONFIG += debug_and_release

CONFIG(debug, debug|release) {
DESTDIR = build/debug
} else {
DESTDIR = build/release
}

SOURCES += main.cpp

and start from a clean directory:


$ ls
main.cpp qmake_example.pro

Then:


$ qmake
$ ls
build main.cpp Makefile.Debug qmake_example.pro
debug Makefile Makefile.Release release
$ ls build/
debug release

First surprise: in addition to the expected build/debug and build/release, qmake also created directories ./debug and ./release.

Then:


$ make debug release
<g++'s output lines, no warnings, no errors>
$ ls build/debug/
qmake_example
$ ls build/release/
qmake_example
$ ls debug/
main.o
$ ls release/
main.o

Second surprise: the executable binaries did go to build/debug and build/release, but the object files went to ./debug and ./release!

New experiment, this time specifying OBJECTS_DIR:


$ cat qmake_example.pro
CONFIG += debug_and_release

CONFIG(debug, debug|release) {
DESTDIR = build/debug
} else {
DESTDIR = build/release
}

OBJECTS_DIR = $$DESTDIR

SOURCES += main.cpp

Starting from a clean state:


$ ls
main.cpp qmake_example.pro

qmake still creates directories build/debug, build/release, ./debug, and ./release:


$ qmake
$ ls
build main.cpp Makefile.Debug qmake_example.pro
debug Makefile Makefile.Release release

but this time at least the object files go to their correct directories:


$ make debug release
<g++'s output lines, no warnings, no errors>
$ ls build/debug/
main.o qmake_example
$ ls build/release/
main.o qmake_example
$ ls debug/
$ ls release/

Only remaining problem: two useless-but-still-created-by-qmake directories, ./debug and ./release.

But now, eliminating the intermediate directory ./build in DESTDIR:


$ cat qmake_example.pro
CONFIG += debug_and_release

CONFIG(debug, debug|release) {
DESTDIR = debug
} else {
DESTDIR = release
}

SOURCES += main.cpp

solves the problem:


$ ls
main.cpp qmake_example.pro
$ qmake
$ ls
debug Makefile Makefile.Release release
main.cpp Makefile.Debug qmake_example.pro
$ make debug release
<g++'s output lines, no warnings, no errors>
$ ls debug/
main.o qmake_example
$ ls release/
main.o qmake_example

The executable binary files and the object files finally go to their intended directories, and no extra directories are created.

Thank you again for pointing me in the right direction. That kind of experience, however, makes me doubt about qmake's quality.

Added after 52 minutes:

Latest news:

The example I posted was minimalistic. Our real project also generates moc_*.cpp files and ui_*.h files.

We just discovered that the moc_*.cpp files are generated in their correct directory (debug or release), but the ui_*.h files are generated in the top-level directory (where the source code resides).

Fortunately, adding this directive to our .pro file:


UI_DIR = $$DESTDIR

fixes the problem.

Several words are popping to my mind right now to describe qmake, but I am afraid the rules of this forum do not allow me to share my thoughts.

jefftee
24th September 2015, 09:46
Heh, I feel your pain. I also add the following directives to all of my qmake pro files that neatly tucks the generated "stuff" out of the way in the build directories:


OBJECTS_DIR = $${DESTDIR}/.generatedfiles
MOC_DIR = $${DESTDIR}/.generatedfiles
RCC_DIR = $${DESTDIR}/.generatedfiles
UI_DIR = $${DESTDIR}/.generatedfiles

Sorry for the time you spent figuring all that out on your own! Once you get a little more experience, I think you'll find qmake is pretty handy but certainly takes some getting used to... :)

anda_skoa
24th September 2015, 11:03
I usually just run qmake as many times as I need different built options.

E.g. create a directory for the debug build, run qmake from there with CONFIG += debug.
If I need a release build, same for release.
If I need a third build, again.

Makes it easy to have builds of various types and doesn't require to build all of them all the time. No point in wasting time building a version you are currently not needing anyway.

And likely using CMake :-)
QMake is really nice for simple, self contained, programs and has a really nice and easy to understand syntax.
Beginner friendly, but limited for advanced use.

Cheers,
_

d_stranz
25th September 2015, 23:39
Bootstrapping myself up into CMake competency at this very moment. I need to maintain a shared library that is compiled and deployed on Win32, Win64, linux, OSX, and Android ARM, along with test programs deployed on Windows. Prior to this, the Windows, linux, and Mac builds were all done on separate machines. Since CMake inherently supports cross-compilation, all except Mac can now be done under Windows. And when I find an OSX compiler that runs under windows, that will be added too.

But wow, what an overwhelming API to learn.

Infinity
28th September 2015, 00:19
I also prefer running qmake for each configuration.


Since CMake inherently supports cross-compilation
You can also do cross-compilation with QMake. I actually build all my Windows binaries under Arch Linux with mingw-w64.

But I think it's true, QMake is limited for advanced use. I'm currently having problems with adding a custom compiler to generate icon files when building under/for Windows. Maybe I will also switch to CMake but you say it, it has an overwhelming API.