第一章: FreeBSD的make

字号:  小   中   大   | 打印 发布: 2008-2-19 09:21    作者: 剑心通明    来源: 互联网    查看: 415次

-

译者:雨丝风片

1.1 FreeBSDmake

作 为常用的和基本的Unix软件开发工具,make是一个可以跟踪全部的文件依赖关系的非常好的簿记工具程序。要管理依赖关系这样的项目细节常常需要花费很 多的时间,甚至会拖延开发进度。当多个开发人员合作一个项目的时候,依赖关系的跟踪就可能变得相当困难了。事实上,正确地使用make可以帮助我们加快应 用程序的开发,从而提高生产效率。

虽然make最初的设计是用来对应用程序版本构建的维护过程进行管理的,我们实际上还可以通过创建一系列的基于目标依赖关系的Unix shell 命令来让make完成多种多样的额外工作。这些依赖关系可以用很多种方式定义——包括需要进行编译的源文件、所需的库文件、shell命令以及其它的目标。

make 有多种风格的版本,其中包括GNU make和System V make。并不是在每个make版本中都有我们接下来讨论的那些特性,具体使用哪个版本完全取决于你的个人喜好。我们将主要关注跟随FreeBSD一起发 布的make(也叫做bmake或pmake),尤其是如何通过它来编译和更新FreeBSD系统,也就是所谓的make world。虽然我们关注的是FreeBSD make,但我们在这里讨论的所有东西对于各种BSD版本来说都是适用的。

我们首先会讲述一个Makefile的基本文件布局和语法。如果这对于你来说太简单了,那你可以直接跳到本章结束处的示例部分去阅读。(注意,我们给出的代码示例只用于演示我们关于make目标和依赖关系的讨论,它们并不一定是可以运行的代码。)

当 然,和其它工具程序一样,最开始应该先去看看man page,以对make提供的命令行选项的概要和细节有一个正式的了解。同时,和其它工具程序一样,学习make的最好方法就是使用它。创建一些小型的源 文件(可以使用任何语言),然后尝试一些下面给出的例子。我们希望读完本章之后你除了理解make的语法规则之外,还知道它是如何工作的。

1.2 Makefile布局

总 的说来,你使用make的方式就是让它去读一个Makefile,你需要在Makefile里指定一个目标及其依赖关系。在运行的时候,make会按顺序 搜索名字为Makefile或makefile的文件。这个Makefile通常是放在一个工程的根目录下的,如果想指定其它的Makefile,可以在 命令行上用-f (filename)的选项给出。

[Copy to clipboard] [- ]

CODE:

make -f OtherMakefile

1.3 语法

一个Makefile的结构由四个基本行组成,它们都可以通过在行尾添加‘/’字符来扩展到下一行(和shell编程相似)。注释是以‘#’号开始的,至行尾结束。

[Copy to clipboard] [- ]

CODE:

########################################
# Simple Makefile with comment example #
########################################

# when run, it will just echo hello
all:
   echo "hello"

要使用make来编译一个工程,首先需要确定在你的当前工作目录中已有一个正确的Makefile,然后再通过下列命令之一来使用make:

[Copy to clipboard] [- ]

CODE:

bash$ make

bash$ make all

bash$ make <target name>

1.4 目标

用来指定目标的方式有很多种,不过最常用的就是用目标文件或一个工程的名字。工程名字不应当包含有空格或标点符号,不过这只是个惯例而已;少量的空格和标点符号也是允许的。这个名字必须写在一个新行的开头,必须以单冒号(:)、双冒号(::)或感叹号(!)三者之一结束。

[Copy to clipboard] [- ]

CODE:

myprog:
     <some commands to the compile myprog target>

another::
     <some commands to the compile another target>

sample!
     <some commands to the compile sample target>

在这些目标名字之后是所需的依赖条件,包括名字、变量以及其它的目标等等。如果你的依赖条件太多的话,可以用一个‘/’和一个newline来将它们分开。所有的依赖条件都必须Makefile内定义或者存在于某个外部文件中,否则make将无法知道如何去完成依赖操作。

一些示例如下:

[Copy to clipboard] [- ]

CODE:

all: driver.cpp network_class.cpp file_io_class.cpp network_libs.cpp file_io_libs.cpp

all: myprog.o

myprog.o:

上例中,all和myprog.o是要make的目标。注意myprog.o既是一个目标又是一个依赖条件。make首先会到myprog.o那儿,执行它的命令,然后返回到all那儿,再执行它的命令。这种操作序列是make的功能基础。

