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

build, makefile, vpath

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)

Problem

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 `Makefile`s of tests to remove their `TARGET`s: ``` 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.

Original source