【Linux开发—Makefile工具】

Linux开发—Makefile工具使用

  • 一,背景
  • 二,认识
    • 1,make与makefile
    • 2,关于编译
    • 3,编译案例:
  • 三,Makefile 宏
    • 1,特殊的宏(`$@`,`$?`, `$<`, `$*`)
    • 2,传统宏
  • 四,Makefile定义依赖性
  • 五,Makefile定义规则
  • 六,Makefile指令
  • 七,Makefile文件重新编译
  • 八,Makefile案例
  • Define required macros here

一,背景

  • 一个项目通常有多个源文件,如果只修改其中一个,就对所有源文件重新执行编译、链接步骤,就太浪费时间了。因此十分有必要引入 Makefile 工具:Makefile 工具可以根据文件依赖,自动找出那些需要重新编译和链接的源文件,并对它们执行相应的动作。

二,认识

1,make与makefile

  • Makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
  • Make工具最主要也是最基本的功能就是通过makefile文件来描述源程序之间的相互关系并自动维护编译工作
  • 而makefile 文件需要按照某种语法进行编写,文件中需要说明如何编译各个源文件并连接生成可执行文件,并要求定义源文件之间的依赖关系。
  • makefile 文件是许多编译器–包括 Windows下的编译器–维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改 makefile 文件而已。
  • 在 UNIX 系统中,习惯使用 Makefile 作为 makefile 文件。如果要使用其他文件作为 makefile,则可利用类似的 make 命令选项指定 makefile 文件。
  • 一个文件,指示程序如何编译和链接程序。makefile文件的默认名称是名副其实的Makefile,但可以指定一个命令行选项的名称。
  • make程序有助于您在开发大型程序跟踪整个程序,其中部分已经改变,只有那些编译自上次编译的程序,它已经改变了部分。

2,关于编译

编译一个小的C程序至少需要一个单一的文件.h文件(如适用)。虽然命令执行此任务只需CC file.c中,有3个步骤,以取得最终的可执行程序,如下所示:

  • 编译阶段:所有的C语言代码.c文件中被转换成一个低级语言汇编语言;决策.s文件。

  • 汇编阶段:前阶段所作的汇编语言代码,然后转换成目标代码的代码片段,该计算机直接理解。目标代码文件.o 结束。

  • 链接阶段:编译程序涉及到链接的对象代码的代码库,其中包含一定的“内置”的功能,如printf的最后阶段。这个阶段产生一个可执行程序,默认情况下,这是名为a.out。

3,编译案例:

假定有以下的源文件。
main.cpp
hello.cpp
factorial.cpp
functions.h

  • main.cpp 文件的内容
#include 

#include "functions.h"

int main(){
    print_hello();
    cout << endl;
    cout << "The factorial of 5 is " << factorial(5) << endl;
    return 0;
}
  • hello.cpp 文件的内容
#include 

#include "functions.h"

void print_hello(){
   cout << "Hello World!";
}
  • factorial.cpp 文件的内容
#include "functions.h"

int factorial(int n){
    if(n!=1){
	return(n * factorial(n-1));
    }
    else return 1;
}
  • functions.h 内容
void print_hello();
int factorial(int n);

琐碎的方法来编译的文件,并获得一个可执行文件,通过运行以下命令:

CC  main.cpp hello.cpp factorial.cpp -o hello
  • CC:代表编译器(gcc),编译C++文件,
  • 这上面的命令将生成二进制的Hello。在我们的例子中,我们只有四个文件,我们知道的函数调用序列,因此它可能是可行的,上面写的命令的手,准备最后的二进制。但对于大的项目,我们将有源代码文件成千上万的文件,就很难保持二进制版本。
  • make命令允许您管理大型程序或程序组。当开始编写较大的程序,你会发现,重新编译较大的程序,需要更长的时间比重新编译的短节目。此外会发现通常只能在一小部分的程序(如单一功能正在调试),其余的程序不变。

三,Makefile 宏

