Where does the default `make` behavior come from? - c

I'm pretty new to Makefiles; thus, I encountered a question for which I can't come up with a good google search to help answer.
I am running a virtual OS which has a distro of fedora setup by someone else. If I construct my own Makefile in a directory, I can setup my .c files to compile however I like. Yet, if I simply run make test, whereby in my directory exists test.c, I will get the following : clang -ggdb3 -std=c99 -Wall -Werror test.c -lcs50 -lm -o test.
My question following this observation was where does this default, seemingly universal, make behavior come from? In other words, where does this Makefile, if it is one, sit on my file system?

make has several predefined implicit rules. Two of which are:
Compiling C programs
n.o is made automatically from n.c with a recipe of the form ‘$(CC) $(CPPFLAGS) $(CFLAGS) -c’.
Linking a single object file
n is made automatically from n.o by running the linker (usually called ld) via the C compiler. The precise recipe used is ‘$(CC) $(LDFLAGS) n.o $(LOADLIBES) $(LDLIBS)’.
Note, make is smart enough to effectively concatenate the above two into one rule when it makes sense:
... could be done by using the ‘.o’ object files as intermediates, but it is faster to do the compiling and linking in one step, so that's how it's done.
You can dump the predefined rules with make -pn. e.g.:
$ make -pn -f /dev/null | grep -A3 '^%: %.c$'
make: *** No targets. Stop.
%: %.c
# commands to execute (built-in):
$(LINK.c) $^ $(LOADLIBES) $(LDLIBS) -o $#
$

This goes for GNU make, which normally is the default make implementation on linux.
There's no default Makefile on your file system containing the default rules.
There are however implicit rules built into make that are in effect whether you supply a makefile or not, and what make does when invoked
is documented here.
These rules knows e.g. how to build an executable from a .c source file. You can learn about those implicit rules here,
e.g make has this default rule when building an executable:
n is made automatically from n.o by running the linker (usually called
ld) via the C compiler. The precise recipe used is ‘$(CC) $(LDFLAGS)
n.o $(LOADLIBES) $(LDLIBS)’
Meaning if you run make test it will try to create an executable test from the file test.o, and you can set the respective CC/LDFLAGS/etc. variables that will be used when linking.
And as another implicit rule it can build a .o file from a .c file, so the above will look for test.o, and try to rebuild that using the rule:
n.o is made automatically from n.c with a recipe of the form ‘$(CC)
$(CPPFLAGS) $(CFLAGS) -c’.
I.e. the implicit rules when running make test will first compile test.c and then link test.o using the compiler you specify with the CC envirnment variable(or the default compiler cc) and the various compiler/linker flags if you set then as environment variables.
.

Related

make command runs only the first row in Makefile

