Search code examples
qtqt5autotoolsmoc

How to use Qt moc in autotools project (in 2017)?


Trying to compile a basic Qt application (new to Qt) with autotools (which I've done many projects with) on Linux.

Everything seems to compile fine until the linking starts, and thats when I get the following results.

test-mainwindow.o: In function `MainWindow::MainWindow()':
mainwindow.cpp:5: undefined reference to `vtable for MainWindow'
mainwindow.cpp:5: undefined reference to `vtable for MainWindow'
mainwindow.o: In function `MainWindow::~MainWindow()':
mainwindow.cpp:11: undefined reference to `vtable for MainWindow'
mainwindow.cpp:11: undefined reference to `vtable for MainWindow'
mainwindow.o: In function `MainWindow::tr(char const*, char const*, int)':
mainwindow.h:10: undefined reference to `MainWindow::staticMetaObject'
collect2: error: ld returned 1 exit status
make[4]: *** [Makefile:415: test] Error 1

I've not done a Qt app before, so I suspect maybe something to do with the moc, or Q_OBJECT in my MainWindow header? Most all examples I could find online are 4+ years old.

Here's what the Makefile.am in my source dir looks like.

include $(top_srcdir)/common.am

bin_PROGRAMS = test

test_SOURCES =               \
    main.cpp                 \
    mainwindow.cpp

test_CPPFLAGS = -I/usr/include -I/usr/local/include -I$(QT5_INCL) -I$(QT5_INCL)/QtWidgets -I$(QT5_INCL)/QtCore

test_LDFLAGS = -L/usr/lib64 -L/usr/local/lib64 -L/usr/local/lib -L$(QT5_LIBS)

test_LDADD = -lrt -lQt5Core -lQt5Gui -lQt5Widgets

And if I append this to the Makefile.am, which I'm sure isn't fully correct at this point ...

moc-%.cpp: %.h
    /usr/lib64/qt5/bin/moc $(DEFINES) $(INCPATH) $< -o $@

moc-%.cc: %.h
    @MOC@ -o$@ $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(MOC_CPPFLAGS) $<

ui-%.h: %.ui
    @UIC@ -o $@ $<

qrc-%.cc: %.qrc
    @RCC@ -o $@ $<

Automake will complain w/ this before I even try to compile ...

Makefile.am:19: warning: '%'-style pattern rules are a GNU make extension
Makefile.am:24: warning: '%'-style pattern rules are a GNU make extension
Makefile.am:27: warning: '%'-style pattern rules are a GNU make extension
Makefile.am:30: warning: '%'-style pattern rules are a GNU make extension

Anyone have ideas about how to implement the moc part of things using autotools?


Solution

  • Automake does not automatically recognize that your program depends on unspecified sources that need to be created by processing other files with moc or other Qt tools. Your *_SOURCES variables need to name such derived sources explicitly, and you need to provide a literal make rule or rules for building them.

    You may furthermore need to name some of those in the value of the BUILT_SOURCES variable. C++ top-level source files are unlikely to need this treatment, but built header files (or any other sources named in an #include directive) very likely do need it.

    The details are somewhat up to you, and I cannot offer a specific solution because it's unclear what your exact needs are. But the desired Automake file might look something like this:

    include $(top_srcdir)/common.am
    
    bin_PROGRAMS = test
    
    # headers that must be processed with moc to generate C++ source files
    test_qtheaders = mainwindow.h
    
    # the source files that will be generated via moc.  The string "_moc" is inserted
    # to, hopefully, avoid name collisions
    test_moc_sources = $(test_qtheaders:.h=_moc.cpp)
    
    test_SOURCES =               \
        main.cpp                 \
        mainwindow.cpp           \
        $(test_moc_sources)
    
    test_CPPFLAGS = -I/usr/include -I/usr/local/include -I$(QT5_INCL) -I$(QT5_INCL)/QtWidgets -I$(QT5_INCL)/QtCore
    
    test_LDFLAGS = -L/usr/lib64 -L/usr/local/lib64 -L/usr/local/lib -L$(QT5_LIBS)
    
    test_LDADD = -lrt -lQt5Core -lQt5Gui -lQt5Widgets
    
    # A traditional-style Make suffix rule for generating *_moc.cpp files from *.h
    # files via moc.  For the former (especially) to be recognized as a suffix,
    # you need to tell Automake about it via the `SUFFIXES` variable.
    # The mkdir is present to support out-of-source building.
    # Assumes that the MOC variable will be set by configure.
    .h_moc.cpp:
        $(MKDIR_P) `dirname $@`
        $(MOC) -o $@ $(test_CPPFLAGS) $(CPPFLAGS) $<
    
    SUFFIXES = .h _moc.cpp
    

    You may also want to list the built sources among your CLEANFILES, else they will be left behind when you make clean.