按 照惯例,all:目标是你的目标中的最高者,这意味着make将从这儿开始去寻找要完成all:目标都需要哪些东西。不过all:目标并不是必需的,如果 没有的话,make就会简单地选择所有列出的目标中的第一个,只对其实施操作,除非你在命令行上指定了某个目标。对于那些有一个核心的应用程序需要维护和 构建的工程来说,我们建议你使用all:目标;这是一个通用的惯例,有助于避免错误和不必要的任务。

上例所示的依赖序列只是很简单的一个。下面是一个更为复杂和灵活的依赖序列,我们没有给出用于具体目标的命令:

[Copy to clipboard] [- ]

CODE:

all: myprog.o lib

lib: lex

lex:

myprog.o: app.h

注 意,在这个例子中,all:目标有两个依赖条件:myprog.o和lib。这两个依赖条件本身又都是目标,make将首先去编译myprog.o。在 make编译myprog.o的时候,会发现有一个和app.h的依赖关系。app.h并没有在这个makefile里定义,但app.h却是一个在当前 目录中的 头文件

用于myprog.o的命令完成之后,make即返回到all:处,继续处理下一个依赖条件,在此例中是lib。依赖条件lib本身也有一个依赖条件lex,所以make在完成lib之前会先去完成lex:。

注意:正如你所看到的,这些依赖关系可能会非常长,或者嵌套得很深。如果你有一个很大的Makefile,那一定要好好地组织一下,把目标的顺序弄好。

1.5 求值规则

依 赖关系是按照依赖于目标名字结束符号的严格规则来求值的。一旦make认为满足规则,它将通过执行相应的命令来创建特定的目标(比如编译该目标)。例如, 使用单冒号:可以让你对需要进行编译的目标进行更为精细的控制。也就是说,你可以指定某个特定的目标文件每次都需要重新编译或者仅当它的源文件过时之后才 编译。这些规则都是基于目标名字的结束符号的,如下:

如果目标名字以单冒号(:)结束,它将根据以下两个规则来创建:

  • 如果目标尚未存在,就像我们在上面举的例子里的all:一样,make就会创建它。
  • 如果任意一个源文件具有比当前目标更新的时间戳。在上例中如果app.h或myprog.c具有更新的时间戳,myprog.o就会被make。这种情况只需简单地用一下touch命令即可出现
[Copy to clipboard] [- ]

CODE:

touch myprog.c

如果目标名字以双冒号(::)结束,它将根据以下三个规则来创建:

  • 如果任意一个源文件具有比当前目标更新地时间戳。
  • 该目标不存在。
  • 该目标没有与之关联的源文件。

如果目标名字以感叹号(!)结束,只要make把它所需的全部依赖条件都创建完毕就会来创建它。

你只能在目标或源文件的最后一个组成部分中使用通配表达式?、*和[],而且只能用于描述已经存在的文件。比如:

[Copy to clipboard] [- ]

CODE:

myprog.[oc]

而使用花括号{}的表达式则不一定非得描述已经存在的文件。比如:

[Copy to clipboard] [- ]

CODE:

{mypgog,test}.o

# the expression above would match myprog.o test.o only

最后需要注意一点:可变表达式是按照目录顺序来处理的,而非字母顺序,就跟在shell表达式中一样。例如,如果你的目标有某些基于字母顺序的依赖条件,下面这个表达式可能就不对了:

[Copy to clipboard] [- ]

CODE:

{dprog,aprog,bprog,cprog}.cpp

1.6 变量

make能够使用变量这一点是非常重要的。例如,你有一个名字为a.c的源文件,由于某种原因你想把它的名字改成b.c。通常情况下,你得把你的makefile里的每个a.c的实例都改成b.c。但是,如果你写成以下方式:

[Copy to clipboard] [- ]

CODE:

MYSRC = a.c

你只需要把这一行更新成新的名字即可,如下:

[Copy to clipboard] [- ]

CODE:

MYSRC = b.c

你因此而节省了时间,这也正是make的首要角色:项目管理。

变量可用$(<变量名字>)或就用一个$来引用,但后者未被广泛使用,因此建议别那样写。

[Copy to clipboard] [- ]

CODE:

$( GCC ) = /usr/local/bin/gcc

make有四种不同类型的变量,下面将按照被搜索的顺序列出它们。(make的搜索将一直进行到发现某个数值的第一个实例为止。)

  • 局部变量:这些是赋给特定目标的数值。
  • 命令行:命令行变量是在命令行上传给make的。
  • 全局变量:全局变量是在该Makefile或任何所包含的Makefile内赋值的。你在一个Makefile内最常看到的就是这些变量。
  • 环境变量:环境变量是在Makefile之外,也就是运行make的shell里设置的。

