qtmakefileautotoolsautomakemoc

How to create a custom suffix rule to use a header in another directory?


I have a Makefile.am like the following to compile MOC files for Qt using automake.

bin_PROGRAMS = test

test_qtheaders = window.h

test_moc_sources = $(test_qtheaders:.h=_moc.cpp)

test_SOURCES =               \
    main.cpp                 \
    main-window.cpp          \
    window.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

SUFFIXES = .h _moc.cpp

# this suffix rule is not finding the headers from src/includes/gui
.h_moc.cpp:
    $(QT_MOC) -o $@ $(test_CPPFLAGS) $(CPPFLAGS) $<

I get the following error.

make[4]: *** No rule to make target 'window_moc.cpp', needed by 'test-window_moc.o'.  Stop.

My directory structure is as follows:

myproj
|- configure.ac
|- Makefile.am
|- ...
` src
    |- Makefile.am
    |- includes
    |    `- gui 
    |        |- main-window.h
    |        `- window.h    // file required to generate *.cpp.
    |
    `- gui
        |- Makefile.am
        |- main.cpp
        |- main-window.cpp
        `- window.cpp   // output of MOC, does not yet exist.

If I place the main-window.h file in src/gui directory, the MOC will find it and build. Adding the src/includes/gui path to the test_CPPFLAGS doesn't do it. I also tried adding ../../includes/gui/$< to the command, but to no avail.

So my question is, how does one tell the suffix rule where to find a file if it's in an outside directory of the Makefile.am?


Solution

  • How to create a custom suffix rule to use a header in another directory?

    You can't. It is inherent in the nature of suffix rules that they apply to targets having a corresponding prerequisite in the same directory. It cannot be otherwise because make doesn't know anything about path structure. That is, although target and prerequisite names can contain directory components, make just treats them as flat strings (which nevertheless does the right thing when it passes such strings to the C library or interpolates them into shell commands).*

    In particular, this suffix rule ...

    .h_moc.cpp:
        $(QT_MOC) -o $@ $(test_CPPFLAGS) $(CPPFLAGS) $<
    

    ..., interpreted in light of your suffix declarations, describes how to build targets with names of the form <prefix>_moc.cpp from corresponding prerequisites named <same_prefix>.h. Supposing that the target you want to build is actually gui/window_moc.cpp (not gui/window.cpp as shown in the question), the relevant prefix would be gui/window. The corresponding prerequisite would then be gui/window.h, and that's simply not the same thing as include/gui/window.h.

    If you want to build targets in gui/ from sources in include/gui/, sticking to portable make syntax and features according to Autotools recommendations, then you will need to write ordinary target rules, not suffix rules:

    gui/window_moc.cpp: include/gui/window.h
        $(QT_MOC) -o $@ $(test_CPPFLAGS) $(CPPFLAGS) $<
    

    I presume that you want a suffix rule to avoid writing multiple regular target rules, and that's a perfectly reasonable idea. But it simply is not an option if you require the source and target layout described in the question.


    * The Autotools are largely irrelevant here, because the main thing they do with custom rules appearing in your Makefile.am is pass them through, unchanged, to the final makefile(s).