make命令和makefile文件

本文介绍make命令及其选项,makefile语法、宏定义及特殊字符,并通过实例演示如何运用makefile实现源代码编译、手册编写及安装过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

make命令和makefile文件的结合提供了一个在项目管理领域十分强大的工具.不仅被用于控制源代码的编译,而且还用于手册页的编写以及将应用程序安装到目标目录.

make命令

make命令的选项和参数

  • -k:让make命令发现错误后继续执行
  • -n:输出要执行的步骤而不真正执行这些操作
  • -f:指定makefile文件

makefile的语法

makefile由依赖关系和规则构成.

依赖关系

定义最终应用程序里的每个文件与源文件间的关系.
规则写法:先写目标的名称,然后紧跟一个冒号,接着是空格或tab,最后是空格或tab隔开的文件列表.
例:

app: main.o 2.o 3.o
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h

如果想一次创建多个文件,可以利用伪目标all.假设应用程序由二进制文件myall和使用手册myapp.1组成.可以用下面这行语句来进行定义:

all: myapp myapp.1

如果未指定一个all目标,则make命令将只创建它在makefile中找到的第一个目标.

规则

规则所在的行必须以tab开头,用空格是不行的.此外,如果makefile文件中的某行以空格结尾,也可能导致make命令执行失败.

app: main.o 2.o 3.o
    gcc -o app main.o 2.o 3.o

main.o: main.c a.h
    gcc -c main.c

2.o: 2.c a.h b.h
    gcc -c 2.c

3.o: 3.c b.h c.h
    gcc -c 3.c

makefile中的宏

可以通过语句MACRONAME=value在makefile文件中定义宏,引用宏的方法是使用$(MACRONAME)${MACRONAME}. make的有些版本还接受$MACRONAME的用法.如果想把一个宏的值设为空,可以用等号(=)后面留空.

例: 使用宏设置编译器和编译器选项

all: myapp

# Which compiler
CC = gcc

# Where are include files kept
INCLUDE = .

# Options for development
CFLAGS = -g -Wall -ansi

# Options for release
# CFLAGS = -O -Wall -ansi


myapp: main.o 2.o 3.o
    $(CC) -o myapp main.o 2.o 3.o

main.o: main.c a.h
    $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c

2.o: 2.c a.h b.h
    $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c

3.o: 3.c b.h c.h
    $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c

make内置一些特殊的宏定义,在使用前展开,含义会随makefile文件的处理进展而发生变化.

定义
$?当前目标所依赖的文件列表中比当前目标文件还新的文件
$@当前目标的名字
$<当前依赖文件的名字
$*不包括后缀名的当前依赖文件的名字

还有两个特殊字符,出现在命令之前:

  • - 告诉make忽略所有错误.如想创建一个目录,忽略目录已存在的错误,可以在mkdir命令前加一个减号
  • @ 告诉make在执行某条命令前不要将该命令显示在标准输出上.

多个目标

制作不止一个目标文件或者将多组命令集中到一个位置来执行是很有用的.
例:该例中新增了两个目标clean和install.make命令只能创建myapp, 需运行make clean和make install命令来创建目标clean和install.

all: myapp

# Which compiler
CC = gcc

# Where to install
INSTDIR = /usr/local/bin

# Where are include files kept
INCLUDE = .

# Options for development
CFLAGS = -g -Wall -ansi

# Options for release
# CFLAGS = -O -Wall -ansi


myapp: main.o 2.o 3.o
    $(CC) -o myapp main.o 2.o 3.o

main.o: main.c a.h
    $(CC) -I$(INCLUDE) $(CFLAGS) -c main.c

2.o: 2.c a.h b.h
    $(CC) -I$(INCLUDE) $(CFLAGS) -c 2.c

3.o: 3.c b.h c.h
    $(CC) -I$(INCLUDE) $(CFLAGS) -c 3.c


clean:
    -rm main.o 2.o 3.o

install: myapp
    @if [ -d $(INSTDIR) ]; \
    then \
        cp myapp $(INSTDIR);\
        chmod a+x $(INSTDIR)/myapp;\
        chmod og-w $(INSTDIR)/myapp;\
        echo "Installed in $(INSTDIR)";\
    else \
        echo "Sorry, $(INSTDIR) does not exist";\
    fi

管理函数库

例:本例通过穿件库函数mylib.a来创建目标myapp.例子中利用内置规则,将制作目标的规则去掉,只指定依赖关系,从而达到简化makefile文件的目的.

all: myapp

# Which compiler
CC = gcc

# Where to install
INSTDIR = /usr/local/bin

# Where are include files kept
INCLUDE = .

# Options for development
CFLAGS = -g -Wall -ansi

# Options for release
# CFLAGS = -O -Wall -ansi

# Local Libraries
MYLIB = mylib.a

myapp: main.o $(MYLIB)
    $(CC) -o myapp main.o $(MYLIB)


$(MYLIB): $(MYLIB)(2.o) $(MYLIB)(3.o)
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h

clean:
    -rm main.o 2.o 3.o $(MYLIB)

install: myapp
    @if [ -d $(INSTDIR) ]; \
    then \
        cp myapp $(INSTDIR);\
        chmod a+x $(INSTDIR)/myapp;\
        chmod og-w $(INSTDIR)/myapp;\
        echo "Installed in $(INSTDIR)";\
    else \
        echo "Sorry, $(INSTDIR) does not exist";\
    fi

makefile文件和子目录

处理含有子目录的项目有两种方法
第一种是在子目录中编写第二makefile文件,用于编译该子目录下的源文件,并将其保存到一个函数库中,然后将该库文件复制到上一级的主目录中.主目录中的makefile文件包含一条用于制作函数库的规则,该规则会调用第二个makefile文件,如下所示:

mylib.a:
    (cd mylibdirectory;$(MAKE))

由于make会针对每个命令调用一个新的shell,而第二个makefile文件的make命令本身又没有执行cd命令,但它又必须在一个不同的目录下创建函数库.为解决这一问题,用括号将两个命令括起来,从而确保它们只被一个单独的shell处理.

第二种方法是在原来的makefile文件中添加一些宏.新添加的宏通过在我们已见过的宏的尾部追加一个字母得到,字母D代表目录,字母F代表文件名.然后可以用下面的规则来替换内置的.c.o 后缀规则:

.c.o:
    $(CC) $(CFLAGS) -c $(@D)/$(<F) -o $(@D)/$(@F)

作用:编译子目录中的源文件并将目标文件放在该子目录中.然后用如下依赖关系和规则来更新当前目录下的函数库:

mylib.a: mydir/2.o mydir/3.o
    ar -rv mylib.a $?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值