Creating Makefile

Creating a C++ makefile for an expected .o file?

Just include sub.o as a dependency of another target. No need to write a recipe for it.
For the main target you may do something like:

main: main.o sub.o
g++ -o $@ $^

This makes use of the built-in default rules that know how to compile main.cpp into main.o.

If the file ever goes missing, Make will come complain that it doesn't know how to create it:

make: *** No rule to make target `sub.o'.  Stop.

How to write makefile for a target that includes a header file?

The rule to write in your makefile would be:

create-exercise.o: exercise.hpp

This is a rule without a recipe, and it simply adds one more prerequisite (exercise.hpp) to the target create-exercise.o, i.e., whenever exercise.hpp changes, create-exercise.o should be rebuilt.

However, the problem when providing that rule is that GNU Make assumes create-exercise.o is an object file generated from a C source file when building create-exercise. Therefore, it doesn't link the C++ library but the C library instead, that's why you are having the liking error of undefined reference to std::cout.

GNU Make has the following implicit rule for generating an executable from a C++ source file:

%: %.cpp
# commands to execute (built-in):
$(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@

You can use that rule's recipe to build create-exercise from creating-exercise.o and still treat creating-exercise.o as an object file that was generated from a C++ source file:

create-exercise: create-exercise.o
$(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@

The whole makefile would be then:

CXXFLAGS = -Wall -std=gnu++17 

create-exercise: create-exercise.o
$(LINK.cpp) $^ $(LOADLIBES) $(LDLIBS) -o $@

create-exercise.o: exercise.hpp

The target create-exercise also becomes the default goal because it's the first rule in the makefile. It's the target to build if you don't specify any to make.

Create a Makefile to compile all .c files in a folder/subfolder and ship the binaries elsewhere

I am assuming that by "efficient" you mean "more generic/automated". Here is the most "efficient" solution I could think of:

CC := gcc
CFLAGS := -Wall
BIN_DIR := exe
SRC_DIRS := $(wildcard *-*)

.PHONY: all clean $(SRC_DIRS)
all: $(SRC_DIRS)

clean:
del /s /q $(BIN_DIR)\*

define SETUP_TARGETS
$(1): $$(patsubst $(1)/%.c,$(BIN_DIR)/%.exe,$$(wildcard $(1)/*.c))
$(BIN_DIR)/%.exe: $(1)/%.c $$(wildcard *.c)
$(CC) $(CFLAGS) $$(addprefix -I ,$$(patsubst %/,%,$$(dir $$^))) $$^ -o $$@
endef

$(foreach target,$(SRC_DIRS),$(eval $(call SETUP_TARGETS,$(target))))

Observations about the makefile:

  • Since you are using the file extension .exe, I am assuming that you are running GNU Make on MS-Windows. Hence, I used Command Prompt syntax and .exe extension.
  • You have to put the makefile in the root directory of the chapter (chapter-1/ in this case).
  • When you copy the code to your makefile, please pay attention to the indented parts (recipe of each rule). Each command line must start with a TAB character, not spaces.
  • I am assuming the directory exe/ already exists and always will be there.
  • The makefile will detect every subdirectory that matches the pattern *-* (e.g. 1-1, 1-2, ...) and will create a target for each one.
  • The command line make target (target is a subdirectory, e.g. 1-1, 1-2, ...) will create one executable in exe/ for each source file in the target subdirectory. I am assuming that each source file has a function main().
  • Each target build will also include every source file *.c from the root directory (ch1.c in this case). I am assuming that these sources do not contain a function main().
  • The files involved in the builds do not have to be named in any particular way. All files will be detected.
  • The include paths used in the compilations are the same directories that contain the sources involved, which are the target subdirectory and the root directory. Hence, you can #include header files from them to your programs.
  • The command lines make and make all will build all the targets.
  • The command line make clean will delete every file in exe/.
  • This makefile was created to minimize build operations so that only executables that are out of date with respect to one of their dependent sources will be built. If you try to rebuild a target without modifying any source, you are going to get a warning like make: Nothing to be done for '1-1'.
  • You can use this makefile for other chapters as well, as long as they have the same directory structure.

Enjoy :)

Creating a makefile - (C)

Rules in a make file are of the form:

target: dependency1 dependency2 etc
command to build target

target is the name of the file you want to build. So the line

program: client.o
gcc client.o -o Client

Is trying to build a file called program. However, the command does not create a file called program, it creates a file called Client. This is less of a problem than you might think, it just means that the rule is always executed whether Client is up to date or not. However, you should change it so the target is the file you are building.

Client: client.o
gcc client.o -o Client

By the way, in most *nixes, file names are case sensitive Client and client are different files on Linux, for example.

That rule has a single dependency: client.o. Unfortunately, your make file does not know how to build client.o - there is no target called client.o.

I am speculating the cause of your error is that you have an old client.o hanging about that doesn't have a main() function in it. This is why the link (the gcc command in the program target) is failing.

The target program.o has the same problem as the target program. You are not building program.o, you are building client.o. This target needs to be changed to

client.o: client.c $(HEADERS)
gcc -c client.c -o client.o

which is happily the dependency for your Client target.

Note The indentation for the command part of a make rule has to be done with a tab. If copy-pasting my answer or any of the other answers, or the answers in the linked question, please make sure your indents are tabs, not spaces.

Update (the issue with test() being an undefined reference)

If you have a function in guiBuilder.c that has a prototype in guiBuilder.h you'll need to compile guiBuilder.c and add it to the link phase.

Your rule for guiBuilder.o will look very similar to the rule for client.o

guiBuilder.o: guiBuilder.c $(HEADERS)
gcc -c guiBuilder.c -o guiBuilder.o

Then you need to add guiBuilder.o as a dependency of Client

Client: client.o guiBuilder.o
gcc client.o guiBuilder.o -o Client

You may have noticed that you now have two rules for creating .o files that are identical other than the names of the source and object files. The accepted answer to the question that you linked shows how you modify the make file so you only need to define the rule once.

How to make a single makefile create 3 output binaries

You can add a phony rule (that is a rule that doesn't build a file itself) which has whatever you want to build as its prerequisites. By convention, this rule is called all in most Makefiles. For your case, it would look like this:

all: serverThreaded server client

It's also common to put this as the very first rule, so if you just type make without a target, it is automatically selected.

An important thing to do is to let make know this rule actually is "phony" by putting it in the prerequisites of the special target .PHONY like this:

.PHONY: all

This is necessary because otherwise, make would expect it to build a file called all. If you'd ever have a file all in your current directory which is newer than all files you actually build, make wouldn't do anything.


Regarding the edit, it doesn't make any sense. Explaining make entirely isn't possible in this Q&A format, so I'll just give you an example how a very basic Makefile could look like, as a start:

CC:= gcc
CFLAGS:= -D_POSIX_SOURCE -Wall -Werror -pedantic -std=c99 -D_GNU_SOURCE -pthread
LIBS:= -pthread

serverThreaded_OBJS:= main.o service_client_socket.o service_listen_socket.o get_listen_socket.o
server_OBJS:= main.o service_client_socket.o service_listen_socket.o get_listen_socket.o
client_OBJS:= client.o

all: serverThreaded server client

serverThreaded: $(serverThreaded_OBJS)
$(CC) -o$@ $^ $(LIBS)

server: $(server_OBJS)
$(CC) -o$@ $^ $(LIBS)

client: $(client_OBJS)
$(CC) -o$@ $^ $(LIBS)

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

clean:
rm -f *.o

.PHONY: all clean

As a side note, it's strange how your serverThreaded and server are built from the exact same sources with the exact same flags -- you will end up with the exact same binaries.



Related Topics



Leave a reply



Submit