通用makefile

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经我编译运行过, 可以成功运行,编译环境也在开始处列出,如果有问题,欢迎指正,如果有更好的写法,请给我留言,希望此模板对大家有所帮助

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值