make程序允许您使用宏,这是类似的变量。 = 一对一个Makefile中定义的宏。例如:

MACROS=  -me
PSROFF=  groff -Tps
DITROFF= groff -Tdvi
CFLAGS= -O -systype bsd43
LIBS := "-lncurses -lm -lsdl"
LIBS += -lstdc++
MYFACE = ":*)"
PROJ_HOME = /home/moon/projects
$(MACROS)
$(MYFACE) :*)

1,特殊的宏($@$?, $<, $*

目标规则集发出任何命令之前,有一些特殊的预定义宏。
- $@ 表示目标文件。
- $? 表示比目标还要新的依赖文件列表

因此,举例来说,我们可以使用一个规则

hello: main.cpp hello.cpp factorial.cpp
	$(CC) $(CFLAGS) $? $(LDFLAGS) -o $@

alternatively:

hello: main.cpp hello.cpp factorial.cpp
        $(CC) $(CFLAGS) $@.cpp $(LDFLAGS) -o $@
  • 在这个例子中$@代表 hello, ? 或 ? 或 ?@.cpp将拾取所有更改的源文件。
    有两个比较特殊的隐含规则中使用的宏。它们是
    • $< 表示第一个依赖文件
    • $* 这个变量表示目标模式中“%”及其之前的部分
  • 对于$*如果目标是“dir/a.foo.b”,并且目标的模式是“a.%.b”,那么,“$*”的值就是“dir/a.foo”。这个变量对于构造有关联的文件名是比较有较。如果目标中没有模式的定义,那么“$*”也就不能被推导出,但是,如果目标文件的后缀是make所识别的,那么“$*”就是除了后缀的那一部分。例如:如果目标是“foo.c”,因为“.c”是make所能识别的后缀名,所以,“$*”的值就是“foo”。这个特性是GNU make的,很有可能不兼容于其它版本的make,所以,你应该尽量避免使用“$*”,除非是在隐含规则或是静态模式中。如果目标中的后缀是make所不能识别的,那么“$*”就是空值。。
    常见的隐含规则的构造 .o(对象)文件,.cpp(源文件)。
.o:.cpp
        $(CC) $(CFLAGS) -c $<

alternatively

.o:.cpp
        $(CC) $(CFLAGS) -c $*.cpp

2,传统宏

有很多默认的宏(输入“make -p”打印出来的默认值)。大多数使用它们的规则是很明显的:
这些预定义变量,即。在隐含规则中使用的宏分为两大类:那些程序名(例如CC)和那些含有参数的程序(如CFLAGS)。

下面是一些比较常见的变量用作内置规则:makefile文件的程序名称的表。

  • AR Archive-maintaining program; default `ar’.
  • AS Program for compiling assembly files; default `as’.
  • CC Program for compiling C programs; default `cc’.
  • CO Program for checking out files from RCS; default `co’.
  • CXX Program for compiling C++ programs; default `g++'.
  • CPP Program for running the C preprocessor, with results to standard output; default `$(CC) -E’.
  • FC Program for compiling or preprocessing Fortran and Ratfor programs; default `f77’.
  • GET Program for extracting a file from SCCS; default `get’.
  • LEX Program to use to turn Lex grammars into source code; default `lex’.
  • YACC Program to use to turn Yacc grammars into source code; default `yacc’.
  • LINT Program to use to run lint on source code; default `lint’.
  • M2C Program to use to compile Modula-2 source code; default `m2c’.
  • PC Program for compiling Pascal programs; default `pc’.
  • MAKEINFO Program to convert a Texinfo source file into an Info file; default `makeinfo’.
  • TEX Program to make TeX dvi files from TeX source; default `tex’.
  • TEXI2DVI Program to make TeX dvi files from Texinfo source; default `texi2dvi’.
  • WEAVE Program to translate Web into TeX; default `weave’.
  • CWEAVE Program to translate C Web into TeX; default `cweave’.
  • TANGLE Program to translate Web into Pascal; default `tangle’.
  • CTANGLE Program to translate C Web into C; default `ctangle’.
  • RM Command to remove a file; default `rm -f’.

这里是一个变量,其值是上述程序的额外的参数表。所有这些的默认值是空字符串,除非另有说明。

  • ARFLAGS Flags to give the archive-maintaining program; default `rv’.
  • ASFLAGS Extra flags to give to the assembler (when explicitly invoked on a .s' or .S’ file).
  • CFLAGS Extra flags to give to the C compiler.
  • CXXFLAGS Extra flags to give to the C compiler.
  • COFLAGS Extra flags to give to the RCS co program.
  • CPPFLAGS Extra flags to give to the C preprocessor and programs that use it (the C and Fortran compilers).
  • FFLAGS Extra flags to give to the Fortran compiler.
  • GFLAGS Extra flags to give to the SCCS get program.
  • LDFLAGS Extra flags to give to compilers when they are supposed to invoke the linker, `ld’.
  • LFLAGS Extra flags to give to Lex.
  • YFLAGS Extra flags to give to Yacc.
  • PFLAGS Extra flags to give to the Pascal compiler.
  • RFLAGS Extra flags to give to the Fortran compiler for Ratfor programs.
  • LINTFLAGS Extra flags to give to lint.

注:您可以取消-R--no-builtin-variables选项隐含规则使用的所有变量。
如,也可以在命令行中定义的宏

          make CPP=/home/moon/projects

四,Makefile定义依赖性

这是很常见的,最终的二进制文件将依赖于各种源代码和源代码的头文件。依存关系是重要的,因为他们告诉对任何目标的源。请看下面的例子
hello: main.o factorial.o hello.o
$(CC) main.o factorial.o hello.o -o hello
在这里,我们告诉hello 依赖main.o,factorial.o和hello.o,所以每当有任何变化,这些目标文件将采取行动。
同时我们会告诉如何准备 .o文件,所以我们必须定义这些依赖也如下
main.o: main.cpp functions.h
$(CC) -c main.cpp

factorial.o: factorial.cpp functions.h
$(CC) -c factorial.cpp

hello.o: hello.cpp functions.h
$(CC) -c hello.cpp

五,Makefile定义规则

一个Makefile目标规则的一般语法
target [target…] : [dependent …]
[ command …]
方括号中的项是可选的,省略号是指一个或多个。注意标签,每个命令前需要。
下面给出一个简单的例子,定义了一个规则使您的目标从 hello 其他三个文件。
hello: main.o factorial.o hello.o
$(CC) $? -o $@
注:在这个例子中,你必须放弃规则,使所有对象从源文件的文件
语义是相当简单的。当"make target"发现目标规则适用,如有眷属的新目标,使执行的命令一次一个(后宏替换)。如果有任何依赖进行,即先发生(让您拥有一个递归)。
如果有任何命令返回一个失败状态,MAKE将终止。这就是为什么看到规则,如:
clean:
-rm *.o *~ core paper

Make忽略一个破折号开头的命令行返回的状态。例如。如果没有核心文件,谁在乎呢?
Make 会 echo 宏字符串替换的命令后,告诉发生了什么事,因为它发生。有时可能想要把它们关掉。例如:
install:
@echo You must be root to install
大家所期望的Makefile的 某些目标。应该总是先浏览,但它的合理预期的目标(或只是做),安装,清理。
make all - 编译一切,让你可以在本地测试,之前安装的东西。
make install - 应安装在正确的地方的东西。但看出来的东西都安装在正确的地方为系统。
make clean - 应该清理的东西。摆脱的可执行文件,任何临时文件,目标文件等。

Makefile的隐含规则
该命令应该在所有情况下,我们建立一个可执行x的的源代码x.cpp的作为一个隐含的规则,这可以说:
.cpp:
$(CC) $(CFLAGS) [email protected] $(LDFLAGS) -o $@
这种隐含的规则说,如何make c, x.c 运行x.c 调用输出x。规则是隐式的,因为没有特定的目标提到。它可用于在所有的情况下。
另一种常见的隐含规则的构造 .o(对象)文件和 .cpp (源文件)。
.o:.cpp
$(CC) $(CFLAGS) -c $<

alternatively

.o:.cpp
$(CC) $(CFLAGS) -c $*.cpp
$^ 表明所有的依赖项
LDFLAGS = -lstdc++
hello:main.o factorial.o hello.o
$(CC) $^ $(LDFLAGS) -o $@
.o:.cpp functions.h
$(CC) -c $<
clean:
-rm *.o hello
install:hello
@echo “You need build hello!”
echo “no @ used”
all:hello install
Makefile 自定义后缀规则
就其本身而言,make已经知道,为了创建一个 .o文件,就必须使用 cc-c 相应的c文件。 建成MAKE这些规则,可以利用这一点来缩短Makefile。如果仅仅只是表示 .h 文件的 Makefile依赖线,依赖于目前的目标是,MAKE会知道,相应的文件已规定。你甚至不需要编译器包括命令。
这减少了我们的Makefile更多,如下所示:
OBJECTS = main.o hello.o factorial.o
hello: $(OBJECTS)
cc $(OBJECTS) -o hello
hello.o: functions.h
main.o: functions.h
factorial.o: functions.h

Make 使用一个特殊的目标,故名 .SUFFIXES允许你定义自己的后缀。例如,依赖线:
.SUFFIXES: .foo .bar
告诉make ,将使用这些特殊的后缀,以使自己的规则。

如何让 make 已经知道如何从 .c 文件生成 .o文件。类似的可以定义规则以下列方式:
.foo:.bar
tr ‘[A-Z][a-z]’ ‘[N-Z][A-M][n-z][a-m]’ < $< > $@
.c:.o
$(CC) $(CFLAGS) -c $<

第一条规则允许你创建一个 .bar 文件从 .foo文件。 (不要担心它做什么,它基本上打乱文件)第二条规则 .c文件创建一个 .o 文件中使用的默认规则。

  • 是一个通配符,用来表示任意的词,可以极大的简化makefile
    LDFLAGS = -lstdc++

all:hello install

hello:*.cpp *.h
$(CC) $^ $(LDFLAGS) -o $@

clean:
-rm *.o hello

install:hello
@echo “You need build hello!”
echo “no @ used”

六,Makefile指令

有好几种指令以不同的形式。让程序可能不支持所有指令。因此,请检查make是否支持指令,我们这里解释。 GNU make支持这些指令
条件指令
条件的指令
ifeq 指令开始的条件,指定的条件。它包含两个参数,用逗号分隔,并用括号括起。两个参数进行变量替换,然后对它们进行比较。该行的makefile继IFEQ的服从如果两个参数的匹配,否则会被忽略。
ifneq 指令开始的条件,指定的条件。它包含两个参数,用逗号分隔,并用括号括起。两个参数进行变量替换,然后对它们进行比较。makefile ifneq 遵守如果两个参数不匹配,否则会被忽略。
ifdef 指令开始的条件,指定的条件。它包含单参数。如果给定的参数为真,则条件为真。
ifndef 指令开始的条件,指定的条件。它包含单参数。如果给定的是假的,那么条件为真。
else 指令会导致以下行如果前面的条件未能被遵守。在上面的例子中,这意味着第二个选择连接命令时使用的第一种选择是不使用。它是可选的,在有条件有一个else。
endif 指令结束条件。每一个条件必须与endif结束。

条件式指令的语法
一个简单的条件,没有其他的语法如下:
conditional-directive
text-if-true
[else
text-if-false]
endif
文本如果真可以是任何行文字,被视为makefile文件的一部分,如果条件为真。如果条件是假的,没有文字来代替。
一个复杂的语法条件如下:
conditional-directive
text-if-true
else
text-if-false
endif
如果条件为真时,文本,如果真正的使用,否则,如果假文本来代替。的文本,如果错误的数量可以是任意的文本行。
有条件的指令的语法是相同的,无论是简单或复杂的条件。有四种不同的测试不同条件下的指令。这里是一个表:
ifeq (arg1, arg2)
ifeq ‘arg1’ ‘arg2’
ifeq “arg1” “arg2”
ifeq “arg1” ‘arg2’
ifeq ‘arg1’ “arg2”
上述条件相反的指令如下
ifneq (arg1, arg2)
ifneq ‘arg1’ ‘arg2’
ifneq “arg1” “arg2”
ifneq “arg1” ‘arg2’
ifneq ‘arg1’ “arg2”

条件式指令示例
libs_for_gcc = -lgnu
normal_libs =

foo: ( o b j e c t s ) i f e q ( (objects) ifeq ( (objects)ifeq((CC),gcc)
$(CC) -o foo $(objects) $(libs_for_gcc)
else
$(CC) -o foo $(objects) $(normal_libs)
endif

include 指令
include指令告诉make暂停读取当前makefile文件和读取一个或多个其它的makefile,然后再继续。该指令是一行在makefile中,看起来像这样:
include filenames…
文件名可以包含shell文件名模式。允许额外的空格开头的行被忽略,但不允许一个标签。例如,如果有三个.mk'文件, a.mk’, b.mk', and c.mk’, 和一个 $(bar) 扩展到bash中,然后下面的表达式。
include foo *.mk $(bar)

is equivalent to

include foo a.mk b.mk c.mk bash
当MAKE处理包括指令,它包含的makefile暂停读取,并从各列出文件中依次读取。当这个过程完成,使读取指令出现在其中的makefile的恢复。

override 指令
如果一个变量已经设置的命令参数,在makefile中被忽略的普通任务。如果要设置makefile的变量,即使它被设置的命令参数,可以使用一个override指令,这是一行看起来像这样:
override variable = value

or

override variable := value
=、+=、:=、?=
= 直接赋值
:= 忽略掉之前的赋值
+= 追加
?= 如果前面没有定义,则定义生效

七,Makefile文件重新编译

make 程序是一个智能的实用程序和工作根据在源文件中的变化。如果有四个文件main.cpp,hello.cpp,factorial.cpp和functions.h。这里所有reamining文件是依赖functions.h,main.cpp的是依赖于hello.cpp,factorical.cpp。因此,如果做任何改变functions.h然后将重新编译所有源文件来生成新的对象文件。但是,如果做任何改变main.cpp,因为这是不依赖任何其他的过滤,那么在这种情况下,只有main.cpp文件将被重新编译而hellp.cpp factorial.cpp将无法重新编译。
虽然编译一个文件时,MAKE检查目标文件和比较时间表,如果源文件有更新的时间戳比目标文件,然后将生成新的对象文件,假设源文件已被改变。

避免重新编译
有可能是项目包括成千上万的文件。有时候可能已经改变了一个源文件,但不想重新编译所有依赖于它的文件。例如,假设添加宏到一个头文件或声明,许多其他文件依赖。假设在头文件中的任何变化需要重新编译所有相关文件,但要知道,他们并不需要重新编译,你宁可不要浪费时间等待他们的编译。
如果预期改变头文件的问题之前,可以使用-t'标志位。这个标志告诉make命令不运行的规则,而是来标记目标,迄今为止,通过改变它的最后修改日期。遵循以下步骤: 1.使用命令'make'来重新编译真的需要重新编译源文件。 2.在头文件中进行更改。 3.使用命令-t’来记录所有的目标文件为最新。下一次运行make,在头文件中的变化不会引起任何重新编译。
如果已经改变了头文件的时候,有一些文件就需要重新编译,做到这一点已经太晚了。相反,可以使用-o文件“的标志,这标志着一个指定的文件作为”old“。这意味着该文件本身不会被重制并没有别的其交代将被重制。遵循以下步骤: 1.重新编译源文件,需要编制独立的特定头文件的原因,make -o headerfile’。如果涉及几个头文件,使用一个单独的-o'选项,每个头文件。 2.touch所有目标文件使用make -t’.
Makefile 其他功能
make 递归使用
递归使用的手段使用,make在makefile作为命令。这种技术是非常有用的,当你想要的makefile各种子系统组成一个更大的系统。例如,假设你有一个子目录,子目录都有其自己的makefile,并且您希望所在目录的makefile中运行make子目录。可以做到这一点如以下:
subsystem:
cd subdir && $(MAKE)

or, equivalently

subsystem:
$(MAKE) -C subdir

可以编写递归复制这个例子只是通过make命令,但有很多事情,了解他们是如何和为什么工作的,以及如何子涉及到顶层make。

通信变量到子make
顶层make变量的值可以被传递到子通过环境,通过显式请求。这些变数定义子作为默认值,但不会覆盖子的makefile使用makefile中所指定的,除非使用`-e’开关
向下传递,或导出,一个变量,变量和其值的环境中运行每个命令添加。子make反过来,make使用环境变量值来初始化它的表格
特殊变量SHELL和MAKEFLAGS总是导出(除非取消导出)。 MAKEFILES导出,如果把它设置到任何东西。
如果想导出特定变量的一个子制造,使用导出指令,像这样:
export variable …
如果想阻止一个变量被导出的,使用撤消导出的指令,像这样:
unexport variable …

MAKEFILES 变量
MAKEFILES如果环境变量的定义,make额外的makefile 名称列表(由空格分隔)之前被读取别人认为其值。这很像include指令:不同的目录中查找这些文件。
MAKEFILES的主要用途是MAKE递归调用之间的通信
但没有必要,最好不用定义和使用该变量

头文件包含在不同的目录
如果已经把你的头文件在不同的目录,在不同的目录中运行make,那么它需要告诉头文件的路径。这是可以做到的makefile中使用-I选项。假设该functions.h文件可在/home/yidaoyun/header头和其他文件/home/yidaoyun/src/然后进行文件将被写入如下。
INCLUDES = -I “/home/yidaoyun/header”
CC = gcc
LIBS = -lm
CFLAGS = -g -Wall
OBJ = main.o factorial.o hello.o

hello: ${OBJ}
${CC} ${CFLAGS} ${INCLUDES} -o $@ ${OBJS} ${LIBS}
.o:.cpp
${CC} ${CFLAGS} ${INCLUDES} -c $<

追加更多的文本变量
通常,它用于添加更多的文字,已定义的变量的值。make 这行包含’+ =‘,像这样:
objects += another.o
这需要值的变量对象,并添加文字another.o'(前面由一个单一的空间)。因此: objects = main.o hello.o factorial.o objects += another.o 设置文件main.o hello.o factorial.o another.o’的对象。
使用’+ ='是类似于:
objects = main.o hello.o factorial.o
objects := $(objects) another.o

Makefile中的续行
如果不喜欢太大的行,在Makefile中,然后你可以使用反斜杠\,如下所示
OBJ = main.o factorial.o
hello.o

is equivalent to

OBJ = main.o factorial.o hello.o

从命令提示符下运行的Makefile
如果已经准备好请示的Makefile的名称为“Makefile”文件,然后简单地写在命令提示符下,它将运行Makefile文件。但是,如果有任何其他的名字的Makefile,然后使用以下命令
make -f your-makefile-name

八,Makefile案例

这是一个例子编译hello程序Makefile。此程序包含三个文件main.cpp,factorial.cpp,hello.cpp。

Define required macros here

SHELL = /bin/sh

OBJS = main.o factorial.o hello.o
CFLAG = -Wall -g
CC = gcc
INCLUDES =
LIBS = -lstdc++

hello:${OBJ}
${CC} ${CFLAGS} -o $@ $^ ${LIBS}

clean:
-rm -f *.o hello

.o:.cpp
${CC} ${CFLAGS} ${INCLUDES} -c $<

现在可以建立hello 程序使用“make”hello项目。如果发出命令“make clean”,则它会删除所有的对象可在当前目录中的生成文件。

你可能感兴趣的:(C++之路,linux)