For some time I struggle with problem and work performance loss because of way how Qmake is working with expansive code base. Consider this minimal project structure which shares *class.h with other, possibly non-Qt projects:
THe Qt-unaffiliated header (can have some .cpp associated as well), moc utility ignores it because no QObjects exist here.
//Aclass.h:
struct A {
// some definitions that sometimes change for external reasons
int first;
// float new_field; // - possible change pulled from repository
double old_field;
};
Bclass.h might be Qt-controlled (moc-ed), might be not.
//Bclass.h:
#include "Aclass.h"
struct B : A {
// some definitions that sometimes change for external reasons
};
Qt controlled code:
//app.h
#include "Bclass.h"
#include <QApplication>
class QApp : public QApplication
{
Q_OBJECT
public:
QApp();
// some methods working with BClass instance
foo();
BClass b;
};
Implementation:
//app.cpp
#include "app.h"
QApp:::QApp (int & argc, char ** argv ) : QApplication (argc, argv ) {}
QApp::foo() { b.old_field = 0.0; }
Some external code that changes along with change in struct A
:
#include "app.h"
int main(int argc, char** argv) {
QApp app( argc, argv );
app.foo();
// app.b.new_field = 0.0f; // also update from repository
}
All .h files are included into .pro file, into HEADERS variable.
If project was already compiled and then struct A
would be changed, e.g. by checkout from repository of main.cpp
and Aclass.h
, the compilation of app.cpp
would not happen again.
An old app.o
would be linked to new main.o
. Which leads to UB - usually a heap corruption or values being written to wrong memory locations.
It happens because Qmake generates makefile in such way that targets for .o files are dependent only on their own header and of list of files processed by moc, i.e app.o
is dependant on app.cpp
, app.h
and, maybe, Bclass.h
.
Project's team avoid it by fully updating whole build tree, deleting makefile and recompiling whole project every time. Which get tedious with source code base grown to size around 1 GB and compile time measured in hours. That's especially bad when changes are introduced for debugging purposes, rebuild being skipped and a bug gets masked or exposed as false positive because of ODR breach.
It's an ODR breach, because main.o would use new definitions of struct A
and of QApp
types, while app.o would be compiled with old ones.
Is there an obscure way to instruct Qmake to include all files like Aclass.h
as dependencies, which doesn't involve a script to delete\update manually for each .o file?