You actually have a couple if something is wrong.
At first you write My mistake was that I assumed that the% .o pattern matches any pattern ending in .o that doesn't exist; this is not true. A pattern matches any string ending in .o . However, the % character that matches the target side is replaced on the background side with an identical string. Therefore, if you have a target obj/task.o and it matches the %.o pattern, then the trunk (what the manual calls it) will be obj/task , and when the %.c precondition means that make will look for the necessary condition obj/task.c Since it does not exist, and make does not know how to build it, this rule is discarded as not applicable. When writing template rules, you should write them like this, ONLY identical parts of the names correspond to the symbol of the template ( % ). ALL non-identical parts, including catalogs, must be indicated explicitly.
Secondly, the rule $(OBJ) : $(SRC) really wrong. This line says that every object file depends on all the source files, so whenever any one source file changes all , the object files will be recompiled. This is really not what you want (if this is what you want, you do not need to do it: you can just write a simple shell script). I do not know what you mean, because the rules are empty, it calls the template rule; you do not need this to invoke the template rule. The target depends on $(OBJ) , and each object file depends on the source file (due to the template). You do not need this line at all.
Third, I don’t know why you are trying to construct .asm files, and not just compile directly from the source to the object, but if you really want them, it would be cleaner and more “make-like” to create a separate template rule To create them: create a template rule $(OBJDIR)/%.o : $(OBJDIR)/%.asm and a rule $(OBJDIR)/%.asm : $(SRCDIR)/%.c . If you want ASM files to be assembly products, you must declare them as a prerequisite for all or similar, otherwise they will be deleted as intermediate files.
Fourth, the use of things like basename is not required. There are many automatic variables that can be used instead. For example, $* expands to the stem, so you can write $(OBJDIR)/$*.asm . Of course, if you create a separate template rule for ASM files, you can simply use $@ or $< directly. There are also various execution functions that can also be used; see manual.
Fifth, you define a variable containing the header file, DEP , but never use it. Since it is not used, if you modify this file, nothing will be rebuilt. If you know that all source files include every header, you can use $(OBJ) : $(DEP) to determine this; but this means (as in the second paragraph above) that any change to any header forces all objects to recompile. You would be better off automating the creation of prerequisites; since you are using GCC, it is quite simple.
Sixth, you use C ++ files (xxx.cpp), but you use the C compiler. This will not work (the link line will fail: although the compiler can see that you are compiling the C ++ file and doing everything correctly, even if you call gcc when you bind a bunch of objects together, it has no idea C objects or C ++ objects (or FORTRAN or something else), so you MUST use the C ++ interface to communicate, otherwise it won’t pull in the right ones C ++ libraries). You should use the CXX make CXX to build C ++ code, not CC , and set it to g++ not gcc .
Seventh, you do not need .SUFFIXES: .c .o use template rules. They are only needed for suffix rules that you do not have here. You can keep a simple .SUFFIXES: although to disable the built-in pattern matching this is a slight performance improvement.
Finally, you will notice that you really do not need the $(SRC) variable, because make can deduce it from the template rules. However, if you want your makefile to be less burdensome to change, you could build the contents of the OBJ variable from the SRC variable, for example SRC = nohupshd.cpp task.cpp , then OBJ = $(patsubst %.c,$(OBJDIR)/%.o,$(SRC)) .
So, all-in, here is how I would recommend you write your make file (I do not include automatically generated dependencies here):
.SUFFIXES: CXX := g++ CXXFLAGS := -O2 -g -Wall -fmessage-length=0 OBJDIR := obj SRCDIR := src TARGET := nohupshd SRC := nohupshd.cpp task.cpp DEP := src/task.h LIBS := # ---- OBJ := $(patsubst %.cpp,$(OBJDIR)/%.o,$(SRC)) ASM := $(patsubst %.cpp,$(OBJDIR)/%.asm,$(SRC)) .PHONY: all clean all: $(TARGET) $(ASM) $(TARGET): $(OBJ) $(CXX) -o $@ $^ $(LIBS) clean: rm -f $(OBJDIR)/* $(TARGET) $(OBJDIR)/%.o : $(SRCDIR)/%.asm $(CXX) $(CXXFLAGS) -c -x assembler-with-cpp $< -o $@ $(OBJDIR)/%.asm : $(SRCDIR)/%.cpp $(CXX) $(CPPFLAGS) -S $< -o $@