Hi I Have this makefile:
main.o:main.c functionsLab1.c functionsLab1.h
gcc -c main.c
functionsLab1.o: functionsLab1.c functionsLab1.h
gcc-c functionsLab1.c
now when i Run the command "make" it only executes the first command in makefile.
how can I Run all the commands at once?
Thanks in advance :)
I Tried to type "make all" command and it showed an error.
make is able to build what you need, but only if you tell it the right dependencies. In particular, your current Makefile is lying about dependencies, since main.o does not at all depend on functionsLab1.c. Rather, the final executable you are trying to build depends on functionsLab1.o. You can probably make your entire Makefile:
main: main.o functionsLab1.o
(Yes, literally one line.). That ignores the dependency on the header file, but it should work for you. Let make use its default rules; they are pretty good. If you want to include the header dependency, do something like:
main: main.o functionsLab1.o
main.o: main.c functionsLab1.h
functionsLab1.o: functionsLab1.c functionsLab1.h
If for some reason you really want to be explicit (you don't!), you can do:
main: main.o functionsLab1.o
$(CC) -o $# $? # Warning: incomplete. See note below
main.o: main.c functionsLab1.h
functionsLab1.o: functionsLab1.c functionsLab1.h
Again, letting make use its default rules to construct the object files. You can override the default rules, but there is very seldom a reason to do so. Indeed, this is an excellent example where attempting to override the default rule gives you a sub-optimal recipe. The default rule would be something like $(CC) $(LDFLAGS) -o $# $? $(LOADLIBES) $(LDLIBS), and many users would reasonably expect to be able to specify LDLIBS. The example shown above ignores LDLIBS, violating the principal of least surprise.

Makefiles in C language

Hello I'm having a hard time understanding makefiles. I play with them to understand them better but here's the issue:
all: main
main: main.o funcIO.o funcMan.o
$(CC) -o $# $^
----------------------------------
funcIO.o: funcIO.c
$(CC) -c -o funcIO.o funcIO.c
funcMan.o: funcMan.o
$(CC) -c -o funcMan.o funcMan.c
This works regardless if everything below the punctured line is there or not. I'm told that this is the right way to write makefiles but why does it work without the targets funcIO.o and funcMan.o and if it works without them, why do we write them? Can you explain it like I'm 5 years old?
Thanks for your time!
Assuming you're using GNU Make (it might be the same for other Makes), this works due to built-in rules. Make already knows how to compile a C source file, and unless you tell it otherwise, it applies this recipe to it:
%.o: %.c
$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c -o $# $<
$# is the target of the rule (the filename of the .o file) and $< is the first prerequisite (the filename of the .c file). The other variables have sensible defaults (mostly empty).
The right way to use Makefiles is to keep them as small as possible. Makefiles are about determining dependencies and only incidentally can be used to build programs. Here's how I would rewrite your Makefile:
all: main
main: main.o funcIO.o funcMan.o
And I only put the all target there because you had it to begin with. Make has a list of builtin rules that know how to build things given certain files as inputs. If you ask it for a .o file, it will look for a file of the same name, but with the extension of .c, .cpp, .f77, etc., and run the rule that builds what you asked for using that prerequisite file. You don't even need to specify how to build those, they come for free! It's the more complex relationships (such as a final binary) that need to be spelled out, as shown in my above example. There's a similar rule for building a binary out of .o files (assuming one of them has the same name as the binary, which yours does), so you don't need to specify any tasks, just the dependencies. You can control how they are run by adjusting special flags:
CFLAGS += -Wall -Wextra -Wpedantic
main: main.o funcIO.c funcMan.o
main: LDLIBS += -lm
This version builds every C-compiled file with those CFLAGS, and builds main while linking in the -lm math library.
If you are building normal C programs, I strongly recommend this approach. Specify the prerequisites of the final binary, and control builds through these Make variables.

Automatic variable not expanding properly in Makefile

I am using the following code:
HELLO_WORLD=hello
$(HELLO_WORLD): $(addsuffix .c,$#)
gcc $< -o $#
However, when I run the code, I receive the following error, implying that $< is not evaluating to anything:
gcc -o hello
gcc: fatal error: no input files
When I use the following code...
HELLO_WORLD=hello
$(HELLO_WORLD): $(addsuffix .c,$#)
gcc $(addsuffix .c,$#) -o $#
...the Makefile evaluates to the following command...
gcc hello.c -o hello
...which is precisely what I would like. However, I do not want to use addsuffix twice. I would like to use $< in the event that I change the prerequisite. How would I go about doing this?
The problem is not with the expansion of $< in the recipe. The problem is the expansion of $# in the prerequisite list.
Automatic variables, such as $#, are only defined in the recipe, not in the target or prerequisite lists. This is highlighted in the GNU Make manual section on automatic variables:
A common mistake is attempting to use $# within the prerequisites list; this will not work.
The fact that hello.c is not actually in the prerequisite list does not prevent you from invoking make hello. It just means that make hello will always invoke the compiler, even if hello.c has not been modified. But it does mean the $< will be as empty as the computed prerequisite list.
GNU make does have a feature to let you do a second expansion of prerequisites; this is explained in the manual. But the simpler solution is to simply not rely on $# in the prerequisite list. If you're trying to create your own generic C compile recipe, use a pattern rule for object file (.o) targets. For the final executable, list all the prerequisites for the final executable (which will almost certainly be more than one file).
Typically this is done using separate variable with names like SRCS and OBJS (or SOURCES and OBJECTS if you don't mind typing vowels). Normally you make the object files prerequisites for the final executable (which will be a link operation), since each individual source file will have its own header prerequisites.
The fundamental problem is automatic variables are only defined in the recipe. So, in the prerequisite, $# is not defined. Because $< will refer to an expression that depends on $#, which does not exist, $< will therefore not exist as well.
So, there are really two ways to resolve the issue. The first way is a bit clunky, but you can use secondary expansions. This essentially allows us to do what we want without adding much code...
HELLO_WORLD=hello
SECONDEXPANSION:
$(HELLO_WORLD): $(addsuffix .c,$$#)
gcc $< -o $#
The more proper way to do this involves restructuring the Makefile and using pattern rules. This gives us a generic recipe for building any C file. With the following Makefile, we can either run "make" or "make hello" to build the executable.
HELLO_WORLD=hello
all:
$(MAKE) $(HELLO_WORLD)
%: %.c
gcc $< -o $#

Error in makefile ("no input files")

This is my absolute first time ever making a makefile, and I'm really trying to understand the process.
I'm trying to create a very simple makefile for a C++ project whose structure is as follows:
root folder
makefile
readme
src folder
...source files all here...
include folder
...header files for external libraries here...
lib folder
...external lib files all here...
bin folder
...output directory for built executable...
obj folder
...object files all here...
I followed the tutorial here.
Here's my makefile:
IDIR=include .
CC=g++
CFLAGS=-I$(IDIR)
ODIR=bin/obj
LDIR=lib
LIBS=none
SRC=src
_DEPS=hello.h
DEPS=$(patsubst %,$(IDIR)/,%(_DEPS))
_OBJ=file1.o file2.o
OBJ=$(patsubst %,$(ODIR)/%,$(_OBJ))
$(ODIR)/%.o: $(SRC)/%.cpp $(DEPS)
$(CC) -c -o $# $< $(CFLAGS) # $(LIBS)
test_proj: $(OBJ)
$(CC) -o $# $^ $(CFLAGS)
.PHONY: clean
clean:
rm -f $(ODIR)/*.o *~ core $(INCDIR)/*~
When I run make on this, I get the following error:
g++ -o .o
g++: fatal error: no input files
compilation terminated.
<builtin>: recipe for target '.o' failed
mingw32-make.exe: *** [.o] Error 1
I'm using GNU Make 3.82.90 built for i686-pc-mingw32, if that matters at all.
Can anyone point out whatever ridiculous error I'm making?
IDIR=include .
is the first problem. Replace it by:
IDIR=include
With your code CFLAGS is expanded as:
-Iinclude .
It does not make sense, I'm afraid. The second problem is:
DEPS=$(patsubst %,$(IDIR)/,%(_DEPS))
which should probably be:
DEPS=$(patsubst %,$(IDIR)/%,$(_DEPS))
and would expand as:
DEPS=include/hello.h
if you fix the first problem, else as:
DEPS=include ./hello.h
which does not make sense neither. The cumulated effect of these two errors are strange recipes (I didn't try to expand them by hand) that probably trigger a make implicit rule with wrong parameters.
IDIR=include .
CC=g++
CFLAGS=-I$(IDIR)
This is wrong. First, for C++ code, use CXX not CC and CXXFLAGS not CFLAGS. Run make -p to understand the builtin rules of your make.
Then -I$(IDIR) does not "distribute" the -I, and IDIR is never used elsewhere. So I suggest to start your Makefile with:
CXX=g++
MY_CXX_LANG_FLAGS= -std=c++11
MY_CXX_WARN_FLAGS= -Wall -Wextra
MY_CXX_INCL_FLAGS= -I. -Iinclude
MY_CXX_MACRO_FLAGS= -DMYFOO=32
### replace with -O2 for a release build below
MY_CXX_OPTIM_FLAGS= -g
CXXFLAGS= $(MY_CXX_LANG_FLAGS) $(MY_CXX_WARN_FLAGS) \
$(MY_CXX_INCL_FLAGS) $(MY_CXX_MACRO_FLAGS)
I won't improve your Makefile, but I do suggest to upgrade to GNU make version 4 if possible (and compiling make 4.1 from its source code is worthwhile in 2015) for that purpose. If possible enable GUILE scripting in it.
If you are forced to use make 3.82 debug your Makefile using remake (with -x); if you can afford a make version 4 use its --trace option
BTW, you might consider using automatic dependencies, that is generating dependencies by passing -M or -MG (etc) flags of g++, see that.
At last, a simple project for a small program (less than a hundred thousands of source lines) might just put all (a few dozens of) its files in the current directory (then the Makefile could be simpler); your proposed directory structure might be arcane for a simple project (but could worth the pain if you have millions of C++ source lines of code). I've given several simple examples of Makefile, e.g. this & that. And GNU make source code itself has a less complex file tree that what you want.
BTW, I strongly disagree with the opinions of that answer (which I did upvote, since it is helpful). I don't feel that GNU make is senile, but I regret that, instead of using recent features available on recent versions (4.x) of make, many people prefer to use complex and arcane Makefile generators (like cmake) instead of coding a clever Makefile (for make version 4 specifically).
At last, you could use other builders, e.g. omake, icmake, ....

Make: compile object file twice in the same target

I have the following simple problem in a Makefile:
%.o:: %.c
gcc -o $# -c $<
lib1.a: test.o
ar -r $# test.o
rm *.o
lib2.a: test.o
ar -r $# test.o
rm *.o
all: lib1.a lib2.a
make lib1.a or make lib2.a work properly. However, make all gives:
gcc -o test.o -c test.c
ar -r lib1.a test.o
rm *.o
ar -r lib2.a test.o
ar: test.o: No such file or directory
make: *** [lib2.a] Error 1
I need to do the rm *.o cause I want the object file to compile each time (in my real Makefile, I have a more complex use case where I compile with different flags).
How can I fix this problem? It seems that make compiles the object file only once.
I tried with .PHONY instead of doing the rm, but again, it compiles only once.
Your makefile is a bit against the make logic, this is why the result is not what you expect:
Here you define two targets (lib1.a and lib2.a) with a common dependency: test.o.
Then you define the rule all (which, by the way, should be .PHONY but this isn't a problem here) that depends on lib1.a and lib2.a.
So, in order to "do" all, make have to build lib1.a and lib2.a. They both depend on test.o, so make builds test.o once, then build lib1.a and lib2.a, expecting that the recipes you defined will just build those files, and nothing more.
The problem is that you delete test.o in the recipe for lib1.a and lib2.a, although this action is not needed to build them, this is something you want to do when cleaning, not building.
There are two solutions:
Move the deletion operation in a rule that is meant to do that (a .PHONY rule named clean for example).
The use of intermediate targets which will be deleted when they're not needed anymore. In fact, you can achieve that without even thinking about intermediate targets if you simply delete the first rule of your makefile (the %.o:: %.c one), because make already has an implicit rule that does that using intermediate targets.
Make is a rule-based system. Rules are declarative: you declare what you want built from what, you don't specify the order in which this happens (unless you can't avoid it). So a good Makefile is declarative: all results are declared like in a declarative programming language. They are like final variables in Java: you bind them to a value, you don't reassign them to a different value afterwards.
Make is also file-based: its "variables", targets and prerequisites are files.
So if you want to build two different things, don't call them by the same name! If you want two different test.o files, call them differently. The problem will go away without you needing to try and convince make that it should be like an imperative programming language, which it was specifically designed not to be. If you want an imperative build specification, use a shell script.

Resources