I want to use GNU make to compile a static library. On my disc, the project is arranged this way:
.
├── makefile
├── publicAPI
│ └── Some headers (.h)
└── source
├── All .cpp files
└── Some headers (.h)
I have written the following makefile, so far:
CPPC = gcc
STANDARD_FLAGS = -std=c++0x
WARN_AS_ERRORS_FLAGS = -pedantic-errors\
-Wall\
-Wextra\
-Werror\
-Wconversion
DEBUG_FLAGS = -g3
OPT_FLAGS = -0O
NO_LINKER_FLAGS = -c
CPPFLAGS = $(OPT_FLAGS) $(DEBUG_FLAGS) $(NO_LINKER_FLAGS) $(STANDARD_FLAGS) \
$(WARN_AS_ERRORS_FLAGS)
# Source files, headers, etc.:
ROOT = $(CURDIR)
INCLUDES = -I$(ROOT)source -I$(ROOT)publicAPI
SRCS = ./source/AsciiColorCode.cpp\
./source/Color.cpp
OBJS = AsciiColorCode.o\
Color.o
LIBS =
MAIN = libcXbase.a # static library
all: $(MAIN)
#echo $(MAIN) has been compiled!
$(MAIN): $(OBJS)
ar -r $(MAIN) $(OBJS) $(LIBS)
.cpp.o:
$(CPPC) $(CPPFLAGS) $(INCLUDES) -c $< -o $#
depend: $(SRCS)
makedepend $(INCLUDES) $^
When I run make all, I get the following error: make: *** No rule to make target 'AsciiColorCode.o', needed by 'libcXbase.a'. Stop. which indicates that the AsciiColorCode.o file has not been created. I can't find what is missing from this:
I find the dependencies
I compile .o object files from .cpp files
I ar everything into a .a file.
What is missing? Also, if you have any comment on my makefile, please don't hesitate as this is my first one.
Regards
You should specify where the prerequisites are. This can be done by adding this line:
VPATH = source
Also, in the rule .cpp.o, the variable CPCC is not defined (probably should be CXX)
Also, OPT_FLAGS should be -O0
Related
I have created a Makefile for the below code structure
work
├── code
| |
| ├──inc/
| | └── main.h and test.h files here
| |
| ├──src/
│ └── main.c and test.c files here
├── _Build/
│ └── Makefile here
Here is the Makefile
# All path are referenced with the directory path of Makefile
# Directory Path for workspace
WORKSPACE = ..
# Directory path for code
PATH_DIR_CODE = $(WORKSPACE)/code
# Directory path for c source files
PATH_DIR_C_SOURCES = $(PATH_DIR_CODE)/src
# Directory path for c header files
DIR_PATH_C_HEADERS = $(PATH_DIR_CODE)/inc
# Directory path for obj files
DIR_PATH_OBJ = $(WORKSPACE)/obj
# Directory path for executables
DIR_PATH_BIN = $(WORKSPACE)/bin
# Executable name declaration
FILE_PATH_EXE = $(DIR_PATH_BIN)/main
# Command mkdir
MKDIR = mkdir
FILE_PATH_C_HEADER = $(shell find $(PATH_DIR_CODE) -name *.h)
DIR_PATH_C_HEADER = $(patsubst %/,%,$(sort $(dir $(FILE_PATH_C_HEADER))))
FILE_PATH_C_SRC = $(shell find $(PATH_DIR_CODE) -name *.c)
DIR_PATH_C_SRC = $(patsubst %/,%,$(sort $(dir $(FILE_PATH_C_SRC))))
INC_FILE_C_HEADER = $(addprefix -I, $(DIR_PATH_C_HEADER))
FILE_PATH_OBJ = $(patsubst $(DIR_PATH_C_SRC)/%.c, $(DIR_PATH_OBJ)/%.o, $(FILE_PATH_C_SRC))
CC = gcc
CFLAGS = -Werror -Wall
CDEPS = -MMD -MP -MF $(#:.o=.d)
LDFLAGS = -Llib
LDLIBS = -lm
MKDIR = mkdir
-include $(FILE_PATH_OBJ:.o=.d)
all : $(FILE_PATH_EXE)
.PHONY : all
$(FILE_PATH_EXE) : $(FILE_PATH_OBJ) | $(DIR_PATH_BIN)
$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $#
$(DIR_PATH_OBJ)/%.o : $(DIR_PATH_C_SRC)/%.c | $(DIR_PATH_OBJ)
$(CC) $(CFLAGS) -c $< $(CDEPS) -o $#
$(DIR_PATH_BIN) $(DIR_PATH_OBJ):
$(MKDIR) -p $#
clean :
$(RM) -rv $(DIR_PATH_BIN) $(DIR_PATH_OBJ)
Based on tutorial for dependencies I have used the flags
CDEPS = -MMD -MP -MF $(#:.o=.d)
and
-include $(FILE_PATH_OBJ:.o=.d)
still I am getting the following error
mkdir -p ../obj
gcc -Werror -Wall -c ../code/src/main.c -MMD -MP -MF ../obj/main.d -o ../obj/main.o
../code/src/main.c:4:10: fatal error: test.h: No such file or directory
#include "test.h"
^~~~~~~~
compilation terminated.
make: *** [Makefile:56: ../obj/main.o] Error 1
To remove this error what should be included in the Makefile?
Dependencies should be removed by this logic or some other logic should be used?
You are conflating two different things.
The .d files tell make where to look for prerequisites of the target. In this case the target is an object file (.o) and the prerequisite is a header file, but to make they're just "targets" and "prerequisites". Make is not restricted to just compiling C programs: it can do essentially any task where changing some files means that some other files need to be updated: compiling programs (not just C programs) is one common use but it can build documentation, web sites, run tests, etc. Make does its job by running commands, just as you would do it yourself from the command line (except make never forgets to add an option and doesn't make typos). It doesn't really know anything about "compilers" and "linkers", internally.
The error you are getting is from the compiler (or to be pedantic, the preprocessor), not make, and the compiler has to be told where to look for the header files it needs to include. Those are two completely different things and require different operations: the compiler requires that you provide the directories to search using the -I command line option.
I suppose it might be nice if the compiler could parse make's .d files and figure out where to look for headers, but it can't. You have to specify the flags yourself.
In your situation it's even more clear: you are actually using the compiler to generate the .d files! So there's a chicken-and-egg problem: if the compiler could get the paths from the .d files, but the .d files are being created from the compiler, then where do the paths come from in the first place?
I want Make to automatically compile protos when I update them, here is what I've got so far:
TARGET=main
BIN_DIR=bin
SRC_DIR=src
OBJ_DIR=obj
PROTO_DIR=protos/
PROTO_COMPILE_DIR=src/$(PROTO_DIR)
CC = g++
CFLAGS = -Wall -std=c++17 -ggdb -pipe -I.
LINKER = g++
LFLAGS = $(CFLAGS) -lprotobuf
SOURCES = $(wildcard src/*.cc) \
$(wildcard src/protos/*.cc) \
$(wildcard src/db_handler/*.cc)
OBJECTS := $(SOURCES:$(SRC_DIR)/%.cc=$(OBJ_DIR)/%.o)
$(BIN_DIR)/$(TARGET): proto $(OBJECTS)
#mkdir -p $(BIN_DIR)/
$(LINKER) $(OBJECTS) $(LFLAGS) -o $#
$(OBJECTS): $(OBJ_DIR)/%.o : $(SRC_DIR)/%.cc
#mkdir -p obj/ obj/protos obj/db_handler
$(CC) $(CFLAGS) -c $< -o $#
.PHONY: proto
proto:
#printf "Compiling protos...\n"
#cd $(PROTO_DIR) && protoc * --grpc_out=../$(PROTO_COMPILE_DIR)\
--cpp_out=../$(PROTO_COMPILE_DIR)\
--plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin"\
&& cd ../
It successfully compiles protos; but, it does so every time, even if there are no changes in files. How can I prevent this and compile protos only if protos change?
Edit: Added project structure
├── LICENSE
├── makefile
├── protos
│ ├── client.proto
│ └── person.proto
├── README.md
└── src
├── db_handler
│ ├── db_handler.cc
│ └── db_handler.h
├── main.cc
└── protos
├── client.grpc.pb.cc
├── client.grpc.pb.h
├── client.pb.cc
├── client.pb.h
├── person.grpc.pb.cc
├── person.grpc.pb.h
├── person.pb.cc
└── person.pb.h
You want to add the source files of the protocol buffers to the right (the prerequisites) of the rule that cares about them. This is how Make understands and tracks their time stamps.
By adding the files to the prerequisites, Make will understand that this is the rule which cares about those source files.
PROTO_SOURCES := $(wildcard $(PROTO_DIR)/*.proto)
PROTOS := $(patsubst $(PROTO_DIR)/%.proto,$(PROTO_COMPILE_DIR)/%.cc,$(PROTO_SOURCES))
$(PROTOS): $(PROTO_SOURCES)
#printf "Compiling protos...\n"
#cd $(PROTO_DIR) && protoc * --grpc_out=../$(PROTO_COMPILE_DIR)\
--cpp_out=../$(PROTO_COMPILE_DIR)\
--plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin"
However, this $(PROTOS) : $(PROTO_SOURCES) is not good if you use parallel builds; because, Make will try to run the command once for each output file. So, make will run N instances of the command at the same time, which means they may clobber each other.
In order to know the fully correct solution you need to provide more information (for those not familiar with protoc). Is it a requirement that you invoke protoc once with all inputs? Or is it valid to run protoc individually on each input .proto file to get its output? Then you can write a pattern rule that generates one file at a time.
Note 1: As to your other attempt, if you use .PHONY to mark the rule then Make will rebuild the rule every time weather it needs to or not.
Note 2: You don't need the cd .. at the end of the second instruction, since it is run in a sub shell.
Thanks to #FiddlingBits I managed to figure out how to do it correctly
TARGET=main
BIN_DIR=bin
SRC_DIR=src
OBJ_DIR=obj
PROTO_DIR=protos/
PROTO_COMPILE_DIR=src/$(PROTO_DIR)
rm = rm -f
CC = g++
CFLAGS = -Wall -std=c++17 -ggdb -pipe -I.
LINKER = g++
LFLAGS = $(CFLAGS) -lprotobuf
SOURCES = $(wildcard src/*.cc) \
$(wildcard src/protos/*.cc) \
$(wildcard src/db_handler/*.cc)
OBJECTS := $(SOURCES:$(SRC_DIR)/%.cc=$(OBJ_DIR)/%.o)
PROTOS := $($(PROTO_DIR)/%.proto=$(PROTO_COMPILE_DIR)/%.cc)
$(BIN_DIR)/$(TARGET): $(PROTOS) $(OBJECTS)
echo $(PROTOS)
#mkdir -p $(BIN_DIR)/
$(LINKER) $(OBJECTS) $(LFLAGS) -o $#
$(OBJECTS): $(OBJ_DIR)/%.o : $(SRC_DIR)/%.cc
#mkdir -p obj/ obj/protos obj/db_handler
$(CC) $(CFLAGS) -c $< -o $#
$(PROTOS):
#printf "Compiling protos...\n"
#cd $(PROTO_DIR) && protoc * --grpc_out=../$(PROTO_COMPILE_DIR)\
--cpp_out=../$(PROTO_COMPILE_DIR)\
--plugin=protoc-gen-grpc="/usr/local/bin/grpc_cpp_plugin"\
&& cd ../
.PHONY: clean
clean:
#$(rm) -r $(OBJ_DIR)/*
#$(rm) -r $(BIN_DIR)/*
#printf "Cleanup complete!\n"
I have the following directory structure for a dummy C project.
.
├── inc
│ ├── getmsg.c
│ └── getmsg.h
├── makefile
└── src
└── main.c
My current generic Makefile is below,
TARGET = main
# finds all .c files, inc/getmsg.c src/main.c
SOURCES := $(shell find * -name *.c)
# converts all .c files to .o files, inc/getmsg.o src/main.o
OBJECTS := $(SOURCES:.c=.o)
# directories that contain .h files for gcc -I flag, inc/
HEADERS := $(dir $(shell find * -name *.h))
CC = gcc
CFLAGS = -Wall -std=c99 -iquote "$(HEADERS)"
all: $(TARGET)
%.o: %.c
$(CC) $(CFLAGS) -c $< -o $#
$(TARGET): $(OBJECTS)
$(CC) -o $# $^
clean:
rm -rf $(shell find * -name *.o) $(TARGET)
This all compiles fine however it just dumps the .o files into the same directory as its corresponding .c file.
What I would like to do is have all object files put into a build directory. To do this I change the OBJECTS to OBJECTS := $(patsubst %,build/%,$(notdir $(SOURCES:.c=.o))) which lists the files build/getmsg.o build/main.o. Then I set the %.o target to build/%.o: %.c.
This however returns No rule to make target 'build/getmsg.o'. So the make file is unable to build the .o files. What am I missing here?
Try changing
%.o: %.c
to
build/%.o: %.c
Here is my folder view:
-> tree
.
├── Makefile
└── src
├── a10.c
├── a11.c
├── a12.c
├── a13.c
├── a14.c
├── a15.c
├── a16.c
├── a1.c
├── a2.c
├── a3.c
├── a4.c
├── a5.c
├── a6.c
├── a7.c
├── a8.c
├── a9.c
├── a.c
└── test.h
a1.c to a10.c are all empty, just test, only a.c and test.h have code:
-> cat src/a.c
#include "test.h"
int main(void)
{
printf("VAR = %d\n", VAR);
printf("VAR1 = %d\n", VAR1);
return 0;
}
-> cat src/test.h
#include <stdio.h>
I have a makefile with a function:
-> cat Makefile
ifeq '${INPUT}' ''
STR = LATEAST
else
STR = ${INPUT}
endif
CC := gcc
LD := gcc
PROGRAM = Test
MAKE_DIR = $(PWD)
MODULES := src
SRC_DIR := $(addprefix $(MAKE_DIR)/,$(MODULES))
BUILD_DIR := $(MAKE_DIR)/output
SRC := $(foreach sdir,$(SRC_DIR),$(wildcard $(sdir)/*.c))
OBJ := $(patsubst %.c,%.o,$(SRC))
INCLUDES := $(addprefix -I,$(SRC_DIR))
INCLUDES += -I$(MAKE_DIR)/include
#test
F_VAR = 43
F_VAR1 = 40+1+2
$(warning F_VAR=$(F_VAR), F_VAR1=$(F_VAR1))
CFLAGS :=
CFLAGS = -DVAR=41+2 -DVAR1=40+1+2
vpath %.c $(SRC_DIR)
define make-goal
$1/%.o: %.c
#$(CC) $(CFLAGS) $(INCLUDES) -c $$< -o $$#
#$(CC) -MM $(CFLAGS) $(INCLUDES) $$< > $1/$$*.d
#echo "Compile $$*.c"
endef
.PHONY: all checkdirs clean help flowchart
all: checkdirs $(BUILD_DIR)/$(PROGRAM)
$(BUILD_DIR)/$(PROGRAM): $(OBJ)
#echo "${STR}"
#$(LD) $^ -o $#
#echo "Generate $(PROGRAM)"
checkdirs: $(BUILD_DIR)
$(BUILD_DIR):
mkdir -p $#
clean:
rm -f $(OBJ) $(BUILD_DIR)/$(PROGRAM)
rm -rf $(BUILD_DIR)
help:
#echo "SRC DIR: $(SRC_DIR)"
#echo "Build DIR: $(BUILD_DIR)"
#echo "Source: $(SRC)"
#echo "Obj: $(OBJ)"
#echo "Includes: $(INCLUDES)"
flowchart:
#cflow2dot pdf ${SRC}
$(foreach sdir,$(SRC_DIR),$(eval $(call make-goal,$(sdir))))
My quesation is, when I touched a header file, it did not trigger a re-compilation, any problem in my makefile?
-> make
Makefile:28: F_VAR=43, F_VAR1=40+1+2
mkdir -p /home/haochen/Work/test_code/test_make/output
Compile a10.c
Compile a11.c
Compile a12.c
Compile a13.c
Compile a14.c
Compile a15.c
Compile a16.c
Compile a1.c
Compile a2.c
Compile a3.c
Compile a4.c
Compile a5.c
Compile a6.c
Compile a7.c
Compile a8.c
Compile a9.c
Compile a.c
LATEAST
Generate Test
the Autodependenciescan be generated:
-> cat src/a.d
a.o: /home/haochen/Work/test_code/test_make/src/a.c \
/home/haochen/Work/test_code/test_make/src/test.h
Problem:
I modified test.h, add a error:
-> vim src/test.h
-> cat src/test.h
#include <stdio.h>
#error here
No re-compile:
-> make
Makefile:28: F_VAR=43, F_VAR1=40+1+2
make: Nothing to be done for `all'.
UPDATE:
I add:
DEP := $(patsubst %.c,%.d,$(SRC))
Meanwhile, I add -include at my last line of make file:
-include $(DEP)
I did not see any improvement
The .d files are makefiles that express the header-dependencies. So you need to -include
the .d files in the makefile, e.g.
OBJ := $(patsubst %.c,%.o,$(SRC))
DEP := $(patsubst %.c,%.d,$(SRC))
...
-include $(DEP)
The dependencies files are properly generated, but you never use them : with #$(CC) -MM $(CFLAGS) $(INCLUDES) $$< > $1/$$*.d, make give the command to generate the dependency file, but it does not use it.
The dependencies files are little Makefiles of themselves, destined to be read by make. So, you need to tell make to read them as well as your main Makefile.
For this, you have the include directive, which suspends the reading of the current makefile and read all listed files as makefiles before resuming reading of the current makefile(I'm quoting the manual here...).
Since the dependencies files won't exist on a first build, you might want to add - in front of it, to nullify the warning saying no such file.
As a side note, be sure to include the dependencies file after your default target (or to use .DEFAULT_GOAL), because if you don't, the default target will become the first found in the included files.
Also, you might want to add your dependencies files as prerequisites of your object files, since a missing dependency file will make make miss some prerequisites.
All of this is from the make maintainer's blog, here, where you can find additional info.
EDIT :
Sorry, I've missed it last time. If I'm not wrong, a.o and $1/%.o cannot match the same target. Even though the Makefile include files in a subdir, it did not automatically know that, and to him a.o and src/a.o are not the same (and indeed). So there is two separate rules, and one is empty, with no recipe (the one in the dependency file)
One solution might be to use the -MT flag for the dependency generation (which set the target to the string you specify, so $# here for example.)
You might want to use the --debug option of make to catch that kind of errors ; so far, I've found it quite useful to track incorrect dependencies.
I found the problem, I should:
write the deps
DPES = ...
compile c file with -MMD option:
... -MMD -c $< -o $#
and then, add:
-include $(DPES)
After a bit of searching, I've managed to throw together the following Makefile:
CC = gcc
CFLAGS = -c -Wall
LDFLAGS =
SOURCEFILES = main.c
SOURCES = src/$(SOURCEFILES)
OBJECTS = $(SOURCES:.c=.o)
EXECUTABLE = netsim
all: $(SOURCES) $(EXECUTABLE)
$(EXECUTABLE): $(OBJECTS)
$(CC) $(LDFLAGS) $(OBJECTS) -o $#
.c.o:
$(CC) $(CFLAGS) $< -o $#
clean:
rm -rf netsim $(OBJECTS)
I would like to be able to move my object files into another directory, but have been struggling with getting that to work. What am I missing?
The trick is to not move your objects.
You should build it and use it from where they are built.
For example you have the following directory structure:
$ tree .
├── Makefile
├── include
│ └── common_head.h
├── obj
└── src
├── foo.c
└── main.c
Manual execution:
$ gcc -o ./obj/foo.o -c ./src/foo.c -I ./include # Build Object #
$ gcc -o ./obj/main.o -c ./src/main.c -I ./include
$ gcc -o exe ./obj/foo.o ./obj/main.o # Build Executable #
Makefile to simulate the above
C_FLAGS := -g -Wall -Wextra
CC := gcc
RM := rm
LINKFLAGS := -lanylibrary
.PHONY: $(TARGET)
.PHONY: clean
VPATH:= ./src/ ./obj/ ./include/
# Path for .c , .h and .o Files
SRC_PATH := ./src/
OBJ_PATH := ./obj/
INC_PATH := -I ./include
# Executable Name
TARGET := exe
# Files to compile
OBJ1 := foo.o \
main.o
OBJ := $(patsubst %,$(OBJ_PATH)%,$(OBJ1))
# Build .o first
$(OBJ_PATH)%.o: $(SRC_PATH)%.c
#echo [CC] $<
#$(CC) $(C_FLAGS) -o $# -c $< $(INC_PATH)
# Build final Binary
$(TARGET): $(OBJ)
#echo [INFO] Creating Binary Executable [$(TARGET)]
#$(CC) -o $# $^ $(LINKFLAGS)
# Clean all the object files and the binary
clean:
#echo "[Cleaning]"
#$(RM) -rfv $(OBJ_PATH)*
#$(RM) -rfv $(TARGET)
So when you do a Make
$ make -B
[CC] src/foo.c
[CC] src/main.c
[INFO] Creating Binary Executable [exe]
To see a dry-run use make -n
$ make clean ; make -n
g++ -g -Wall -Wextra -o obj/foo.o -c src/foo.c -I ./include
g++ -g -Wall -Wextra -o obj/main.o -c src/main.c -I ./include
g++ -o exe obj/foo.o obj/main.o -lanylibrary
So after building your directory structure should look like this.
$ tree .
├── Makefile
├── exe
├── include
│ └── common_head.h
├── obj
│ ├── foo.o
│ └── main.o
└── src
├── foo.c
└── main.c
So from my previous answer.
You don't have to use any PHONY move and also no objects are recreated unnecessarily.
Something like this?
SOURCES = src/main.c
OBJECTS = obj/main.o
...
obj/%.o: src/%.c
$(CC) $(CFLAGS) $< -o $#
Once that's working, you can add further tricks, like this:
OBJECTS = $(patsubst src/%.c, obj/%.o, $(SOURCES)