这些变量可以在Makefile里用以下五种操作符进行定义:

  • 等号“=”是最常用的操作符,这和shell的情况是类似的。数值被直接赋给了变量。例如:

    [Copy to clipboard] [- ]

    CODE:

    VAR = < value >

  • 加号等号“+=”的意思是附加赋值,通过把所赋数值附加到当前数值的后面来完成对变量的赋值。例如:
    [Copy to clipboard] [- ]

    CODE:

    VAR += < value to append >

  • 问号等号“?=”的意思是条件赋值,仅当该变量从未赋值时才进行赋值。条件赋值在向一个字符串数值添加前缀的时候非常有用。例如:
    [Copy to clipboard] [- ]

    CODE:

    VAR ?= < value if unset >

  • 冒号等号“:=”的意思是扩展赋值,在赋值前会对所赋数值进行扩展;通常这种扩展是在所赋变量被引用的时候才进行的。例如:
    [Copy to clipboard] [- ]

    CODE:

    VAR := < value to expand >

  • 感叹号等号“!=”的意思是shell命令赋值。在命令被扩展并发给shell执行完毕之后,将命令结果赋给变量。结果中的newline字符将被空格取代。例如:
    [Copy to clipboard] [- ]

    CODE:

    VAR != < shell command to execute >

注意:有些变量是在外部的系统级的Makefile内定义的(在/etc/make.conf或/etc/defaults/make.conf中),如果你在设置环境变量方面遇到了问题,就去检查一下系统级文件里的设置。
      
一个完整的例子如下:

[Copy to clipboard] [- ]

CODE:

#####################
  # Example make file #
  #####################
  CURDIR != pwd
  CFLAGS ?= -g
  CFLAGS += -Wall -O2
  
  all:
      echo $CFLAGS
  #######################
  
  bash$ CFLAGS="-g -Wall" make

在上例中,CURDIR被设置成了shell命令pwd的结果。(注意,这种赋值并不需要backtick命令(即' ')。)CFLAGS如果从未设置,会首先被设置成-g,然后不管它的当前值是什么,都会被附加上-Wall -O2。

1.7 命令

如 果没有命令,那make什么都不是,只有把命令告诉make它才能完成它的工作。make只能运行这些命令,然后基于shell的退出状态判断这些命令是 否成功。因此,如果命令失败,shell返回一个错误,make将会因错误而退出并于该处终止。make可以就被看作是另外一个命令——除了运行其它命令 之外,它和那些命令并没有什么实际的交互。

命令必须与一个目标相关联,任何一个目标都可以有多个命令。例如:

[Copy to clipboard] [- ]

CODE:

# example for an all-install target

all-install:
   $(CC) $(CFLAGS)  $(MYSRC)
   cp  $(MYPROG) $(INSTALL_DIR)
   echo "Finished the build and install"

每个命令都必须在一个目标之后以新行开始,在实际命令起始位置之前必须要有一个tab键,如上所示。

对于大多数情况而言,Makefile里的命令只要是个有效的shell命令就行,命令还经常会包括变量。例如:

[Copy to clipboard] [- ]

CODE:

CPP = -g++
CFLAGS = -Wall -O2

myprog.o:
    $(CPP) $(CFLAGS) -c myprog.c

下面这个例子告诉make使用给定值编译myprog.c。这些命令可能会比一行要长,它们也可以被写来完成其它的任务。这是非常重要的,因为编译器可以接受相当多的命令行选项、环境设置以及定义等等,比如:

[Copy to clipboard] [- ]

CODE:

CLFAGS = $(LINK_FLAGS) $(LINK_LIBS) $(OTHER_LIBS) /
$(OPTIMIZER_FLAGS) $(DEFINES) $(NO_KERNEL) $(OBJS) /
$(CPU_TYPE_FLAGS) $(USE_MY_MALLOC) $(UDEFINES) /
$(SRC_FILE)  $(OTHER_SRC_FILES)

下面这个例子告诉make删除所有的object文件、core文件以及应用程序本身,然后把log文件移走,这些操作都可以很方便的完成。

[Copy to clipboard] [- ]

CODE:

CPP = -g++
CFLAGS = -Wall -O2
APP = myapp
DATE != date +%m%d%y_%H_%M
LOG = debug

myprog.o:
      $(CPP) $(CFLAGS) -c myprog.c
      rm -f  *.o  *.core $(APP)
      mv  $(LOG).log  $(LOG)_$(DATE).log

clean:
    rm -f *.o *.core $(APP)
    mv $(LOG).log $(LOG)_$(DATE).log

