Question

I have the following Makefile.am which is supposed to create foo.hdb and foo.cdb from foo.h (via the Python script):

TESTS = check_foo

check_PROGRAMS = check_foo

check_foo_SOURCES = check_foo.c $(top_builddir)/src/isti.h \
    foo.cdb foo.h foo.hdb
check_foo_CFLAGS = @CHECK_CFLAGS@ $(all_includes) -I../../clib/src/
check_foo_LDADD = $(top_builddir)/src/libcorm.la @CHECK_LIBS@ -lsqlite3

%.hdb %.cdb: %.h
    PYTHONPATH=$(top_builddir)/cgen/src python $(top_builddir)/cgen/src/isti/cgen/run.py $<

clean-local:
    rm -f *.hdb *.cdb

However, although make foo.hdb and make foo.cdb work (call the Python code and generates the foo.hdb and foo.cdb files from foo.h), make clean check (or the two separately) does not (missing foo.hdb - no such file) - the pattern rule is not called to generate foo.hdb from foo.h.

In other words: the pattern rule is not being called for the files listed in check_foo_SOURCES.

How can I make this work? The rest of the autotools infrastructure is working fine. From looking at the Makefile I suspect the issue is with how autotools expands the check sources.

This is all on Linux with Gnu make. Here is the Makefile.

[Updated slightly to reflect the help from MadScientist].

Later update

The following Makefile (just make, not autotools) works fine, so the issue seems to be related to autotools and check support.

all: check_foo

CFLAGS=-I../../clib/src
LDFLAGS=-L../../clib/src/.libs

check_foo: check_foo.c foo.h corm_foo.h corm_foo.c
    gcc $(CFLAGS) $(LDFLAGS) $^ -o $@ -lcorm -lsqlite3

corm_%.h corm_%.c: %.h
    PYTHONPATH=../../cgen/src python ../../cgen/src/isti/cgen/run.py $<

clean:
    rm -f corm_*.h corm_*.c
    rm -f *.o

(Note that I've switched from xxx.hdb to corm_xxx.h, etc, so that file extensions remain OK).

More Details

Since it seems to be related to the CHECK macros, this is configure.ac:

AC_INIT([corm], [0.1], [a.cooke@isti.com])
AC_CONFIG_MACRO_DIR([m4])
PKG_CHECK_MODULES([CHECK], [check >= 0.9.4]) 
AM_INIT_AUTOMAKE([-Wall foreign -Werror])
AC_PROG_CC_C99
AM_PROG_CC_C_O
LT_INIT
AC_CONFIG_HEADERS([config.h])
AC_CONFIG_FILES([Makefile clib/Makefile clib/src/Makefile clib/tests/Makefile clib/docs/Makefile cgen/Makefile cgen/docs/Makefile example/Makefile example/src/Makefile])
AC_CHECK_PROGS([DOXYGEN], [doxygen], AC_MSG_WARN([Doxygen not found - continuing without Doxygen support]))
AM_CONDITIONAL([HAVE_DOXYGEN], [test -n "$DOXYGEN"])
AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([clib/docs/Doxyfile cgen/docs/Doxyfile])])

SOLUTION

