Reputation: 53
I know there is infinite amount of resources on the Internet with regards makefiles, but it is something that I still do not understand it. At the moment I have 3 c files PolyCalculatorMain.c PolyCalculator.c PolyCalculator.h and Polyfunctions.c
So far I have
PolyCalculator: PolyCalculator.o PolyCalculatorMain.c
gcc -Wall -ggdb -o PolyCalculator PolyCalculatorMain.c PolyCalculator.o
PolyCalculator.o: PolyCalculator.c
gcc -Wall -ggdb -c PolyCalculator.c PolyCalculator.h
clean:
rm PolyCalculator *.o *~ *#
Any help or explanation on were to go to complete this file would be much appreciated. Note Im only beginning so I dont want any complex code as I will not understand it
Upvotes: 0
Views: 472
Reputation: 16540
Here is a proper GNU make
file for your project:
# set macro with list of source files in current directory using wildcard
# note: the ':=' states that the list is to only be generated once
# if only '=' were used, the the list would be re-generated each time referenced
SRCS := $(wildcard: *.c)
# set macro with list of object files using pattern substitution
OBJS := $(SRCS:.c=.o)
# set macro with list of header files in the current directory using wildcard
HDRS := $(wildcard: *.h)
# set macro with list of compiler flags
# there are many other very useful compiler flags
# but this list will result in:
# all warnings displayed
# compile only
# debug info being include
# (there are other forms of the -g parameter to get more debug info)
# no optimization
# look for some included files in the current directory
CFLAGS := -Wall -c -g -O0 -I.
# set macro with list of linker flags
# all warnings displayed
# debug info being included
LFLAGS := -Wall -g
# set macro with path+name of pre-processor/compiler/linker utility
# by convention, the utility is referenced by $(CC)
CC := /usr/bin/gcc
# by 'make' design, the make utility is referenced by $(MAKE)
# set macro with path+name of file deletion utility
# by convention, the utility is referenced by $(RM)
RM := /usr/bin/rm -f
# by convention, there are (at least) two phony target names
# tell 'make' that 'all' and 'clean' are targets that will NOT produce an output file
# with those names
.PHONY: all clean
# this is first target so will be performed if:
# user enters 'make' with no parameters
# or user enters 'make all'
# the PolyCalculator executable is stated to be a pre-requisite
all: PolyCalculator
# this is a real target
# state that this target has a pre-requisite of the files listed in the OBJS macro
# the executable name is defined by the '-o' and following name
# use the parameters listed in the LFLAGS macro
PolyCalculator: $(OBJS)
gcc $(LFLAGS) -o PolyCalculator $(OBJS)
# this is a real target (PolyCalculator.o)
# state that this target has a pre-requisite of file: PolyCalculator.c
# state that this target has a pre-requisite of the files listed in the HDRS macro
# use the parameters listed in the CFLAGS macro
# target 'PolyCalculator' lists PolyCalculator.o as a pre-requisite
# so it can be performed automatically
# it can also be performed directly when the user enters 'make PolyCalculator.o'
PolyCalculator.o: PolyCalculator.c $(HDRS)
gcc $(CFLAGS) -o PolyCalculator.o PolyCalculator.c
# this is a real target (PolyCalculatorMain.o)
# state that this target has a pre-requisite of file: PolyCalculatorMain.c
# state that this target has a pre-requisite of the files listed in the HDRS macro
# use the parameters listed in the CFLAGS macro
# target 'PolyCalculator' lists PolyCalculatorMain.o as a pre-requisite
# so it can be performed automatically
# it can also be performed directly when the user enters 'make PolyCalculatorMain.o'
PolyCalculatorMain.o: PolyCalculatorMain.c $(HDRS)
gcc $(CFLAGS) -o PolyCalculatorMain.o PolyCalculatorMain.c
# this is a real target (PolyFunctions.o)
# state that this target has a pre-requisite of file: PolyFunctions.c
# state that this target has a pre-requisite of the files listed in the HDRS macro
# use the parameters listed in the CFLAGS macro
# target 'PolyCalculator' lists PolyFunctions.o as a pre-requisite
# so it can be performed automatically
# it can also be performed directly when the user enters 'make PolyFunctions.o'
PolyFunctions.o: PolyFunctions.c $(HDRS)
gcc $(CFLAGS) -o PolyFunctions.o PolyFunctions.c
# this is a phony target (see .PHONY, above)
# it is performed when the user enters 'make clean'
clean:
$(RM) PolyCalculator *.o *~ *#
Upvotes: 0
Reputation: 755044
I make sure to use macros extensively, because you can redefine macros on the make
command line if need be. There are also many standard macros, such as CC
for the name of the C compiler and CFLAGS for the (majority) of the flags passed to the compiler. Also, make
has built-in rules for many operations, such as converting a .c
file into a .o
file.
The philosophy behind make
is that compilation is expensive, so you do as little of it as possible, but as much as necessary. Thus, make
is typically commanded to build object files, and then links the object files with libraries to build programs.
You say you have three source files and one header. So, my makefile for your program would include:
FILES.c = PolyCalculatorMain.c PolyCalculator.c Polyfunctions.c
FILES.o = ${FILES.c:.c=.o}
PROGRAM = PolyCalculator
CFLAGS = -Wall -ggdb
all: ${PROGRAM}
${PROGRAM}: ${FILES.o}
${CC} -o $@ ${CFLAGS} ${FILES.o} ${LDFLAGS} ${LDLIBS}
PolyCalculatorMain.o: PolyCalculator.h
PolyCalculator.o: PolyCalculator.h
Polyfunctions.o: PolyCalculator.h
clean:
rm -f ${PROGRAM} *.o *~ *# core a.out
The choice between $(XYZ)
and ${XYZ}
is arbitrary; I chose to use curly brackets 30 years ago, and don't see a reason to change. Being consistent is what matters.
The FILES.c
macro (yes, macro names may contain dots, despite the fact that vim
doesn't colour them as macros) lists a set of three file names. If I needed to add a fourth, then I'd add it to this list. If the list got too long for a single line, I'd use a macro of the format:
FILES.c = \
PolyCalculatorMain.c \
PolyCalculator.c \
Polyfunctions.c
This allows editing of one line affected when a file is added unless you add it at the end of the list. I normally keep such lists in sorted order.
The FILES.o
macro is defined by applying a suffix-transformation rule to the FILES.c
macro. The part after the :
is .c=.o
; it means replace the names that end with .c
with .o
at the end.
The PROGRAM macro is what I use in a single-program makefile. For a multi-program makefile, each program gets its own macro in all caps. (Although make
macros are not constrained to all caps, it is normal to use all caps for macros.) The CFLAGS macros sets your chosen options for the C compiler. Good marks for using -Wall
. I use more stringent options like -Wextra -Werror
and specify a standard (-std=c11
), but -Wall
is a good start.
It is conventional to have the first rule in your makefile called all
. It says "everything is up to date when ${PROGRAM}
is up to date".
The next rule says that the program depends on the object files. If any of the object files is newer than the program, you rebuild the program using the command line shown. The $@
hieroglyph is the same as ${PROGRAM}
in this rule. Formally, it is the name of the target being built. It makes the command line generic — everything except the -o
is a macro. This tends to be the way that makefiles
work; the information is almost all in macros. The ${LDFLAGS}
and ${LDLIBS}
macros are semi-standard; they allow you to pass options to the linker (ld
, invoked by the C compiler), and are used to specify library-related options. For example, you might have:
LDFLAGS = -L ${OTHERLIBDIR}
LDLIBS = -l${OTHERLIB}
to link with a library not located in /usr/lib
and other standard locations. The macros OTHERLIBDIR
and OTHERLIB
would be defined earlier in the makefile
, of course.
The next three lines say that the object files (note that it is the object files, not the source files) depend on the header file. make
is quite capable of deducing that PolyCalculatorMain.o
is built from PolyCalculatorMain.c
, so you don't have to specify that dependency explicitly. It can't deduce the header dependency so you have to specify it. Note that I've not specified a custom command, so the default command will be used. That's usually:
${CC} -c ${CFLAGS} $*.c
where the $*
is a shorthand for the basename of the file being compiled.
I modified the clean
command to use rm -f
(which won't complain if the arguments are missing), and added a.out
and core
to the list of debris files.
You can add endless extra stuff to a basic makefile
like this. You might add a depend
rule to automatically generate the header dependencies; that is useful when your code begins to get complex with multiple headers, and different source files using different sets of headers. If you have a multi-lingual source tree, you have to worry about multiple lists of files. If you have multiple programs, you have to separate the files specific to one program from those common to several, and then think about whether to build the common object files into a local convenience library so that you don't have to list the exact dependencies for each program.
But this outline will get you going.
If you're using GNU make
, you might well add a line:
.PHONY: all clean
This tells GNU make
that there won't be files called all
and clean
created.
I note in passing that the name Polyfunctions.c
is inconsistent with the other two; the F
should be capitalized for consistency.
Upvotes: 1
Reputation: 2528
Because you didn't give really clear detains of what you would want changed / modified, I giving you what I would write the makefile as below, although some might (will?) disagree with me in places. I see that you edited you original post and mention a PolyFunctions.cpp (and I guesssing that there is a PolyFunctions.h)
CC=gcc
CCFLAGS=-ansi -Wall -pedantic
INCS=
LK=gcc
LKFLAGS=
LIBS=
OBJS = PolyCalculator.o PolyCalculatorMain.o PolyFunctions.o
PROG = PolyCalculator
$(PROG) : $(OBJS)
$(LK) $(LKFLAGS) $(OBJS) $(LIBS) -o $(PROG)
rebuild : clean $(PROG)
PolyCalculatorMain.o : PolyCalculatorMain.cpp PolyCalculator.h PolyFunctions.h
$(GCC) -c $(CCFLAGS) $(INCS) PolyCalculatorMain.cpp -o PolyCalculatorMain.o
PolyCalculator.o : PolyCalculator.cpp PolyCalculator.h
$(GCC) -c $(CCFLAGS) $(INCS) PolyCalculator.cpp -o PolyCalculator.o
PolyFunctions.o : PolyFunctions.c PolyFunctions.h
$(GCC) -c $(CCFLAGS) $(INCS) PolyFunctions.cpp -o PolyFunctions.o
clean:
rm -f $(OBJS) $(PROG) *.o *~ *#
OK, the changes that I made (and why)
First I like using variables that are then expanded as rules are fired. I find this a convenient way to insure that all steps use the same settings. Also, I have added variables that you can use if you need to add headers to compilation (i.e. the INCS
variable). I have broken out the commands and flags for the linker as well, the LK
, LKFLAGS
and LIBS
variable. Currently most of these are empty.
I added a variable for the various object files that are created, I like doing this because if I add a new source file to the project I have to do two things, the first would be to write a rule for it and the second would be to add it to the OBJS
variable. In my opinion, the more things I need to do when adding a new source file, the greater the chance that I will forget something.
I added a variable for the final program name. Again, makes it simple if I want to change it…and I'm lazy — typing $(PROG)
is easier and less error prone than typing PolyCalculator
:)
Ordering of targets is a personal choice, I always put the target to rebuild the main program as the first (or default) target in the makefile. That way when I run make
I just build what has changed since my last build. I put a rule near the top to do a total rebuild of the application, in the above this is the rebuild
rule. Running make rebuild
is operationally equivalent to make clean; make
; but I prefer a single target to accomplish this
I rewrote your rules using the variable I defined in point 1.
I added the -f
flag to rm
so clean will do everything even if it can't find a file. Again, this tends to be personal preference.
Upvotes: 1
Reputation: 7059
In compile command you don't normally specify .h files. They are included by the preprocessor (which runs before the compiler) as it encounters #include statements. That is the only thing I can see.
The .h files do show up in Makefiles as dependencies since changes to them can require code that uses them to need to be recompiled.
I would say the basic rule is to include anything on the dependency line which would cause a recompile to be necessary if they changed.
PolyCalculator: PolyCalculator.o PolyFunctions.o PolyCalculatorMain.c
gcc -Wall -ggdb -o PolyCalculator PolyCalculatorMain.c PolyCalculator.o PolyFunctions.o
PolyCalculator.o: PolyCalculator.c PolyCalculator.h
gcc -Wall -ggdb -c PolyCalculator.c
PolyFunctions.o: PolyFunctions.c PolyFunctions.h
gcc -Wall -ggdb -c PolyFunctions.c
clean:
rm PolyCalculator *.o *~ *#
Makefiles allow you to have variables that can be reused. For example, you can have your compiler options in a variable:
options=-Wall -ggdb
and then you use them with brackets and a dollar sign
gcc ${options}
You can also break them up:
debug=-ggdb
warnings=-Wall
options=${warnings} ${debug}
You can also combine all your object files into a variable:
obj_files=PolyCalculator.o PolyFunctions.o
PolyCalculator: ${obj_files} PolyCalculatorMain.c
gcc -Wall -ggdb -o PolyCalculator PolyCalculatorMain.c ${obj_files}
PolyCalculator.o: PolyCalculator.c PolyCalculator.h
gcc -Wall -ggdb -c PolyCalculator.c
PolyFunctions.o: PolyFunctions.c PolyFunctions.h
gcc -Wall -ggdb -c PolyFunctions.c
clean:
rm PolyCalculator *.o *~ *#
Upvotes: 1