但 是,如果并不存在log文件,make就会因错误而退出。为了避免这种情况,可以在命令之前加上“-”号。通过在命令前面加上减号,你就告诉了make忽 略执行命令时遇到的错误。(不过make仍然会打印出错误信息。)因此,在某个命令出现错误之后make仍将继续。例如:

[Copy to clipboard] [- ]

CODE:

clean:
    -rm -f *.o *.core $(APP)
    -mv $(LOG).log $(LOG)_$(DATE).log

这将使得make忽略掉rm和mv命令可能遇到的错误。

你还可以让make禁止掉“echo”之类的命令的输出。echo首先告诉make打印出包括echo语句在内的整个命令,然后再执行命令并将字符串打印到屏幕上:

[Copy to clipboard] [- ]

CODE:

echo $(SOME_DEFINED_STRING)

要避免这种情况,可以在echo命令之前加上“@”符号,这将告诉make只打印字符串,比如:

[Copy to clipboard] [- ]

CODE:

@echo $(SOME_DEFINED_STRING)

“-”号和“@”号都既可用于变量,又可用于形为字符串的命令,但需要确定你对变量命令的引用是正确的。下例演示了如何对命令使用@操作符:

[Copy to clipboard] [- ]

CODE:

ECHO = echo
MESSAGE = "Print this message"

msg::
  @$(ECHO) $(MESSAGE)

