Multi-use Makefiles? - makefile

I have the files main.c, version1.c, version2.c and header.h
version1.c and version2.c are two different implementations of the same header file (header.h), and this header file is used by main.c.
Now, I want to
$ make v1
cc -c main.c
cc -c version1.c
cc -o prog main.o version1.o
or
$ make v2
cc -c main.c
cc -c version2.c
cc -o prog main.o version2.o
depending on which version of code I'm using.
I tried Conditionally appending to a variable inside a Makefile target and wrote
.PHONY: v1 v2
OBJ = main.o
v1: OBJ += version1.o
v2: OBJ += version2.o
v1 v2: prog
prog: $(OBJ)
cc -o prog $(OBJ)
%.o: %.c header.h
cc -c $<
But this quite simply does not work:
$ make v1
cc -c main.c
cc -o prog main.o version1.o
cc: version1.o: No such file or directory
make: *** [prog] Error 1
it seems that make evaluates $(OBJ) before the append when it appears in the dependency list, but after it when it appears in the command list.
Is this a bug in make? Or am I just doing it wrong? How do I do this correctly?

The target-specific modification of the variable (v1: OBJ += version1.o) applies in the body of the rule, not in the prerequisite list. Here's how I'd do it:
.PHONY: v1 v2
v1: version1.o
v2: version2.o
v1 v2: main.o
cc -o prog $^
%.o: %.c header.h
cc -c $<

Related

How can my makefile include subdirectories?

