一、测试代码
本文以将main.c add.c common.h 三个文件编译成一个可执行文件为例,来讲解Makefile的编写。以下是每个文件的代码(很简单,测试用)
1.main.c
#include <stdio.h>
#include "common.h"
int main()
{
int a = 1;
int b = 2;
printf("add(%d, %d) = %d\n",a, b, add(a,b));
return 0;
}
2.add.c
int add(int a, int b)
{
return a+b;
}
3.common.h
#ifndef __COMMON_H__
#define __COMMON_H__
extern int add(int a, int b);
#endif
二、.c 文件与Makefile同级
将.c 文件放在Makefile同级目录,然后单独建一个目录.h头文件,如图。 以下是 Makefile 原文:
CC = gcc
TARGET = prog
OBJS = main.o add.o
INCLUDE = -I./include
$(TARGET):$(OBJS)
$(CC) $(OBJS) -o $(TARGET)
%.o:%.c
$(CC) $(INCLUDE) -c $^ -o $@
.PHONY:clean
clean:
rm *.o prog
逐行解释:
-
CC = gcc: = 号在Makefile中是声明变量并给变量赋值的意思。这里声明了一个 自定义变量 CC,并用gcc编译器赋值。 -
TARGET = prog:声明Makefile目标文件,也就是这个Makefile最终目标是生成一个叫prog的文件。一般这种最终输出的文件都用TARGET 来承载 -
INCLUDE = -I./include :用-I 选项指定自己写的头文件的路径。-I 和路径之间有没有空格都行,一般没有空格 -
**
(
T
A
R
G
E
T
)
:
(TARGET):
(TARGET):(OBJS) ** ** $(CC) $(OBJS) -o $(TARGET) **: 格式
要生成的目标文件:生成目标文件所需要依赖的所有文件
编译命令
符号 | 描述 |
---|
$^ | 所有依赖文件的集合,用空格分开,如果其中有重复的依赖文件,则只保留一个。比如依赖文件有三个: 1.c, 2.c, 1.c,那么$^拿到的只有 1.c 和 2.c 两个文件,舍弃了一个1.c | $< | 依赖文件中的第一个文件。如果依赖文件是以 “%” 定义的,那么 $< 就是依赖文件的集合。一般不会有重复的依赖文件,所以上面的 Makefile,用 $^ 替换掉 $< 也是可以的 | $+ | 与 $^ 一样,只是如果有重复的依赖文件,不会舍弃重复的依赖文件 | $@ | 目标文件的集合 |
三、.c文件与Makefile不同级
.c 文件放在src 目录,.h 文件放在include 目录,目录结构如图: 以下是 Makefile 原文:
CC = gcc
TARGET = prog
SOURCE = $(wildcard ./src/*.c)
OBJS = $(patsubst %.c, %.o, $(SOURCE))
INCLUDE = -I./include
$(TARGET):$(OBJS)
$(CC) $(OBJS) -o $(TARGET)
%.o:%.c
$(CC) $(INCLUDE) -c $^ -o $@
.PHONY:clean
clean:
rm $(OBJS) $(TARGET)
这里只有第3、4行需要注意,相当于用这两行内容可以找到指定目录下的文件
- 第3行
wilcard 是获取src目录下所有的.c文件 - 第4行
patsubst 是列出 SOURCE 中 .c文件对应的所有 .o 文件
四、链接静态库
在前面的基础上,新建目录lib ,创建sub.c 将sub.c 编译成静态库libsub.a ,代码如下:
int sub(int a, int b)
{
return a - b;
}
编译成静态库的命令
gcc -c sub.c -o sub.o
ar crv libsub.a sub.o
补充: 如果静态库依赖于多个源文件,则依次编译得到.o,然后将所有的.o编译成 .a 静态库。
eg. 要将1.c、2.c、3.c编译成一个静态库,则ar crv libXXX.a 1.o 2.o 3.o
当然,除了用命令编译,也可以写成在lib 目录下写一个Makefile,我就是这么做的,如下。(这个Makefile只用于编译静态库)
CC = gcc
TARGET = libsub.a
OBJS = sub.o
$(TARGET):$(OBJS)
ar crv $(TARGET) $(OBJS)
%.o:%.c
$(CC) -c -o $@ $<
.PHONY:clean
clean:
rm *.o $(TARGET)
接下来,就是修改我们最开始的那个Makefile
CC = gcc
TARGET = prog
SOURCE = $(wildcard ./src/*.c)
OBJS = $(patsubst %.c, %.o, $(SOURCE))
INCLUDE = -I./include
LIBS = -lsub
LIBPATH = -L./lib
$(TARGET):$(OBJS)
$(CC) $(OBJS) -o $(TARGET) $(LIBPATH) $(LIBS)
%.o : %.c
$(CC) $(INCLUDE) -c -o $@ $<
.PHONY:clean
clean:
rm $(OBJS) $(TARGET)
第5、6行,编译时需要用 -L 指定静态库的路径,用 -l 指定静态库。 注意: 链接静态库是编译的最后一步链接 做的事情。所以要加在第10行,而且只能加在末尾,如果直接跟在 $(CC) 后面,make 时会提示找不到静态库里的函数
|