makefilegnu-make

Makefile error when .PHONY target doesn't contain the output file names


The Makefile below throws the following error when compiled.

Error:

make: *** No rule to make target 'libfoo.so.0.1', needed by 'all'.  Stop.

Makefile:

LIBNAME = libfoo
SOLIBNAME = $(LIBNAME).so.0.1
BINNAME = bar

SRC_DIR := exe/src
SRCS := $(shell find $(SRC_DIR) -name '*.cpp')
OBJS := $(SRCS:.cpp=.o)

CXXFLAGS += $(INCLUDES) -std=c++17 -Wall -Wextra -fPIC

LIB_LDFLAGS += \
    -L../dir1 \
    -lads

all: foobuild barbuild

.PHONY: foobuild barbuild all clean

$(LIBNAME).a: lib/src/abc.o
    $(AR) -crs $@ $^

foobuild: lib/src/abc.o
    g++ -shared -o $(SOLIBNAME) $^ $(LIB_LDFLAGS)
    ln -srf $(SOLIBNAME) $(LIBNAME).so
    ln -srf $(SOLIBNAME) $(LIBNAME).so.0

BIN_LDFLAGS += \
    -L. \
    -lfoo

barbuild: $(OBJS) foobuild
    g++ -o $(BINNAME) $(OBJS) $(BIN_LDFLAGS)

clean:
    rm -f $(LIBNAME)* $(BINNAME) exe/src/*.o lib/src/*.o

If I replace .PHONY with $(SOLIBNAME) $(BINNAME) all clean, it builds without any error, which I do not understand.


Solution

  • In typical use, the Make target on the left-hand side of a rule should be the name of the file the rule generates. Don't use a .PHONY: target if you're building a file; use the name of the file as the target name. In your setup you're creating three file targets (one shared library and two symlinks) and so this can usefully be three rules.

    $(SOLIBNAME): lib/src/abc.o
        g++ -shared -o $(SOLIBNAME) $^ $(LIB_LDFLAGS)
    
    $(LIBNAME).so: $(SOLIBNAME)
        ln -srf $(SOLIBNAME) $(LIBNAME).so
    
    $(LIBNAME).so.0: $(SOLIBNAME)
        ln -srf $(SOLIBNAME) $(LIBNAME).so.0
    

    In a comment you ask about still having a consistent name for the command line, even if the library version changes. Here a .PHONY target makes sense.

    .PHONY: foobuild
    foobuild: $(LIBNAME).so $(LIBNAME).so.0
    

    Now even if you change the build metadata, you'll have a fixed name you can use at the command line.

    LIBNAME := libfoo
    LIBFOO_MAJOR := 0
    LIBFOO_MINOR := 0
    LIBFOO_PATCH := 1
    LIBFOO_SO_SHORT := $(LIBNAME).so.$(LIBFOO_MAJOR)
    LIBFOO_SO_FULL := $(LIBNAME).so.$(LIBFOO_MAJOR).$(LIBFOO_MINOR).$(LIBFOO_PATCH)
    
    .PHONY: foobuild
    foobuild: $(LIBFOO_SO_SHORT) $(LIBFOO_SO_FULL)
    
    make foobuild LIBFOO_MAJOR=1 LIBFOO_MINOR=0 LIBFOO_PATCH=0
    
    # same as
    # make libfoo.so.1 libfoo.so.1.0.0 LIBFOO_MAJOR=1 LIBFOO_MINOR=0 LIBFOO_PATCH=0