Set up my makefile to compile C with just "make"
Asked Answered
S

4

5

I'm doing the Learn C The Hard Way course by Zed Shaw, and on Lesson 2 I find that in the videos and book, the author only uses make ex1 to compile his C, while I need to run gcc -Wall -g ex1.c -o ex1. Here is my Makefile, It seems like the first part is what is supposed to make the make command work to compile, but the "make" command doesn't compile it.

CFLAGs=-Wall -g

clean:
    rm -f ex1.exe

all:
    gcc -Wall -g ex1.c -o ex1.exe




Surefooted answered 9/1, 2019 at 22:44 Comment(2)
CFLAGs, with a lower-case s?Refugiorefulgence
@JosephQuinsey, what's the reason of CFLAG[sS] (in any lettercase) if then it's not used in the rest of Makefile.... there's no dependency info in that Makefile, so no automatic rule will be selected, and no use of CFLAGS variable will be made.Fluoridate
C
4

The default goal is the first goal, so switch the order of these so that all is the default.

There's more detail in the documentation on goals:

By default, the goal is the first target in the makefile (not counting targets that start with a period). Therefore, makefiles are usually written so that the first target is for compiling the entire program or programs they describe. If the first rule in the makefile has several targets, only the first target in the rule becomes the default goal, not the whole list. You can manage the selection of the default goal from within your makefile using the .DEFAULT_GOAL variable
Cowboy answered 9/1, 2019 at 22:51 Comment(4)
Also, ex1.exe should be a prerequisite for all (all: ex1.exe instead of just all:), else make cannot detect if it's up-to-date. Also, optionally add .PHONY: all to avoid confusion with a file called literally all.Gossett
@Cowboy That's not what I'm looking for, what I was meaning was to use only make. The make all is only for ex1, and I want to just use make.Surefooted
My bad! This answer worked for the most part though! Thanks!Surefooted
@anatolyg, nope... ex1.exe doesn't need to be a prerequisite of anything, as it's the all target what is only needed in the command line to build ex1.exe. The way the Makefile is written is not my preferred one... but just saying make all will make the executable. No dependency info has been wired on it.Fluoridate
F
4

Your Makefile has no dependency info included in the form of Rules. Rules are lines beginning at the left column (in column 0) and having a : separator between targets (objects to be built) and requisites (objects provided to the rule)

Make does the chaining of rules so no target can be build before its prerequisites have, so a good starting point would be:

ex1.exe: ex1.o

this means that ex1.exe (the first target in your Makefile) depends on ex1.o (the compiled object).... but how. You add the commands just below, but inserting in front of the command line a tab character:

[tab char]gcc -Wall -g ex1.o -o ex1.exe

First of all, see how the command takes ex1.o as input for gcc and generates (by means of option -o) the file ex1.exe.

The meaning of all of this is that always that you lack ex1.exe or it happens to be modified (from its modification date in the file) before the prerequisite (ex1.o) that command will be used.

Now, how we compile the object file? (in your Makefile it is made directly)

ex1.o: ex1.c
    $(CC) $(CFLAGS) -c -o $@ $<

this line needs more explanation (although if you don't use it, it is automatically provided by the make program) It uses substitution variables: $(CC) expands to the default C compiler (cc normally, but you can change it). $(CFLAGS) is the variable you defined on top of your Makefile and expands to the compiler options used normally to compile programs. $@ is expanded by the target file (in this case, the ex1.o file) and the $< expands to the left prerequisite of the right part of the rule. This rule, which is so common to compile C files into object files, is automatically generated for you by make, and more can be created in make advanced used (I'm not going to enter here, due to the amount of space needed) just to say that it can be written like this:

.c.o:
    $(CC) $(CFLAGS) -c -o $@ $<

which means: any file with extension .o can be created from a file with the same name, but extension .c by using the next command line below. This makes automatically to depend each .o file of a file with the same name but extension .c and is preloaded in make.

So, your simplest Makefile could be:

CFLAGS=-Wall -g
src=ex1.c
ex1.exe: $(src)
    $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(src)

