I had a strange bug that I managed to reduce to the following. I have a folder with only two empty files: a Makefile and a cpp file:

$ ls -la
total 8
drwxrwxr-x  2 erelsgl erelsgl 4096 Mar  3 20:12 .
drwxrwxr-x 13 erelsgl erelsgl 4096 Mar  3 20:10 ..
-rw-rw-r--  1 erelsgl erelsgl    0 Mar  3 20:12 Demo.cpp
-rw-rw-r--  1 erelsgl erelsgl    0 Mar  3 20:09 Makefile
$ cat Makefile 
$ cat Demo.cpp 

When I run make I get the expected result:

$ make
make: *** No targets.  Stop.

But when I run make Demo.o I get a very strange outcome:

$ make Demo.o
clang++-5.0 -std=c++17   -c -o Demo.o Demo.cpp
make: clang++-5.0: Command not found
<builtin>: recipe for target 'Demo.o' failed
make: *** [Demo.o] Error 127

The command "clang++-5.0 -std=c++17" were actually in the Makefile some days ago, but I have long since then replaced it with clang++-9. And now, as you can see, the Makefile is entirely empty. Why is make still running these old commands?

EDIT: The same thing happens when I change the Makefile to contain these two lines:

%.o: %.cpp xxx.h
    clang++-9 --compile $< -o $@

when the file xxx.h does not exist. This is very confusing: I run make Demo.o and it is built with clang++-5.0 instead of clang++-9!

  • 2
    Do you have a CXX and/or CXXFLAGS defined in your environment? if so, make is likely using those as part of a default rule – steeldriver Mar 3 '20 at 18:26
  • 1
    Yes, they are defined! Is there a way to disable this default behavior? (in case I cannot change the environment) – Erel Segal-Halevi Mar 3 '20 at 18:41
  • 7
    Does this answer your question? How does this Makefile makes C program without even specifying a compiler? – Stephen Kitt Mar 3 '20 at 18:46
  • Not sure how portable, but at least with GNU make, make -r or make --no-builtin-rules should disable builtin rules altogether. However you should always be able to override environment variables at run time ex. CXX=g++ CXXFLAGS= make Demo.o – steeldriver Mar 3 '20 at 18:48
  • 1
    I would recommend breaking the last bit of code into two rules %.o: %.c\n clang... and xyz.o: xxx.h yyy.h ... (A single line: no recipe). – ctrl-alt-delor Mar 3 '20 at 22:40

See How does this Makefile makes C program without even specifying a compiler? for background. Make has built-in rules which tell it how to build various files; in this particular case, since you’re asking for an object file, and there’s a matching .cpp file in the current directory, it uses the rule which specifies how to build an object file from a .cpp file.

In GNU Make, certain parts of that rule end up using variables; in particular, CXX for the C++ compiler, and CXXFLAGS for the flags to use when compiling C++ code. If the environment contains values for those variables, those values take precedence over the built-in defaults. Since CXX is still set to clang++-5.0, Make tries to use that to build Demo.o.

With your two-line Makefile, the recipe you define doesn’t get used because its prerequisites don’t all exist and can’t all be made. Instead, the generic pattern rule gets used again, still with CXX set to clang++-5.0.

You can disable built-in rules entirely with the -r option to make. You can also supplement them; thus

Demo.o: Demo.cpp xxx.h

with no recipe will use the built-in rule, but require xxx.h in addition to Demo.cpp (and correctly fail since xxx.h doesn’t exist and can’t be built).

  • Thanks for the explanation. This issue was very confusing. – Erel Segal-Halevi Mar 4 '20 at 9:51

Not the answer you're looking for? Browse other questions tagged or ask your own question.