Linux学习 – Makefile
什么是makefile
什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的programmer,makefile还是要懂。这就好像现在有这么多的HTML的编辑器,但如果你想成为一个专业人士,你还是要了解HTML的标识的含义。特别在Unix下的软件编译,你就不能不自己写makefile了,会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。
因为,makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
makefile的作用
make命令执行时,需要一个 Makefile 文件,以告诉make命令需要怎么样的去编译和链接程序。
1)如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。
2)如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。
3)如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。
只要我们的Makefile写得够好,所有的这一切,我们只用一个make命令就可以完成,make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。
makefile的规则
在讲述这个Makefile之前,还是让我们先来粗略地看一看Makefile的规则。
target ... : prerequisites ...
command
...
...
target也就是一个目标文件,可以是Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。
prerequisites就是,要生成那个target所需要的文件或是目标。
command也就是make需要执行的命令。(任意的Shell命令)
这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。
说到底,Makefile的东西就是这样一点,好像我的这篇文档也该结束了。呵呵。还不尽然,这是Makefile的主线和核心,但要写好一个Makefile还不够,我会以后面一点一点地结合我的工作经验给你慢慢到来。内容还多着呢。:)
一个简单的makefile的进化过程
Demo目录下有如下源码
1. main.c
2. sub.h sub.cpp
3. add.h add.cpp
代码如下
main.c
#include <stdio.h>
#include "sub.h"
#include "add.h"
int main()
{
printf("Add: %d\n",add(5,3));
printf("Sub: %d\n",sub(5,3));
return 0;
}
sub.h
#ifndef __SUB_H__
#define __SUB_H__
int sub(int l, int r);
#endif
sub.cpp
#include "sub.h"
int sub(int l, int r)
{
return l+r;
}
add.h
#ifndef __ADD_H__
#define __ADD_H__
int add(int l, int r);
#endif
add.cpp
#include "add.h"
int sub(int l, int r);
gcc -c hello.c -o hello.o
利用GCC编译要执行如下指令
gcc -Wall -g -c sub.c -o sub.o
gcc -Wall -g -c add.c -o add.o
gcc -Wall -g -c main.c -o main.o
gcc -Wall -g main.o sub.o add.o -o main
一个最简单的最直观的makefile就是直接对上述指令进行封装
makefile 第一版本
main: main.o sub.o add.o
gcc -Wall -g *.o -o main
main.o: main.c
gcc -Waa -g -c main.c -o main.o
sub.o: sub.c sub.h
gcc -Waa -g -c sub.c -o sub.o
add.o: add.c add.h
gcc -Wall -g -c sub.c -o sub.o
clear:
rm -f *.o main
makefile 第二版本
.PHONY:clear
OBJS=main.o sub.o add.o
main: $(OBJS)
gcc -Wall -g $^ -o $@
sub.o: sub.c sub.h
gcc -Wall -g -c $< -o $@
add.o: add.c add.h
gcc -Wall -g -c $< -o $@
clear:
rm -f $(OBJS) main
$@ $^ $<为makefile自动化变量其含义如下
- $@: 规则的目标文件名 在上述makefile中分别表示 main sub.o add.o
- $^: 规则的目标文件名 在上述makefile中分别表示 main.o sub.o add.o
- $<: 规则的第一个依赖文件 在上述makefile中分别表示 sub.c add。c
OBJS为定义的变量,在makefile中变量引用要加入 $(变量名)
makefile 第三版本
.PHONY:all clear
SRC=$(wildcard *.c)
BIN=$(SRC:%.c=%)
OBJS=$(SRC:%.c=%.o)
CC=gcc
CFLAGS=-Wall -g
all:$(BIN)
$(BIN):%:%.o
$(CC) $(CFLAGS) $< -o $@
.c.o:
$(CC) $(CFLAG) -c $< -o $@
clear:
rm -f $(BIN) $(OBJ)
此makefile可以根据多个.c文件生成对应的可执行文件
解释:
- .c.o 相当于 %.c:%.o 为makefile匹配模式,代表生成对用的.o文件要依赖对应的.c文件。
编译器自动推导:同名的.c文件生成同名的目标文件
$(wildcard 模式) -列出当前目录下匹配模式的文件, $(wildcard *.c) 列出当前目录下所有的.c文件,
BIN: 代表要生成的目标,BIN=$(SRC:%.c=%) 及将所有.c文件生成对应的可执行文件
以下还有其他几个版本拿出来一起参考一下
.PHONY:all clear
SRC=$(wildcard *.c)
BIN=$(SRC:%.c=%)
OBJS=$(SRC:%.c=%.o)
CC=gcc
CFLAGS=-Wall -g
all:$(BIN)
%:%.o
$(CC) $(CFLAGS) $^ -o $@
.c.o:
$(CC) $(CFLAG) $< -o $@
clear:
rm -f $(BIN) $(OBJ)
.PHONY:all clear
SRC=$(wildcard *.c)
BIN=$(SRC:%.c=%)
OBJS=$(SRC:%.c=%.o)
CC=gcc
CFLAGS=-Wall -g
print:
@echo SRC=$(SRC)
@echo BIn=$(BIN)
@echo OBJS=$(OBJS)
all:$(BIN)
$(BIN):%:%.o
$(CC) $(CFLAGS) $< -o $@
#.c.o:
$(OBJ):%.o:%.c
$(CC) $(CFLAG) -c $< -o $@
clear:
rm -f $(BIN) $(OBJS)
.PHONY:clear all
CC=gcc
CFLAGS=-Wall -g
all: main hello hello1
OBJS=main.o sub.o add.o
main:$(OBJS)
$(CC) $(CFLAGS) $^ -o $@
hello:hello.o
$(CC) $(CFLAGS) $^ -o $@
hello1:hello1.o
$(CC) $(CFLAGS) $^ -o $@
#%.o:%.c
.c.o:
$(CC) $(CFLAGS) -c $< -o $@
clear:
rm -f main *.o
.PHONY:all clear
SRC=$(wildcard *.c)
BIN=$(SRC:%.c=%)
OBJS=$(SRC:%.c=%.o)
CC=gcc
CFLAGS=-Wall -g
print:
@echo SRC=$(SRC)
@echo BIn=$(BIN)
@echo OBJS=$(OBJS)
all:$(BIN)
$(BIN):%:%.o
$(CC) $(CFLAGS) $< -o $@
#.c.o:
$(OBJ):%.o:%.c
$(CC) $(CFLAG) -c $< -o $@
clear:
rm -f $(BIN) $(OBJS)
.PHONY:all clear
SRC=$(wildcard *.c)
BIN=$(SRC:%.c=%)
OBJS=$(SRC:%.c=%.o)
CC=gcc
CFLAGS=-Wall -g
print:
@echo SRC=$(SRC)
@echo BIn=$(BIN)
@echo OBJS=$(OBJS)
all:$(BIN)
$(BIN):%:%.o
$(CC) $(CFLAGS) $< -o $@
#.c.o:
$(OBJ):%.o:%.c
$(CC) $(CFLAG) -c $< -o $@
clear:
rm -f $(BIN) $(OBJS)
此makefile可用于多级目录编译,每个目录有自己的makefile,然后再最外层执行这个makefile
SUBDIRS=$(shell ls -d */)
#printf:
# @echo SUBDIRS=$(SUBDIRS)
.PHONY:default all clean $(SUBDIRS)
default:all
@echo -----------0
all clean:
$(MAKE) $(SUBDIRS) TARGET=$@
@echo ----------1
$(SUBDIRS):
$(MAKE) -C $@ $(TARGET) #make -C Demo1 all
@echo -----------2
makefile这个东西对于在Linux下编程,十分重要,多写一些就能掌握其中的规则,同时要懂一些shell编程,可更容易写出高质量的makefile