0

So I'm trying to write this makefile and I'm having some issues getting it working, in fact I'm having issues getting makefiles in general working (no doubt due to user error).

make output:

$ make -v
    GNU Make 4.2.1
    Built for x86_64-unknown-linux-gnu
    Copyright (C) 1988-2016 Free Software Foundation, Inc.
    License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
    This is free software: you are free to change and redistribute it.
    There is NO WARRANTY, to the extent permitted by law.

$ make clean all
    rm -rf a.out  bin
    rm -rf obj
    g++ -lm  -o bin/a.out
    /usr/lib/gcc/x86_64-pc-linux-gnu/8.1.0/../../../../lib/Scrt1.o: In function `_start':
    (.text+0x20): undefined reference to `main'
    collect2: error: ld returned 1 exit status
    make: *** [makefile:32: bin/a.out] Error 1

makefile:

CXX := g++
LXX := g++
CXXFLAGS := -m64 -std=gnu++14 -fno-enforce-eh-specs -fno-rtti -fpermissive -O3 -funswitch-loops -w
CXXDEBUG := -ggdb3 -pg
LDFLAGS  := -lm

EXE := a.out
SRC_DIR := src
BIN_DIR := bin
OBJ_DIR := obj
INCLUDE := $(addprefix -I, include)
SOURCES := $(foreach sdir, $(SRC_DIR), $(wildcard $(sdir)/*.cpp))
OBJECTS := $(pathsubst src/%.cpp, obj/%.o, $(SOURCES))
vpath %.cpp $(SRC_DIR)/

define make-goal
$1/%.o: %.cpp 
    $(CXX) $(INCLUDE) $(CXXFLAGS) -c $$< -o $$@
endef

.PHONY: clean all checkdirs $(BIN_DIR)/a.out
.SECONDARY: 

default: all

all: checkdirs $(BIN_DIR)/a.out

$(BIN_DIR)/a.out: $(OBJECTS)
    $(LXX) $(LDFLAGS) $^ -o $@

checkdirs: $(BIN_DIR) $(OBJ_DIR)

$(BIN_DIR):
    @mkdir -p $@
$(OBJ_DIR):
    @mkdir -p $@

clean: 
    rm -rf $(EXE) $(OBJECTS) bin
    @find . -name "*~" -exec rm {} \;
    @find . -name "*.o" -exec rm {} \;
    rm -rf obj

$(foreach odir,$(OBJ_DIR),$(eval $(call make-goal,$(odir))))

My theory is that the $(foreach ...) at the end is never executed OR that the implicit pattern rules created are not executed (come to think of it I could never get %.o: %.c rules working either)

But honestly? I have no idea, the manual didn't provide any help - at least not that I could find (or, possibly, understand).

What I do understand is that no object files are output, ergo ld fails because it's not finding a main function.

This much is obvious and (painfully) clear to me. (I'm testing this with a very simple example: hello world, it compiles cleanly and executes as expected with g++ world.cpp -o a.out so it's not a g++ sanity issue I think).

Am I using GNU extensions wrong? I know call is a GNU extension.

the expected output would be:

├── bin
│   └── a.out
├── makefile
├── obj
│   └── world.o
└── src
    └── world.cpp

This makefile is very similiar to other makefiles I've seen floating around on SO, e.g https://stackoverflow.com/a/2484343/2717116 - however this makefile when copied verbatim also does not appear to work for me - verbatim output:

make all
g++  -o build/test.exe
g++: fatal error: no input files
compilation terminated.

as expected the same issue is faced, namely that make-goal is never reached for one reason or another.

1 Answers1

0

This is wrong:

OBJECTS := $(pathsubst src/%.cpp, obj/%.o, $(SOURCES))

The function name is patsubst not pathsubst.

MadScientist
  • 92,819
  • 9
  • 109
  • 136
  • well now I just feel stupid, it works flawlessly with that typo fixed - accepted and upvoted, thank you. (I mean it, I'd never have spotted that myself) –  May 22 '18 at 05:31
  • 1
    Yeah, that's tricky. You could enable the `--warn-undefined-variables` option when you run make and it should warn. Other than that you just have to realize that if make is not building your prerequisites, it _MUST_ mean that the `$(OBJECTS)` variable is expanding to the empty string and just stare at it until the penny drops (that's what I did :)) – MadScientist May 22 '18 at 07:12