OK, so summarizing the various things below, there were two important issues (once I had fixed file extensions - see the "plain" makefile and fceller's answer), either one of which was sufficient to make things work:

  1. (The handling of) Header files is complicated. Because of auto-dependencies, programatically generated header files break things. The solution is to use BUILT_SOURCES

  2. But (the handling of) .c files is not complicated. So putting the corm_foo.c in front of check_foo.c would trigger the generation of that file. Since that also generates corm_foo.h, everything works (because check_foo.c now can include corm_foo.h).

Also, fceller has some good general points about tidy makefiles and explains why the "plain" makefile works.

Était-ce utile?

La solution 3

You need to tell automake that foo.hdb is a source file that is to be constructed. Add the following to Makefile.am:

BUILT_SOURCES = foo.hdb

Autres conseils

The line

%.cdb: %.hdb

does not do anything. Check the GNU make manual and you'll see that a pattern rule without a command line is used to DELETE a previously defined rule with that same pattern. Since there's no previous rule, this is essentially a no-op.

If you have a command that creates TWO output files with ONE invocation of a rule, then you need to put both patterns in the same rule, like this:

%.cdb %.hdb: %.h
        PYTHONPATH=$(top_builddir)/cgen/src python $(top_builddir)/cgen/src/isti/cgen/run.py $<

This will tell GNU make that both targets are generated from one invocation of the rule. BE AWARE! This syntax only has this behavior for pattern rules. Explicit rules with multiple targets do something entirely different (confusingly enough).

As for the make clean behavior, I'm not sure. The makefile generated by automake is too complex for me to just read it; it would require some debugging (run it and trace what happens). However, I suspect that the rules there are not designed properly to allow a clean target and a build target to both be run in the same invocation of make. Run the two commands separately and see if that works better:

make clean
make check

First of all: you do not need to include the "*.h" in *_SOURCES. The automake will generated code to generate the dependencies. From the manual: Header files listed in a _SOURCES definition will be included in the distribution but otherwise ignored

The change you made in the plain Makefile ("Note that I've switched from xxx.hdb to corm_xxx.h, etc, so that file extensions remain OK") is essential. The automake FILTERS the *_SOURCES list using the filename extension in order to see what to call (CC, CXX, F77).

The following Makefile.am will work:

TEST = check_foo

check_PROGRAMS = check_foo

check_foo_SOURCES = check_foo.c foo.db.c
check_foo_CFLAGS = 
check_foo_LDADD = 

%.db.c %.db.h: %.h
    echo "int i = 1;" > foo.db.c
    echo "int j;" > foo.db.h

clean-local:
    rm -f *.db.h *.db.c

Let me try to add an indirect answer/discussion to the existing direct ones.

I recently moved away from make for the same kind of reasons you experienced: it is extremely powerful but sometimes a bit difficult to debug when things do not go as expected.

I recently discovered pydoit which is very promising as a debuggable replacement build tool for make. Since the notion of "pattern rules" was not present in it, I proposed an independent package to do the job: fprules.

This is how you would perform the same task that you mention in your post, with doit and fprules:

from fprules import file_pattern

# all: check_foo
DOIT_CONFIG = {'default_tasks': ['check_foo']}

CFLAGS = '-I../../clib/src'
LDFLAGS = '-L../../clib/src/.libs'

# check_foo: check_foo.c foo.h corm_foo.h corm_foo.c
#     gcc $(CFLAGS) $(LDFLAGS) $^ -o $@ -lcorm -lsqlite3
def task_check_foo():
    """
    Compiles the `check_foo` executable
    """
    src_files = ('check_foo.c', 'foo.h', 'corm_foo.h', 'corm_foo.c')
    dst_file = 'check_foo'
    return {

        'file_dep': src_files,
        'actions': ["gcc %s %s %s -o %s -lcorm -lsqlite3" % (CFLAGS, LDFLAGS, ' '.join(src_files), dst_file)],
        'verbosity': 2,
        'targets': [dst_file],
        'clean': True
    }

# corm_%.h corm_%.c: %.h
#     PYTHONPATH=../../cgen/src python ../../cgen/src/isti/cgen/run.py $<
def task_gen_corm():
    """
    Generates files `corm_%.h` and `corm_%.c`
    for each header file `%.h`.
    """
    for data in file_pattern('./*.h', dict(h_file='./corm_%.h', c_file='./corm_%.c')):
        yield {
            'name': data.name,
            'file_dep': [data.src_path],
            'actions': ["PYTHONPATH=../../cgen/src python ../../cgen/src/isti/cgen/run.py %s" % data.src_path],
            'verbosity': 2,
            'targets': [data.h_file, data.c_file],
            'clean': True
        }

# clean:
#     rm -f corm_*.h corm_*.c
#     rm -f *.o
# No need to create tasks for this: 
# with 'clean': True, `doit clean` will clean all target files

Then simply run doit in the folder.

Do not hesitate to provide feedback on the projects pages if needed: for example multiline commands are not supported, you can vote for them if you too feel that they are missing: https://github.com/pydoit/doit/issues/314

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top