1.8 条件语句(#if,#ifndef等等)

如 果你对C和C++比较熟悉,那你肯定知道条件预处理命令。功能繁多的make也有一个类似的特性。条件语句使你可以选择Makefile里的哪个部分需要 被处理。这些条件语句最多可以嵌套30层,并且可以放在Makefile里的任何地方。每条语句都必须以一个圆点(.)开始,而且条件语句块必须 以.endif结束。

条件语句允许使用逻辑操作符,比如逻辑AND“&&”、逻辑OR“||”,整条语句还可以用“!” 操作符取反。“!”操作符具有最高优先级,其后依次是逻辑AND和逻辑OR。括号可以被用来指定优先级顺序。关系运算符也是可以使用的,比如 “>”、“>=”、“<”、“<=”、“==”和“!=”。这些操作符可被用于十进制和十六进制的数值。对于字符串则可以使用 “==”和“!=”操作符。如果没有给出操作符,那么将会把数值和0进行比较。

在下面的例子中,在对VER变量进行赋值之后对条件进行测试。注意,如果VER变量未被赋值,则最后的.else条目将被求值为真,TAG将被赋成2.4_stable的值。

[Copy to clipboard] [- ]

CODE:

.if $(VER) >= 2.4
  TAG = 2.4_current
.elif $(VER) == 2.3
  TAG = 2.3_release
.else
  TAG = 2.4_stable
.endif

条件语句可用于测试变量,也可以用于下面这种函数风格的表达式。

这些用法中有些是有简写形式的。出于兼容性方面的考虑,我们列出了简写形式。非简写形式意思更为确定,也更容易被理解,但需要你敲入更多的字符。

在使用简写形式的时候是不必使用括号的。此外,简写形式还可以和if/else语句以及其它的简写形式混合在一起:

make( < arg > ) short hand [ .ifmake, .ifnmake, .elifmake, .elifnmake ]

在上例中,make将以一个目标名字作为它的参数。如果这个目标已在命令行上给出,或者它就是缺省进行make的目标,那么该值就为真。下例中将根据make()表达式的规则给CFLAGS赋值:

[Copy to clipboard] [- ]

CODE:

.if make(debug)
  CFLAGS += -g
.elif make(production)
  CFLAGS += -O2
.endif

下面是用简写形式表示的相同代码:

[Copy to clipboard] [- ]

CODE:

.ifmake debug
  CFLAGS += -g
.elifmake production
  CFLAGS += -O2
.endif

target( < arg > )

这种形式将以一个目标名字作为参数。仅当该target已被定义时该值才为真。这个表达式没有简写形式。例如:

[Copy to clipboard] [- ]

CODE:

.if target(debug)
  FILES += $(DEBUG_FILES)
.endif

在上例中,如果debug目标返回真的话就会对FILES变量进行附加操作。

empty ( < arg > )

这种形式以一个变量为参数,并允许使用修饰符。当变量被扩展之后是一个空字符串时该值为真。这个表达式没有简写形式。此外需要注意的是,你在使用这个表达式的时候并不需要对数值进行引用,记住是VAR而不是$(VAR)。例如:

[Copy to clipboard] [- ]

CODE:

.if empty (CFLAGS)
   CFLAGS = -Wall -g
.endif

defined( < arg > ) short hand [ .ifdef , .ifndef , .elifdef, elifndef ]

下面这个例子采用一个变量作为参数。仅当变量已被定义时该值为真。

[Copy to clipboard] [- ]

CODE:

.if defined(OS_VER)
  .if $(OS_VER) == 4.4
     DIRS += /usr/local/4.4-STABLE_src
  .endif
.else
  DIRS += /usr/src
.endif

下面是简写形式:

[Copy to clipboard] [- ]

CODE:

.ifdef OS_VER
. if $(OS_VER) == 4.4
  DIRS += /usr/local/4.4-STABLE_src
. endif
.else
  OS_VER = 3.2
  DIRS += /usr/src
.endif

正如你所看到的,make允许嵌套的条件和define表达式。和C不同的是,你不能对if语句和变量赋值语句进行缩进。如果想让你的条件语句块更清晰一点的话,你可以在圆点之后、if之前加一些空格。示例如下:

[Copy to clipboard] [- ]

CODE:

.if $(DEBUG) == 1
   $(CFLAGS) = -g
.     ifndef $(DEBUG_FLAGS)
         $(FLAGS) = $(DEBUG_FLAGS)
.     endif
.endif

exists( < arg > )

下面的例子演示了如何使用exists,以及如何给一个目标添加条件语句。如果存在tmp目录,make将运行-rm tmp/*.o命令。正如你在这个例子中看到的,.if语句仅在clean目标中被求值;所运行的命令必须遵从常规的命令语法。

[Copy to clipboard] [- ]

CODE:

clean:
    -rm -f *.o *.core
.if exists(tmp)
    -rm tmp/*.o
.endif

1.9 系统Makefiles,模板以及.include指令

C 的一个重要特性就是它的简单明了的预处理指令,也就我们常说的#include。这个特性也在make中实现了。所不同的是,如果你想包含另外一个 Makefile,那你得在文件末尾包含它,而不是像C那样在文件头包含,这是因为变量是按顺序进行赋值的。Makefile中又有Makefile可能 会造成混淆,不过包含一个Makefile的基本语法却是很简单的。注意,在include单词前面必须要加一个圆点(.)。基本语法如下:

[Copy to clipboard] [- ]

CODE:

.include file name

如果要指定一个位于系统make目录中的Makefile,可以使用尖括号:

[Copy to clipboard] [- ]

CODE:

.include <file name>

如果所指定的文件位于当前目录或者是由-I命令行选项指定的目录中,则使用双引号,这和C的#include是类似的:

[Copy to clipboard] [- ]

CODE:

.include "file name"

在下面的例子中,如果已在project.mk中定义了CFLAGS变量,它将被这里的新声明所取代。当包含多个Makefile时这就可能造成一些麻烦。

[Copy to clipboard] [- ]

CODE:

.include "../project.mk"

CFLAGS = -g

FreeBSD 系统有很多的系统Makefile可供包含,与之对应的是完成各种任务的例程。在大多数的FreeBSD系统中,这些Makefile位于/usr /share/mk/目录里。除此之外,还有一个/etc/defaults/make.conf,它是可以被/etc/make.conf取代的。(细 节请参见man make.conf。)

下面是一个常用的Makefile的简单列表。如果你正准备进行大量的内核编程或应用程序移植,那就好好利用Makefile吧。这些Makefile的形式为bsd.<type>.mk,其中<type>表示这些Makefile的用途。

  • bsd.dep.mk:这是一个用来处理Makefile依赖关系的非常有用的包含文件。
  • bsd.port.mk:这是一个在构建应用程序的ports 的时候使用的包含文件。

使 用.include指令的好处就是你可以把你的工程的Makefile分成很多的片断。比如,你的工程可以有一个主Makefile,由所有其它的子 Makefile进行包含。这个主Makefile可以包含编译器变量及其所需的选项。这样一来,就不必在每个Makefile里都指定编译器和所需选项 了,对编译器名字的引用也因此而得到了简化。这些公共使用的片断之后还可以用于其它的Makefile,对编译程序等例程的修改也将随之在所有的 Makefile中生效。

1.10 高级选项

高级的make选项主要为了增加灵活性。我们建议你仔细阅读一下make的man page,以便对make有一个深入的了解。下面这些选项是我们最常使用的。

局部变量

make可以使用专门定义的局部变量,其范围仅限于当前目标。下面列出了七个这样的局部变量,同时还给出了它们的System V兼容的老的表示形式。(不建议使用System V的老的表示形式,之所以列出它们,只是出于向后兼容的考虑。)

这个变量就是目标的名字:

[Copy to clipboard] [- ]

CODE:

.TARGET
old style notation: '@'

这个变量包含了当前目标的全部源文件的列表:

[Copy to clipboard] [- ]

CODE:

.ALLSRC
old style notation: '>'

这个变量是当前目标所隐含的源文件。它的数值是这个目标的源文件的名字和路径。(示例见下面的.SUFFIX部分。)

[Copy to clipboard] [- ]

CODE:

.IMPSRC
old style notation: '<'

这个变量保存的是已经被确定为过期的源文件的列表:

[Copy to clipboard] [- ]

CODE:

.OODATE
old style notation: '?'

这个变量的值是不包括后缀和路径的文件名:

[Copy to clipboard] [- ]

CODE:

.PREFIX
old style notation: '*'

这个变量的值是档案文件的名字:

[Copy to clipboard] [- ]

CODE:

.ARCHIVE
old style notation: '!'

这个变量的值是档案成员的名字:

[Copy to clipboard] [- ]

CODE:

.MEMBER
old style notation: '%'

当在依赖关系行中使用这些局部变量的时候,只有.TARGET、.PREFIX、.ARCHIVE和.MEMBER具有该目标的值。

另外还有一个非常不错的指令:

[Copy to clipboard] [- ]

CODE:

.undef <variable>

这会让你非常方便地取消对一个变量的定义。注意,只有全局变量才能取消定义。例如:

[Copy to clipboard] [- ]

CODE:

.ifdef DEBUG
.undef RELEASE
.endif

1.11 转换规则(后缀规则)

转换规则规定了如何去创建一个目标。你可以利用这些规则,省下为每个目标文件编写规则的时间。语法很简单:.SUFFIXES: (suffix list)

注意,可能有多个不同的后缀 共享 相 同的转换后缀(.cpp、.cc、.c都可以转换成a.o)。如果没有列出后缀,make将把之前的所有后缀都删除。这可能会在你有多 个.SUFFIXES规则的时候造成很大的混乱。我们建议你只使用一个规则,并把它放在Makefile的底部。把.SUFFIXES语句块中列出的东西 和target进行比较,你就容易理解它的结构了。

[Copy to clipboard] [- ]

CODE:

.SUFFIXES: .cpp .c .o .sh

.c.o:
     $(CC) $(CFLAGS) -c ${.IMPSRC}

.cpp.o:
     $(CPP) $(CXXFLAGS) -c ${.IMPSRC}

.sh:
     cp ${.IMPSRC} ${.TARGET}
     chmod +x ${.TARGET}

上面给出的这些规则将会编译C和C++源文件。不过对于.sh:规则而言,它也可以告诉make去创建相应的shell脚本。注意,如果想在这个例子中列出某个shell脚本作为目标,那就不能添加.sh扩展名,不过那个shell脚本本身却必须是带有.sh后缀的。

如 果我们把不带.sh后缀的install_script列出来作为一个依赖条件,那就必须存在一个带有.sh后缀的shell脚本,名字为 install_script.sh。也就是说,如果某个文件可以被列出来作为一个目标,那么在这个文件的存在期间,仅当make认为那个目标和该文件比 起来过了期之后才会去创建它,make并不会去创建这个文件。示例见下;更多的信息请参见apps.h的例子:

[Copy to clipboard] [- ]

CODE:

all: install_script $(OBJS)
     $(CPP) $(CFLAGS) -o $(APP) $(OBJS) $(LINK)

1.12 有用的命令行选项

下面给出了一些非常容易学习和使用的命令行选项。这并不是一个完整的列表,要想知道其它的选项还得去看man page。

-D <variable name to define>

这个选项将在命令行上定义一个变量,如果你的Makefile里面有.ifdef语句,那么使用这个选项就非常方便了。例如:

[Copy to clipboard] [- ]

CODE:

.ifdef DEBUG
  CFLAGS += -g -D__DEBUG__
.endif

于是,当你运行命令make -D DEBUG的时候,make就会正确的设置CFLAGS,在编译你的应用程序的时候也把调试语句编进去。

-E < variable name to override >

这个选项将用环境变量的值取代Makefile中给变量赋的值。在使用这个选项之前,需要先设置你的shell中的环境变量。例如:

[Copy to clipboard] [- ]

CODE:

bash $ CFLAGS="-O2 -Wall" make -E CFLAGS

-e

和大写形式类似,-e将用环境变量取代Makefile中的所有变量。如果某个变量没有定义相应的环境变量,则按正常方式赋值。

-f <makefile to use>

这个选项使你可以在命令行上指定Makefile,这一点在你需要多个Makefile时很有用。例如:

[Copy to clipboard] [- ]

CODE:

bash$  make -f Makefile.BSD

-j < number of max_jobs >

这个选项使你可以指定make能够派生出多少个job来。一般来说make只会派生出一个,但对于一个非常大的工程而言,如果想让你的硬件物尽其用的话,那就指定4个吧,如下:

[Copy to clipboard] [- ]

CODE:

make -j 4 world

如果超过了4个,有时反而会延长执行时间,不过有些人倒是可能觉得CPU被六个或更多的job折腾的样子很有趣。

-n

这个选项对于Makefile的调试很有用,它可以让你看到make究竟会执行哪些命令,但又不会实际去执行它们。对于有很多命令的大型工程,最好是把输出 重定向 到一个外部文件中,否则就会有浩如烟海的命令显示出来。示例如下:

[Copy to clipboard] [- ]

CODE:

bash $ make -n >> make_debug.log 2>&1

-V < variable name >

这个选项将基于全局的上下文打印变量的值。与前一个选项类似,make也不会去构建任何目标。你可以在命令行上指定多个-V选项,如下:

[Copy to clipboard] [- ]

CODE:

make -V CFLAGS -V LINK -V OBJS

1.13 一个最后的例子

下面列出的Makefile是一个可以重复使用的Makefile的例子。在你包含了它之后,它就知道从列出的.SUFFIXES规则中得知如何去编译C++源文件。它还知道如何去 安装 应 用程序和清除开发目录。这显然并不是一个很好理解的Makefile,但它却是一个很好的创建通用模板风格的Makefile的范例,这种 Makefile包含了那些用于开发的公共例程。这不只是节省了在创建每个Makefile时重复输入这些公共规则的时间,它还能让开发人员重复使用已知 的好例程。

[Copy to clipboard] [- ]

CODE:

########################################################
#
# FILE: Makefile
#
# AUTHOR: Nathan Boeger
#
# NOTES:
#  This is a generic Makefile for *BSD make, you will
#  need to customize the listed variables below inside
#  the Makefile for your application.
#
# INSTALL_DIR = name of the directory that you want to install
#   this applicaion (Ex: /usr/local/bin/ )
#
# APP          = name of the application
#
# C_SRC      = C source files (Ex: pstat.c )
#
# CPP_SRC  = CPP source files (Ex: node.cpp)
#
#
# $Id: ch01.html,v 1.5 2004/08/10 14:41:39 nathan Exp $
#########################################################

# Make the OBJ's from our defined C & C++ files
.ifdef CPP_SRC
OBJS            =       ${CPP_SRC:.cpp=.o}
.endif

.ifdef C_SRC
OBJS            +=      ${C_SRC:.c=.o}
.endif

# define the  Compiler. The compiler flags will be appended to
# if defined, else they are just assigned the values below
CPP             =        g++
CFLAGS          +=       -Wall -Wmissing-prototypes -O
LINK            +=       -lc

# Add a debug flag.
.ifdef DEBUG
  CFLAGS += -g
.endif

# Targets
all: ${OBJS}
    $(CPP) $(CFLAGS) -o $(APP) ${OBJS} $(LINK)

depend:
    $(CPP) -E -MM ${C_SRC} ${CPP_SRC}  > .depend

#######################################################
#
#        INSTALL SECTION
#
# install will copy the defined application (APP) into the
# directory INSTALL_DIR and chmod it 0755
# for more information on install read MAN(1) install
########################################################
install: all
    install -b -s $(APP) $(INSTALL_DIR)
  
clean
     rm -f $(APP) *.o *.core

# SUFFIX RULES
.SUFFIXES: .cpp .c .o

.c.o:
       $(CPP) $(CFLAGS) -c ${.IMPSRC}
.cpp.o:
       $(CPP) $(CFLAGS) -c ${.IMPSRC}

下面给出的Makefile是需要你在工程目录内部创建的:

[Copy to clipboard] [- ]

CODE:

#######################################################
#       PROJECT Makefile
#
# This is what the programs makefile would look like
# These are the only variables you will need to define
######################################################

APP            = myapp
C_SRC          = debug_logger.c
CPP_SRC        = myapp.cpp  base_classes.cpp
INSTALL_DIR    = /usr/local/bin/

# And include the template Makefile, make sure its
# path is correct.

.include "../../bsd_make.mk"

引自: http://www.bsdlover.cn/html/01/n-201.html

FreeBSD的make相关推荐

  1. FreeBSD Ports加速的方法

    使用代理. 在/etc/make.conf中设置: FETCH_ENV= "HTTP_PROXY=IP[:端口]" 如果需要,在FETCH_ENV值后面加入空格, HTTP_PRO ...

  2. FreeBSD设备驱动管理介绍(BSP: Ti AM335x)

    这段时间一直在忙FreeBSD驱动移植的项目,因此对FreeBSD做了一定的了解,鉴于网上对于FreeBSD的设备驱动资料较少,在这里给出本人对于FreeBSD驱动管理的理解心得(主要是USB驱动管理 ...

  3. FreeBSD 6.0架设管理与应用-第三章 UNIX 系统入门

    在开始进阶的 FreeBSD 设定之前,我们先来了解一下 UNIX 系统的架构及基本知识.这些知识对于我们之后管理.使用 FreeBSD 十分重要.如果您是 UNIX 新手,请务必详读本章. 本章将介 ...

  4. 邮件服务器 之 基于FreeBSD和Postfix的邮件系统与邮件列表的web mail安装

    作者: 杨廷勇(scyzxp at toping.net) 来自: LinuxSir.Org 版权:杨廷勇 Copyright © 2004.2005.2006 摘要: 本文介绍使用FreeBSD + ...

  5. 如何在FreeBSD中安装Nginx,MySQL,PHP(FEMP)

    本文介绍如何在FreeBSD 13系统中安装Nginx.MySQL.和PHP服务. 系统环境 FreeBSD 13.0-RELEASE 更新系统 在安装任何软件之前更新系统是一个好习惯,以便检查系统更 ...

  6. 话说Ubuntu和FreeBSD将要合成一个新的版本:UbuntuBSD

    2019独角兽企业重金招聘Python工程师标准>>> UbuntuBSD称自己是'Unix for human beings',从名称上就可以看出,这是一个什么样的结合体.Ubun ...

  7. FreeBSD 8.0 终于发布正式版了

    马上上两个链接先 http://wiki.freebsdchina.org/news/2009/freebsd_8.0_new_features    FreeBSD 8.0 新特性概览 http:/ ...

  8. 在FreeBSD下安装mysql+apache+php

    转自:[url]http://bbs.chinaunix.net/viewthread.php?tid=389832[/url] 在FreeBSD下安装mysql+apache+php ======= ...

  9. freebsd php mysql_FreeBSD下Mysql5+Apache2+PHP5的安装

    ■ MySQL5 的安装 源代码:mysql-5.0.18.tar.gz 下载地址:http://dev.mysql.com/get/Downloads/MySQL-5.0/mysql-5.0.18. ...

  10. Panabit 安装笔记之FreeBsd 6.2的安装

    由于公司内部需要安装一个流量监控工具,在网上遛了N天,发现传统的网络管理工具大部分是基于C/S架构的,要想获得客户端的流量或控制客户端的一些P2P的工具的使用或及时通讯工具的使用,就必须安装客户端,感 ...

最新文章

  1. Mac下Android studio 之NDK配置教程(一)
  2. 联想x250为什么这么贵_为什么ThinkPad这么贵?
  3. 诺顿360“偷偷”挖矿被怒喷,杀毒软件手伸向GPU,官方:都是为了用户好
  4. 深度神经网络(DNN)模型与前向传播算法
  5. Oracle 备份还原数据库练习.
  6. 爬数据html解析,jsoup网络爬取数据HTML解析
  7. Linux学习之系统编程篇:孤儿进程和僵尸进程(描述对象都是子进程)
  8. DCMTK:OFtuple的单元测试
  9. 好文章系列(都是网上非常好的文章)
  10. 童话世界区块宠物源码
  11. 网站泛解析 和 主记录解析
  12. Linux Kernel代码艺术——数组初始化
  13. x11/xwindow GUI窗口代码范例
  14. java debug怎么用_debug怎么用
  15. mysql数据库test密码_TestCenter常见问题
  16. 巨人就在你的身边——14期英语反馈表彰大会有感
  17. 为什么要用Handler
  18. 2019.03.26 bzoj4444: [Scoi2015]国旗计划(线段树+倍增)
  19. WPF基础到企业应用系列8——依赖属性之“风云再起”
  20. Surface3 2+64 wifi版本重装8.1系统(疑难解答中无从‘驱动器恢复‘选项,另一种方式成功)

热门文章

  1. 996程序员入职一年多,同事涨了4千他没涨,跟领导提涨薪,回复愣了
  2. 利用PHP获取学生成绩
  3. 20220626——每日推送信息API总结
  4. 使用线性连接图、蛛网图或分枝与混沌图对参数 a 进行讨论与观察【数学实验】【matlab】
  5. ElementUI中某一列插入组件(slot-scope=“scope“用法)
  6. 图灵数学·统计学系列
  7. SVG图案和渐变填充
  8. PCIe 资料收集2
  9. 在用Delphi 10.2.3 提示connection closed gracefully的处理
  10. Three.js-材质纹理详解