buildmakefilevpath

How to make the target depend on lib file, but exclude it from $^ (VPATH involved)?


Imagine this structure:

root/
|
+-- include/
+-- src/
+-- build/
+-- lib/
+-- tests/
    |
    +-- common
    +-- test1
    +-- test2
    +-- test3

In tests/, the folder common/ contains a couple of source files that generate data or read data from logged file etc, which are shared between all tests. Each test therefore has only one file; main.c (currently). The Makefile of each test looks like this:

.SUFFIXES:
.SUFFIXES: .c .o

TARGET := test
# OS dependent stuff omitted for brevity
CFLAGS += -I../../include -I../common
LDFLAGS += -L../../lib -lname

VPATH = ../common

.phony: all clean
all: $(TARGET)
clean:
    -$(RM) *.o $(TARGET)
# Other targets removed for brevity

OBJECTS := main.o io.o read_str.o
$(TARGET): $(OBJECTS) ../../lib/libname.a
    $(LD) -o $@ $(OBJECTS) $(LDFLAGS)
%.o: %.c
    $(CC) -c $(CFLAGS) -o $@ $<
main.o: main.c io.h $(wildcard ../../include/*.h)

At first I had the common/ files copied in each test and everything was fine, but now that I put them in common/ and used VPATH, I am facing an issue.

Let me explain a few lines:

main.o: main.c io.h $(wildcard ../../include/*.h)
               ^^^^
           VPATH correctly finds this file in common/

$(TARGET): $(OBJECTS) ../../lib/libname.a
                      ^^^^^^^^^^^^^^^^^^^
                  If the library is rebuilt, the tests need to re-link

    $(LD) -o $@ $(OBJECTS) $(LDFLAGS)
                ^^^^^^^^^^
            I can't use $^ because that also includes ../../lib/libname.a

As you may have already guessed, since the common/*.o files are not in this folder, $(LD) fails to find them. On the other hand, make, given VPATH = ../common has no problem finding them. If I could remove ../../lib/libname.a, then with $^, I could give the correct paths to the object files.

Removing the dependency to the lib file however, means that when I make at the top-level, the tests will not get updated if only the lib itself is being updated.

So, my question is, how can I tell make there is a dependency to another file (a lib file) but it is not used in the command below it, given that the other dependencies are found using VPATH?

I thought about removing the dependencies, and in the Makefile that builds libname.a, add a command after the library is made that tells the Makefiles of tests to remove their TARGETs:

lib/Makefile:

.SUFFIXES:

TARGET = libname.a
VPATH = ../build

.PHONY: all clean
all: $(TARGET)
clean:
        -$(RM) $(TARGET)

$(TARGET): list.o of.o my.o library.o files.o
        $(AR) $(TARGET) $^
        @$(MAKE) --no-print-directory -C ../tests remove_targets

This would force $(LD) to make them again. This solution however tends to make the Makefile relationships into a mess. The Makefile that builds the lib is unrelated to whether there are tests or not, and I prefer to keep them decoupled.


I build and test a lot, that is why I try to get the dependencies complete, so that make takes the minimum possible actions to correctly build everything. A solution that says, clean and rebuild every time is not an option.

I am also aware of this syntax, which could be used if I wanted to give the lib file directory to gcc, instead of using -Lpath/to/lib -lname. I am not sure however if that is a good idea (I mean, I always saw linking with -L, and I'm not sure if there is a disadvantage when directly giving the library file)

By the way, if I am doing something horribly wrong, or if I am making it too difficult for myself, please tell me how this should be done correctly.


Solution

  • So, my question is, how can I tell make there is a dependency to another file (a lib file) but it is not used in the command below it, given that the other dependencies are found using VPATH?

    The question is "how can I make LIB a prerequisite but not be in the list of all prerequisites?"

    This may not be the best answer, but should work, just filter it out of the list:

    $(TARGET): $(OBJECTS) ../../lib/libname.a
        $(LD) -o $@ $(filter-out ../../lib/libname.a,$^) $(LDFLAGS)