前言

对于Windows下开发,很多IDE都集成了编译器,如Visual Studio,提供了“一键编译”,编码完成后只需一个操作即可完成编译、链接、生成目标文件。

Linux开发与Windows不同,Linux下一般用的的gcc/g++编译器,如果是开发ARM下的Linux程序,还需用到arm-linux-gcc/arm-linux-g++交叉编译器。

Linux下也可以实现“一键编译”功能,此时需要一个编译脚本“Makefile”,Makefile可以手动编写,也可以借助自动化构建工具(如scons、CMake)生成。手动编写Makefile是Linux和Windows程序员的区别之一,一般地一个通用的Makefile能够适合大部分Linux项目程序。

这里提供了三个 Makefile 模板

编译可执行文件Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
VERSION  =1.00
CC =gcc
DEBUG =-DUSE_DEBUG
CFLAGS =-Wall
SOURCES =$(wildcard ./source/*.c)
INCLUDES =-I./include
LIB_NAMES =-lfun_a -lfun_so
LIB_PATH =-L./lib
OBJ =$(patsubst %.c, %.o, $(SOURCES))
TARGET =app

#links
$(TARGET):$(OBJ)
@mkdir -p output
$(CC) $(OBJ) $(LIB_PATH) $(LIB_NAMES) -o output/$(TARGET)$(VERSION)
@rm -rf $(OBJ)

#compile
%.o: %.c
$(CC) $(INCLUDES) $(DEBUG) -c $(CFLAGS) $< -o $@

.PHONY:clean
clean:
@echo "Remove linked and compiled files......"
rm -rf $(OBJ) $(TARGET) output

【要点说明】

  • 程序版本

开发调试过程可能产生多个程序版本,可以在目标文件后(前)增加版本号标识。

1
2
VERSION = 1.00
$(CC) $(OBJ) $(LIB_PATH) $(LIB_NAMES) -o output/$(TARGET)$(VERSION)
  • 编译器选择

Linux下为gcc/g++;arm下为arm-linux-gcc;不同CPU厂商提供的定制交叉编译器名称可能不同,如Hisilicon“arm-hisiv300-linux-gcc”。

1
CC = gcc
  • 宏定义

开发过程,特殊代码一般增加宏条件来选择是否编译,如调试打印输出代码。-D是标识,后面接着的是“宏”。

1
DEBUG =-DUSE_DEBUG
  • 编译选项

可以指定编译条件,如显示警告(-Wall),优化等级(-O)。

1
CFLAGS =-Wall -O
  • 源文件

指定源文件目的路径,利用“wildcard”获取路径下所有依赖源文件。

1
SOURCES =$(wildcard ./source/*.c)
  • 头文件

包含依赖的头文件,包括源码文件和库文件的头文件。

1
INCLUDES =-I./include
  • 库文件名称

指定库文件名称,库文件有固定格式,静态库为libxxx.a;动态库为libxxx.so,指定库文件名称只需写“xxx”部分,

1
LIB_NAMES =-lfun_a -lfun_so
  • 库文件路径

指定依赖库文件的存放路径。注意如果引用的是动态库,动态库也许拷贝到“/lib”或者“/usr/lib”目录下,执行应用程序时,系统默认在该文件下索引动态库。

1
LIB_PATH =-L./lib
  • 目标文件

调用“patsubst”将源文件(.c)编译为目标文件(.o)。

1
OBJ =$(patsubst %.c, %.o, $(SOURCES))
  • 执行文件

执行文件名称

1
TARGET =app
  • 编译
1
2
%.o: %.c
$(CC) $(INCLUDES) $(DEBUG) $(CFLAGS) $< -o $@
  • 链接

可创建一个“output”文件夹存放目标执行文件。链接完输出目标执行文件,可以删除编译产生的临时文件(.o)。

1
2
3
4
$(TARGET):$(OBJ)
@mkdir -p output
$(CC) $(OBJ) $(LIB_PATH) $(LIB_NAMES) -o output/$(TARGET).$(VERSION)
@rm -rf $(OBJ)
  • 清除编译信息

执行“make clean”清除编译产生的临时文件。

1
2
3
4
.PHONY:clean
clean:
@echo "Remove linked and compiled files......"
rm -rf $(OBJ) $(TARGET) output

编译静态库Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
VERSION     =
CC =gcc
DEBUG =
CFLAGS =-Wall
AR =ar
ARFLAGS =rv
SOURCES =$(wildcard *.c)
INCLUDES =-I.
LIB_NAMES =
LIB_PATH =
OBJ =$(patsubst %.c, %.o, $(SOURCES))
TARGET =libfun_a

#link
$(TARGET):$(OBJ)
@mkdir -p output
$(AR) $(ARFLAGS) output/$(TARGET)$(VERSION).a $(OBJ)
@rm -rf $(OBJ)

#compile
%.o: %.c
$(CC) $(INCLUDES) $(DEBUG) -c $(CFLAGS) $< -o $@

.PHONY:clean
clean:
@echo "Remove linked and compiled files......"
rm -rf $(OBJ) $(TARGET) output

【要点说明】

基本格式与“编译可执行Makefile”一致,不同点包括以下。

  • 使用到“ar”命令将目标文件(.o)链接成静态库文件(.a)。静态库文件固定命名格式为:libxxx.a。

编译动态库Makefile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
VERSION   =
CC =gcc
DEBUG =
CFLAGS =-fPIC -shared
LFLAGS =-fPIC -shared
SOURCES =$(wildcard *.c)
INCLUDES =-I.
LIB_NAMES =
LIB_PATH =
OBJ =$(patsubst %.c, %.o, $(SOURCES))
TARGET =libfun_so

#link
$(TARGET):$(OBJ)
@mkdir -p output
$(CC) $(OBJ) $(LIB_PATH) $(LIB_NAMES) $(LFLAGS) -o output/$(TARGET)$(VERSION).so
@rm -rf $(OBJ)

#compile
%.o: %.c
$(CC) $(INCLUDES) $(DEBUG) -c $(CFLAGS) $< -o $@

.PHONY:clean
clean:
@echo "Remove linked and compiled files......"
rm -rf $(OBJ) $(TARGET) output

【要点说明】

基本格式与“编译可执行Makefile”一致,不同点包括以下。

  • 编译选项和链接选项增加“-fPIC -shared ”选项。动态库文件固定命名格式为libxxx.so。