makefile是编译大型C/C++项目的重要工具,虽说现在有cmake等自动编译工具,但是,掌握makefile的写法,依然是C/C++程序开发者必备的技能.
现以一个完整的例子来说明用makefile生成静态库(.a文件)以及可执行文件.
编译环境:
操作系统版本: Debian 3.2.54-2 x86_64
GCC版本: gcc version 4.7.2
目录及文件组织如下
add.h
add.cpp
minus.h
minus.cpp
main.cpp
Makefile
utils/utils.h
utils/utils.cpp
utils/Makefile
包含当前目录及一个子目录utils, 其中,utils下面的文件将被编译为一个静态库libztx.a
各文件内容如下
$ cat add.h
#ifndef ADD_H
#define ADD_H
#include <stdio.h>
int add(int a, int b);
int add_1();
int add_2();
#endif
$ cat add.cpp
#include "add.h"
int add(int a, int b)
{
return a+b;
}
$ cat minus.h
#ifndef MINUS_H
#define MINUS_H
#include <stdio.h>
int minus(int a, int b);
#endif
$ cat minus.cpp
#include "minus.h"
int minus(int a, int b)
{
return a-b;
}
$ cat main.cpp
#include <stdio.h>
#include <stdint.h>
#include "add.h"
#include "utils.h"
int main()
{
uint32_t now = Utils::now();
uint32_t now1 = time(NULL);
int sum = add(1, 2);
printf("hellow makefile, time:%u, now:%u, sum:%d\n", now1, now, sum);
return 0;
}
$ cat utils/utils.h
#ifndef UTILS_H
#define UTILS_H
#include <stdint.h>
#include <string>
class Utils
{
public:
static uint32_t now();
static uint32_t to_number(std::string str);
};
#endif
$ cat utils/utils.cpp
#include <time.h>
#include <stdlib.h>
#include "utils.h"
uint32_t Utils::now()
{
return time(NULL);
}
uint32_t Utils::to_number(std::string str)
{
return atoi(str.c_str());
}
main函数在当前目录的main.cpp文件中
再看看当前目录和子目录下的两个makefile
当前目录下的Makefile
$ cat Makefile
CC=g++
RM=rm
EXE=hello
SRC_DIR=./
OBJ_DIR=.objs
DEP_DIR=.objs
INC_DIR=-I./ -I./utils
CCFLAGS= -g -Wall
## refer to wplan
SRC=$(wildcard *.cpp)
OBJS=$(addprefix $(OBJ_DIR)/, $(patsubst %.cpp, %.o, $(notdir $(SRC))))
DEPS=$(addprefix $(DEP_DIR)/, $(patsubst %.cpp, %.d, $(notdir $(SRC))))
LIBS = -L./utils -lztx -lpthread -lmysqlclient ## 如果用不到线程库和mysql库,这两个库可以不用链接进来
all: $(EXE)
${EXE}:${OBJS} ./utils/libztx.a
@##$(CC) $(CFLAGS) -o $@ $^ $(LIBS)
@## 因为生成EXE需要所有.o文件参与所以下面的指令使用$^, 而不应该用第一个依赖$<
@echo "object files: ${OBJS}"
${CC} -g -Wall -o $@ $^ $(LIBS)
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CC) -c -g -Wall $< -o $@ $(INC_DIR)
$(DEP_DIR)/%.d:$(SRC_DIR)/%.cpp
@set -e; rm -f $@; \
$(CC) -MM $(INC_DIR) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,$(OBJ_DIR)/\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
-include $(DEPS) ## include all dep files in the makefile
## rule to generate a dep file by using C preprocessor
## see man cpp for details on the -MM and -MT options
.PHONY: clean
clean:
${RM} -f ${EXE} ${OBJS} ${DEPS}
## next is annotation for this Makefile
## $@: 目标文件名称
## $^: 所有的依赖文件,以空格分开,不包含重复的依赖文件
## $<: 第一个依赖文件的名称
## patsubst: 替换通配符, 把变量$(SRCS)中符合后缀是.cpp的文件全部替换成.o
## end of Makefile
当前目录下的Makefile用来生成可执行文件hello, 要引用到子目录utils下面的库,用来获得当前时间
也就是main函数里面的 uint32_t now = Utils::now(); 这行需要用到utils下面的静态库
再看看子目录utils目录下面的Makefile
$ cat utils/Makefile
CC=g++
RM=rm
TARGET=libztx.a
SRC_DIR=./
INC_DIR=-I./
OBJ_DIR=../.objs
DEP_DIR=../.objs
AR=ar
CPPFLAGS = -g -Wall
SRC=$(wildcard *.cpp)
OBJS=$(addprefix $(OBJ_DIR)/, $(patsubst %.cpp, %.o, $(notdir $(SRC))))
DEPS=$(addprefix $(DEP_DIR)/, $(patsubst %.cpp, %.d, $(notdir $(SRC))))
all: $(TARGET)
$(TARGET):$(OBJS)
@echo "\n\n"
@echo "first rely file" $<
$(AR) -cr $@ $^
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.cpp
$(CC) -c -g -Wall $(INC_DIR) $< -o $@
$(DEP_DIR)/%.d : $(SRC_DIR)/%.cpp
@set -e; rm -f $@; \
$(CC) -MM $(INC_DIR) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,$(OBJ_DIR)/\1.o $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$
sinclude $(DEPS)
.PHONY: clean
clean:
$(RM) -f $(TARGET) $(OBJS) $(DEPS)
## end of Makefile in utils
编译方法:
进入子目录, 运行utils/Makefile, 生成静态库 libztx.a
$ cd utils
$ make
此时可以看到utils目录下面生成了libztx.a
在进入当前目录,运行Makefile, 生成可执行文件
$ cd ..
$ make
可以看到当前目录生成了可执行文件hello
运行可执行文件,得到结果
$ ./hello
hellow makefile, time:1545631453, now:1545631453, sum:3
以上就是整个工程的代码文件和makefile文件,在指定的编译环境下可运行,也可以作为大型工程的模板.
该makefile模板有如下功能:
1. 当头文件改变后(即.h文件), 再次执行make命令,会导致相关的源文件重新编译, 比如在add.h中加一个空行(即敲一个回车), 则add.cpp main.cpp两个文件会重新编译, 然后重新生成可执行文件
2. 修改某个源文件(即.cpp文件)时,再次执行make命令,只有这个文件重新编译,然后重新生成可执行文件,这样就大大节省了编译时间
3. 可以链接自定义的静态库(上面的utils目录下的libztx.a)并使用静态库中的函数(Utils::now()函数)
以上makefile经我编译运行过, 可以成功运行,编译环境也在开始处列出,如果有问题,欢迎指正,如果有更好的写法,请给我留言,希望此模板对大家有所帮助