as you see I've used a variable src to describe the C source files used to compile my program. The dependency will be issued only when you have modified any or the prerequisites (there's only one now, on ex1.c but could be more) and as I'm doing compiling and linking in the same compilation, I need to pass the compilation flags and the link flags (not used, you can delete the reference to $(LDFLAGS) here) Indeed you could have written:

ex1.exe: ex1.c
     cc -g -Wall -o ex1.exe ex1.c
clean:
     rm -f ex1.exe

so, the default action is to create ex1.exe, which depends on ex1.c (so only when ex1.c is modified, does ex1.exe is being recompiled. To compile it, just do:

make ex1.exe

or

make

that will use the default first rule of the Makefile.

clean is a fake target (there's no file called clean to generate) so when you use

make clean

its associated command is always executed. It makes make to delete your target files (as it removes all the make generated files), so next time you run make it will re-create all files by doing the necessary compilations.

COMMON USE

For simple programs, let's say, several source .c files, and some .h files, with separate compilation of each (-c option in compilation) and linking, I normally use this approach (definining the object files needed for each target program in a different variable)

# makefile for programs foo and bar.

targets = foo bar
TOCLEAN = $(TARGETS)  # this variable stores what should be erased on clean target.  Here we store the programs to erase.

foo_objs = a.o b.o c.o
TOCLEAN += $(foo_objs)  # here, we add the objects of foo

foo: $(foo_objs)
    $(CC) $(LDFLAGS) -o $@ $(foo_objs)

bar_objs = d.o e.o f.o
bar_libs = -lm -lintl -lX
TOCLEAN += $(bar_objs)  # here we add the objects of bar

bar: $(bar_objs)
    $(CC) $(LDFLAGS) -o $@ $(bar_objs) $(bar_libs)

clean:
    rm -f $(TOCLEAN)

a.o b.o c.o: a.h b.h c.h globals.h
b.o c.o d.o e.o f.o: my_defs.h

(as you see, the dependency on the header files is to the compiled object --- not the .c source file... the source file builds the dependency, but is the object file what has to be recreated in case the included file is modified, not the including .c file)

Fluoridate answered 11/1, 2019 at 9:32 Comment(6)
What's the reason you use $(CC) $(LDFLAGS) rather than the standard $(LINK.o)? (That prevents a target-specific LINK.o = LINK.cc for linking C++ objects, for example). Similarly, it's standard practice to use $(RM) rather than rm -f, though the need to override that is rarer.Breeding
I'd also use a target-specific bar: LDLIBS += -lm -lintl -lX, but perhaps that's just a personal preference?Breeding
Well, there's a good reason to call the compiler to do the linking phase. It will include the language runtime modules needed to support that language. If you call the linker directly, you'll have to provide those files yourself. The same applies to the standard C library.Fluoridate
use of a LDLIBS global variable of make doesn't allow you to specify different libraries for each exacutable. You can use that for general project libraries, and then have a program-specific set of libraries for each executable. This is not as important as with the compilation flags, because no automatic rules for linking are normally included by default in make, but it is important on compiling. As you see, both programs use different objects and libraries.Fluoridate
Indeed; that's why I write a target-specific LDLIBS.Breeding
yes... but that's a GNU make extension. As you see, I was most care of not using GNU make extensions and used the non-preferred old notation of .c.o: target.Fluoridate
S
1
CFLAGS = -Wall -g

all: ex1

clean:
    $(RM) ex1

.PHONY: all clean

Fixed my CFLAGS definition at the beginning and added the .PHONY at the end for all and clean so make knows not to make files for these.

Surefooted answered 9/1, 2019 at 23:17 Comment(1)
please, if you use ex1 as prerequisite of all then use that same ex1 as the argument to rm -fFluoridate
M
0

For a very simple makefile, I do this:

qsorttest: qsorttest.c 
     gcc -Wall -g qsorttest.c -o qsorttest -lm

clean:
     rm -f qsorttest

"qsorttest.c" is the name of my source file and the executable I create is "qsorttest" Be sure lines under "program name: source file" and "clean:" are tabbed in and not moved with the space key. I beat my head against the wall for a while until I googled my way out of that.

I save that as "Makefile" in the same directory as my other files for the program and make works every time. I just change the source file & program names as needed and add extra parameters for linking libraries if I need them.

Mahout answered 9/1, 2019 at 23:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.