(updated for clarity) (solution added at bottom)
I found a makefile online which builds all the cpp files in that directory and compiles them.
But I can't work out how I can include files inside a subdirectory.
Here's a breakdown of what happens:
I create the files test.cpp & test.hpp and place them inside the sub-directory '/gui' which is contained within my working directory, they contain the function testFunction().
Without including test.hpp, I type "make" into terminal and I receive the error:
:
g++ -c -o main.o main.cpp
main.cpp: In function 'int main(int, char**)':
main.cpp:6:2: error: 'testFunction' was not declared in this scope
testFunction();
^~~~~~~~~~~~
make: *** [<builtin>: main.o] Error 1
If I include (#include "gui/test.hpp"), I then receive a different error:
:
g++ -c -o main.o main.cpp
g++ main.o -Wall -o testfile
/usr/bin/ld: main.o: in function `main':
main.cpp:(.text+0x14): undefined reference to `testFunction()'
collect2: error: ld returned 1 exit status
make: *** [makefile:34: testfile] Error 1
But if I then add "-I/gui" or (at a guess) "-I./gui" to CFLAGS, I get the exact same error message.
Here's the makefile for reference:
TARGET = testfile
LIBS =
CC = g++
CFLAGS = -g -Wall
.PHONY: default all clean
default: $(TARGET)
all: default
OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))
HEADERS = $(wildcard *.hpp)
%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c $< -o $#
.PRECIOUS: $(TARGET) $(OBJECTS)
$(TARGET): $(OBJECTS)
$(CC) $(OBJECTS) -Wall $(LIBS) -o $#
clean:
-rm -f *.o
-rm -f $(TARGET)
Thanks in advance!
Updated makefile since accepted answer:
(Changes were to include directories, CC replaced with CXX, and %.c replaced with %.cpp)
TARGET = testfile
DIRS =
LDLIBS =
CXX = g++
CXXFLAGS= -g -Wall
# this ensures that if there is a file called default, all or clean, it will still be compiled
.PHONY: default all clean
default: $(TARGET)
all: default
# substitute '.cpp' with '.o' in any *.cpp
OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp $(addsuffix /*.cpp, $(DIRS))))
HEADERS = $(wildcard *.h)
# build the executable
%.o: %.cpp $(HEADERS)
$(CXX) $(CXXFLAGS) -c $< -o $#
# if make is interupted, dont delete any object file
.PRECIOUS: $(TARGET) $(OBJECTS)
# build the objects
$(TARGET): $(OBJECTS)
$(CXX) $(OBJECTS) -Wall $(LDLIBS) -o $#
clean:
-rm -f *.o $(addsuffix /*.o, $(DIRS))
-rm -f $(TARGET)
To understand what's happening here you have to look up the definitions of declaration versus definition in C++ (and other languages). You should definitely do that.
A declaration (typically put into a header file) is like the address of your house. If someone wants to send you a letter, they need your address. If your main function wants to call another function like testFunction(), it needs the declaration of the function.
The first error happens because you don't have the header file included, so the compiler doesn't have the declaration of the function you want to call, which means it won't compile your calling function.
But for the letter to actually arrive, you need your actual house. The address is the declaration and your house is the definition... in this case the actual function implementation. That lives in test.cpp file. When you link your code together, the linker (in this scenario I guess the linker is like the postal service :p :) ) will try to link up the call to the definition.
However, you can see that you are not compiling the test.cpp file nor are you linking the object file:
g++ main.o -Wall -o testfile
here we see main.o, but not gui/test.o.
Why not? This line:
OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp))
Matches all *.cpp files and converts them into .o files. But *.cpp matches only files in the current directory, like main.cpp. If you want to put files in a different directory you have to tell make where they are; for example:
OBJECTS = $(patsubst %.cpp, %.o, $(wildcard *.cpp gui/*.cpp))

What would be the minimal Makefile for a C project?

I find plenty of answers such as this one that doesn't use the implicit rules.
The minimum I can write is this:
SRC = $(wildcard *.c)
OBJ = $(patsubst %.c, %.o, $(SRC))
EXEC=a.exe
all: $(EXEC)
$(EXEC): $(OBJ)
$(CC) $^ -o $#
clean:
$(RM) $(OBJ)
$(RM) $(EXEC)
But I am sure I can remove the linking part as well.
Is it possible to reduce this Makefile a bit more?
EDIT
With the help of Maxim Egorushkin I wrote this:
#Makefile
OBJS=$(patsubst %.c,%.o,$(wildcard *.c))
EXEC=a
$(EXEC): $(OBJS)
all : $(EXEC)
clean :
rm -f $(OBJS)
.PHONY: all clean
It does build my files, but it doesn't link anything:
$ make
cc -c -o bar.o bar.c
cc -c -o cow.o cow.c
cc -c -o foo.o foo.c
What should I change?
The dummy source files are created as follow:
echo "int main() {return 0;}" > cow.c
touch foo.c bar.c cow.c
The bare minimum would be:
all : a
a : a.o b.o c.o
clean :
rm -f a a.o
.PHONY: all clean
It expects source files a.c, b.c and c.c to produce executable a:
$ touch a.c b.c c.c
$ make
cc -c -o a.o a.c
cc -c -o b.o b.c
cc -c -o c.o c.c
cc a.o b.o c.o -o a
/usr/lib/gcc/x86_64-redhat-linux/5.3.1/../../../../lib64/crt1.o: In function `_start':
(.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
<builtin>: recipe for target 'a' failed
make: *** [a] Error 1
However, you do not get automatic header dependency generation with the built-in GNU make rules. Extra 5 lines would be required for that.

How to create targets into specific directory with make?

My path:
./folder1/folder2/
My makefile is located in folder1.
My makefile:
CC = g++
FLAGS = -o3 -std=c++11
all: prng.o exec
prng.o: ./folder2/prng.cpp
$(CC) $(FLAGS) -o $# $<
exec: prng.o
/$<
prng.o is generated in folder1. I want it to be generated in folder2. How to do that?
CC = g++
FLAGS = -o3 -std=c++11
all: folder1/prng.o exec
folder1/prng.o: folder2/prng.cpp
$(CC) $(FLAGS) -o $# $<
exec: prng.o
/$<
BTW, your C compiler should have a C11 binary, something like /usr/bin/c11. That would let you say,
CC = c11
CFLAGS = -o3
Also, by using CFLAGS (instead of FLAGS), you shouldn't have to mention it in the recipe.

How to convert a source c files to corresponding .o files using suffix rule in makefile?

Suppose we have a.c b.c c.c .So the make file will like this
app: a.o b.o c.o
gcc -o app.o a.o b.o c.o
a.o: a.c
gcc -c a.c
b.o: b.c
gcc -c b.c
c.o: c.c
gcc -c c.c
In the future more C files may be added. So do I need to make target of .o extensions for each .c file. I got to know about suffix rules which uses the .source-extension.target-extension. But I could understand how to use this suffix rule in the make file. Please provide me the command to be included in make file and please describe the syntax.I am newbie to makefile.
You can use the below makefile.
app: a.o b.o c.o
gcc -o $# $^
a.o : a.h
b.o : b.h
c.o : c.h
%.o: %.c
gcc -c $<
Where $# is the target(app), $^ is the list of dependencies and $< is the corresponding c file to compile to object file
Below is the sample makefile for compiling c code.
TARGET = a.out
SRCS = a.c b.c c.c
OBJS = $(SRCS:.c=.o)
CFLAGS = -g -ggdb -O2 -Wall -Werror
CC = gcc
RM = rm
.PHONY: all clean
%.o : %.c
$(CC) $(CFLAGS) -c $< -o $#
$(TARGET) : $(OBJS)
$(CC) $^ -o $#
clean:
$(RM) *.o
$(RM) $(TARGET)

Makefile error, can't resolve include

I'm working with a project using flex/bison and trying to compile it using make. The lex.yy.c, tab.c, tab.h from flex/bison are generated correctly and placed in the obj directory. However, there is an error when trying to compile the obj/lex.yy.c file and it cannot resolve an include to a file in the src/frontend directory. Any ideas where I am going wrong? Makefile and output included below.
Makefile:
VPATH = src obj src/frontend src/interpreter
SRCS = lex.yy.c C.tab.c symbol_table.c nodes.c print_ast.c interpreter.c main.c
OBJS := $(SRCS:%.c=obj/%.o)
INCLUDES = -Isrc -Iobj -Isrc/frontend -Isrc/interpreter
CPPFLAGS = -Wall
LDFLAGS = -Wall
CC = gcc
LEX = flex
YACC = bison -d -t -v
all: bin/mycc
bin/mycc: $(OBJS)
$(CC) -g $(LDFLAGS) $(INCLUDES) -o $# $^
obj/lex.yy.c: C.flex obj/C.tab.h
$(LEX) -o $# $<
obj/C.tab.c: C.y
$(YACC) -o $# $<
obj/C.tab.h: obj/C.tab.c
#touch $#
obj/%.o: src/%.c
$(CC) -g $(CPPFLAGS) $(INCLUDES) -c $^
clean:
rm $(OBJS) obj/lex.yy.c obj/C.tab.c obj/C.tab.h
depend:
$(CC) -M $(SRCS) > .deps
cat Makefile .deps > makefile
Output:
bison -d -t -v -o obj/C.tab.c src/frontend/C.y
src/frontend/C.y: conflicts: 4 shift/reduce, 14 reduce/reduce
src/frontend/C.y:248.11-53: warning: rule useless in parser due to conflicts: external_declaration: function_definition
flex -o obj/lex.yy.c src/frontend/C.flex
gcc -Wall -c -o obj/lex.yy.o obj/lex.yy.c
src/frontend/C.flex:13:19: fatal error: token.h: No such file or directory
#include "token.h"
^
compilation terminated.
make: *** [obj/lex.yy.o] Error 1
The problem is that you define your -I flags for compiling in the variable $(INCLUDES) instead of in the normal $(CPPFLAGS). As a result, when the default rule for compiling C files runs, it does not use any of those -I flags and so the compiler can't find the include files. You can see the command line for the compiler in your output.
To fix it, get rid of the INCLUDES = line and add all of them to CPPFLAGS:
CPPFLAGS = -Wall -Isrc -Iobj -Isrc/frontend -Isrc/interpreter

Resources