第三方开发工具,iii电动工具

  第三方开发工具,iii电动工具

  内置规则

  到目前为止,我们已经详细说明了如何在makefile文件中执行流程的每一步。其实makefile有大量的内置规则,可以大大简化makefile文件,尤其是当我们有大量源文件的时候。让我们创建foo.c,这是一个传统的Hello World程序。

  #包含stdlib.h

  #包含stdio.h

  int main()

  {

  printf(" Hello World/n ");

  退出(EXIT _ SUCCESS);

  }

  我们尝试用make编译它,而不是指定makefile文件。

  $ make foo

  cc福. c -o福

  $

  我们可以看到,make知道如何调用编译器,虽然在这种情况下,他选择了cc而不是gcc(这在Linux下可以正常工作,因为通常cc是链接到gcc的)。有时候,这些内置规则就是推理规则。的默认规则使用宏,因此我们可以通过为这些宏指定新值来更改默认行为。

  $ rm foo

  $ make CC=gcc CFLAGS="-Wall -g" foo

  gcc -Wall -g foo

  $

  我们可以使用-p选项让make print输出它的内置规则。内置规则太多,无法在此一一列举,但这里是make的GNU版本make -p的简短输出,演示了其中的一些:

  OUTPUT_OPTION=-o $@

  compile . c=$(CC)$(CFLAGS)$(CPPFLAGS)$(TARGET _ ARCH)-c

  %.o: %。c

  要执行的命令数量(内置):

  $(COMPILE.c) $(OUTPUT_OPTION) $

  现在,我们可以通过指定构建目标文件的规则,使用这些内置规则来简化makefile,因此makefile的相关部分简化如下:

  主界面:主界面

  2 . o:2 . c a h b h

  3.o: 3.c b.h c.h

  后缀和模式规则

  我们看到的内置规则是和后缀(类似于Windows和MS-DOS中的文件扩展名)一起工作的,所以当指定一个文件的扩展名时,make知道应该使用哪个规则来创建不同扩展名的文件。这里最常见的规则是创建以结尾的文件。o来自以结尾的文件。这个规则是用编译器编译文件,但是不链接源文件。

  有时我们需要能够创造新的规则。以前有些源文件是用不同的编译器编译的:MS-DOS下两个,Linux下gcc。为了满足MS-DOS编译器的要求,C源文件,而不是C源文件,需要用。cpp后缀。不幸的是,Linux下使用的make版本现在没有内置的编译规则。cpp文件。(他确实有一个在Unix下更常见的. cc规则)

  因此,要么为每个单独的文件指定一个规则,要么我们需要教创建一个新的规则,用。cpp分机。如果我们在这个项目中有大量的源文件,那么指定一个新的规则可以节省大量的输入工作,并且可以更容易地在项目中添加一个新的源文件。

  要添加一个新的后缀规则,我们首先在makefile中添加一行来告诉make新的后缀;然后我们可以用这个新的后缀来写一个规则。使用以下语法样式定义一般规则,从带有旧后缀的文件创建带有新后缀的文件:旧_后缀。新后缀:

  下面是makefile中一个新的通用规则的代码片段,它用于转换。cpp文件到。o文件:后缀:卡片打印处理机(Card Print Processor的缩写)

  . cpp.o:

  $(CC)-xc $(CFLAGS)-I $(INCLUDE)-c $。

  对cpp.o的特殊依赖:告诉make下一个规则用于转换带有。带cpp后缀的文件。o后缀。当我们编写这个依赖项时,我们使用一个特殊的宏名,因为我们不知道我们要转换的实际文件名。为了理解这个规则,我们只需要回忆一下$将被扩展到起始文件名(带有旧的后缀)。注意,我们只是告诉make如何获取?o文件从。cpp文件;Make已经知道如何从目标文件中获取二进制可执行文件。

  当我们调用make时,他用我们的新规则从bar.cpp中获取bar.o,然后用它的内置规则从. o中获取一个可执行文件,-xc标志用来告诉gcc这是一个C源文件。

  最近,make知道了如何用。cpp扩展名,但是这种技术在将一种文件类型转换为另一种文件类型时非常有用。

  旧版的make包含了相应的语法来达到同样的效果,而且更好。例如,匹配规则使用通配符语法来匹配文件,而不是仅依赖文件扩展名。

  模式规则等同于。上述示例中的cpp规则如下:

  %.cpp: %o

  $(CC)-xc $(CFLAGS)-I $(INCLUDE)-c $。

  使用创建管理库

  当我们在处理一个大型项目时,使用库来管理多个编译的产品通常很方便。是一个库文件,通常扩展名为。一个,并包含一个对象文件集合。make命令有一个处理库的特殊语法,这使得它们更容易管理。

  此语法为lib (file.o),这意味着目标文件file.o存储在库lib中。Make有一个用于管理库的内置规则,通常如下:

  c.a:

  $(CC) -c $(CFLAGS) $

  $(AR) $(ARFLAGS) $@ $*。o

  宏$(AR)和$(ARFLAGS)通常分别默认为命令AR和选项rv。这个简短的语法告诉make获取。一个来自. c文件的库,他必须执行两条规则:

  第一条规则是他必须编译源文件并生成目标文件。

  第二个规则是使用ar命令修改库并添加新的目标文件。

  因此,如果我们有一个包含文件bas.o的库fud,那么在第一个规则中,$将被替换为bas.c。在第二个规则中,$ @被替换为库fud.a,而$ *被替换为bas。

  测试管理库

  事实上,使用管理库的规则非常简单。让我们修改我们的程序,使文件2.o和3.o保存在名为mylib.a的库中。我们的makefile文件需要一些小的修改,因此Makefile5如下所示:

  所有人:myapp

  #哪个编译器

  CC=gcc

  #安装位置

  INSTDIR=/usr/local/bin

  #包含文件保存在哪里

  包含=。

  #发展选项

  CFLAGS=-g -Wall -ansi

  #发布选项

  # CFLAGS=-O -Wall -ansi

  #本地图书馆

  MYLIB=mylib.a

  myapp: main.o $(MYLIB)

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

  $(MYLIB):$(MYLIB)(2 . o)$(MYLIB)(3 . o)

  主界面:主界面

  2 . o:2 . c a h b h

  3.o: 3.c b.h c.h

  清洁:

  -rm main.o 2.o 3.o $(百万英镑)

  安装:myapp

  @ if[-d $(INSTDIR)];/

  然后/

  CP myapp $(inst dir);/

  chmod a x $(inst dir)/myapp;/

  chmod og-w $(inst dir)/myapp;/

  echo“安装在$(INSTDIR)”中;/

  else /

  echo“对不起,$(INSTDIR)不存在”;/

  船方不负担装货费用

  在这里,我们需要注意我们如何使用默认规则来完成大部分工作。现在让我们测试makefile文件的新版本。

  多年期应用程序*。o mylib.a

  $ make -f生成文件5

  gcc -g -Wall -ansi -c -o main

  gcc -g -Wall -ansi -c -o 2.o 2.c

  ar rv mylib.a 2.o

  a - 2.o

  gcc -g -Wall -ansi -c -o 3.o 3.c

  ar rv mylib.a 3.o

  a - 3.o

  gcc -o myapp main.o mylib.a

  $ touch c.h

  $ make -f生成文件5

  gcc -g -Wall -ansi -c -o 3.o 3.c

  ar rv mylib.a 3.o

  r - 3.o

  gcc -o myapp main.o mylib.a

  $

  操作原理

  首先,我们删除所有的目标文件和库,并允许make构建myapp。他在使用库链接main.o之前通过编译和创建库来构建myapp,然后我们测试3.o的测试规则,他会通知make如果C.H .发生变化,那么3.C .必须重新编译。他会正确的完成这些任务,编译3.c并在重新链接之前更新库,这样就创建了一个新的可执行文件myapp。

  高级主题:Makefile和子目标

  如果我们写一个大项目,有时候把组成库的文件从主文件中分离出来,存放在一个子目录中是非常方便的。Make可以通过两种方式来完成这项任务。

  首先,我们可以在这个子目录中有第二个makefile文件来编译该文件,将其存储在一个库中,然后将该库复制到上一级主目录中。高级目录中的主makefile文件有一个构建这个库的规则。调用第二个makefile文件的语法如下:

  mylib.a:

  (cd mylibdirectory$(MAKE))

  也就是说我们要一直尝试构建mylib.a当make调用这个规则来构建库的时候,他会进入子目录mylibdirectory,然后调用一个新的make命令来管理库。因为这会调用一个新的shell,所以使用makefile的程序不会执行cd命令。但是,用于执行规则构建库的被调用shell位于不同的目录中。圆括号可以保证它们都在单独的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 $?

  我们需要决定在我们自己的项目中更喜欢哪种方法。很多项目只是简单的避免有子目录,但是这会导致源目录下有大量的文件。正如我们在前面的概述中看到的,我们在子目录中使用make只是增加了复杂性。

  GNU make和gcc

  如果我们使用GNU make和GNU gcc编译器,还有另外两个有趣的选项:

  第一个是make的-jN(jobs )选项。这将使用make同时执行n个命令。此时,make可以同时调用多个规则来独立编译项目的不同部分。根据我们的系统配置,重新编译时这是一个很大的改进。如果我们有多个源文件,尝试这个选项是有价值的。一般来说,一个小数字,比如-j3,是一个很好的起点。如果我们与其他用户共享我们的机器,请小心使用此选项。其他用户可能不喜欢我们每次编译时都启动大量的进程。

  另一个有用的选项是gcc的-MM选项。这将产生一个适合make的依赖项列表。在有大量源文件的项目中,每个文件都包含不同的头文件组合。正确获取依赖关系非常困难,但是非常重要。如果我们使用的每个源文件都依赖于每个头文件,有时我们会编译不必要的文件。另一方面,如果我们忽略一些依赖,问题会更严重,因为我们不会编译那些需要重新编译的文件。

  测试-gcc-mm

  下面我们使用gcc的-MM选项为我们的示例项目生成一个依赖列表:

  $ gcc -MM main.c 2.c 3.c

  主界面:主界面

  2 . o:2 . c a h b h

  3.o: 3.c b.h c.h

  $

  操作原理

  Gcc编译只是以适合插入makefile的形式输出所需的依赖行。我们需要做的就是将输出保存到一个临时文件中,然后将它插入makefile文件中,以获得一组完美的依赖规则。如果我们有gcc的输出副本,我们的依赖就没有理由出错。

  如果我们对makefile有信心,我们可以尝试使用makedepend工具,它执行与-MM选项类似的功能,但实际上会将依赖项添加到指定makefile的末尾。

  在我们离开makefile这个话题之前,可能值得指出的是,我们不能只局限于使用makefile来编译代码或创建库。我们可以用它们来自动化任何任务。例如,有一个序列命令允许我们从一些输入文件中获取一个输出文件。通常“非编译器”用户可能适合调用awk或sed来处理一些文件或生成手册页。我们可以自动处理任何文件,只要通过修改文件的日期和时间信息。

第三方开发工具,iii电动工具