热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发工具(三)

内建规则到目前为止,我们已经在makefile文件中确切的指定了如何执行过程的每一步。事实上,makefile有大量的内建规则从而可以很大程度的简化ma

内建规则

到目前为止,我们已经在makefile文件中确切的指定了如何执行过程的每一步。事实上,makefile有大量的内建规则从而可以很大程度的简化makefile文件,特别是当我们有大量源文件的时候。下面我们创建foo.c,这是一个传统的Hello World程序。

#include
#include
int main()
{
    printf(“Hello World/n”);
    exit(EXIT_SUCCESS);
}

不指定makefile文件,我们尝试使用make来编译。

$ make foo
cc     foo.c -o foo
$

正如我们所看到的,make知道如何调用编译器,尽管在这种情况下,他选择cc而不是gcc(在Linux下这可以正常工作,因为通常cc链接到gcc)。有时,这些内建规则是推断规则(inference rules)。默认的规则使用宏,所以通过为这些宏指定一个新值,我们可以改变默认的行为。

$ rm foo
$ make CC=gcc CFLAGS=”-Wall -g” foo
gcc -Wall -g    foo.c   -o foo
$

我们可以使用-p选项使得make打印出其内建规则。内建规则太多而不能在这里全部列出,但是下面是GNU版本的make的make -p的简短输出,演示了其中的部分规则:

OUTPUT_OPTION = -o $@
COMPILE.c = $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c
%.o: %.c
# commands to execute (built-in):
        $(COMPILE.c) $(OUTPUT_OPTION) $<

我们现在可以通过指定构建目标文件的规则使用这些内建规则来简化我们的makefile文件&#xff0c;所以makefile文件的相关部分简化为&#xff1a;

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

后缀与模式规则

我们所看到的内建规则使用后缀进行工作(与Windows和MS-DOS的文件名扩展相类似)&#xff0c;所以当指定一个带有扩展名的文件时&#xff0c;make知道应使用哪条规则来创建带有不同扩展名的文件。在这里最通常的规则就是由以.c为结尾的文件创建以.o为结尾的文件。这个规则就是使用编译器编译文件&#xff0c;但是并不链接源文件。

有时我们需要能够创建新规则。程序开发作者过去在一些源文件上需要使用不同的编译器进行编译&#xff1a;两个在MS-DOS下&#xff0c;以及Linux下的gcc。要满足MS-DOS编译器的要求&#xff0c;C&#43;&#43;源文件而不是C源文件&#xff0c;需要以.cpp为后缀进行命名。不幸的是&#xff0c;现在Linux下使用的make版本并没有编译.cpp文件的内建规则。(他确实具有一个在Unix下更为常见的.cc的规则)

所以或者是为每一个单独的文件指定一个规则&#xff0c;或者是我们需要教给make一个新的规则来由以.cpp为扩展名的文件创建目标文件。假如我们在这个工程中有大量的源文件&#xff0c;指定一个新规则节省了大量的输入工作&#xff0c;并且使得在工程中添加一个新源文件更为容易。

要添加一个新的后缀规则&#xff0c;我们首先在makefile文件中添加一行&#xff0c;告诉make新的后缀&#xff1b;然后我们就可以使用这个新的后缀来编写一条规则。make使用下面的语法样式来定义一条通用的规则来由具有旧后缀的文件创建具有新后缀的文件&#xff1a;

..:

下面是我们的makefile文件中一条新的通用规则的代码片段&#xff0c;用于将.cpp文件转换为.o文件&#xff1a;

.SUFFIXES:      .cpp
.cpp.o:
   $(CC) -xc&#43;&#43; $(CFLAGS) -I$(INCLUDE) -c $<

特殊依赖.cpp.o:告诉make接下来的规则用于将以.cpp为后缀的文件转换为以.o为后缀的文件。当我们编写这个依赖时&#xff0c;我们使用特殊的宏名&#xff0c;因为我们并不知道我们将要转换的实际文件名。要理解这条规则&#xff0c;我们只需要简单的回忆起$<会扩展为起始文件名(带有旧后缀)即可。注意&#xff0c;我们只是告诉make如何由.cpp文件得到.o文件&#xff1b;make已经知道如何由一个目标文件获得二进制可执行文件。

当我们调用make时&#xff0c;他使用我们的新规则由bar.cpp获得bar.o&#xff0c;然后使用其内建规则由.o获得一个可执行文件。-xc&#43;&#43;标记用于告诉gcc这是一个C&#43;&#43;源文件。

在近些时候&#xff0c;make知道如何处理带有.cpp扩展名的C&#43;&#43;源文件&#xff0c;但是当将一种文件类型转换为另一种文件类型时&#xff0c;这个技术是十分有用的。

更为旧的make版本包含一个对应的语法用来达到同样的效果&#xff0c;而且更好。例如&#xff0c;匹配规则使用通配符语法来匹配文件&#xff0c;而不是仅依赖于文件扩展名。

对于上面例子中与.cpp规则等同的模式规则如下&#xff1a;

%.cpp: %o
   $(CC) -xc&#43;&#43; $(CFLAGS) -I$(INCLUDE) -c $<

使用make管理库

当我们正处理一个大型工程时&#xff0c;使用库来管理多个编译产品通常是比较方便的。库是文件&#xff0c;通常以.a为扩展名&#xff0c;包含一个目标文件的集合。make命令有一个处理库的特殊语法&#xff0c;从而使得他们更易于管理。

这个语法就是lib (file.o)&#xff0c;这就意味着目标文件file.o存储在库lib.a中。make具有一个内建的规则用于管理库&#xff0c;通常如下面的样子&#xff1a;

.c.a:
   $(CC) -c $(CFLAGS) $<
   $(AR) $(ARFLAGS) $&#64; $*.o

宏$(AR)与$(ARFLAGS)通常分别默认为命令ar与选项rv。这个简短的语法告诉make由一个.c文件得到.a库&#xff0c;他必须执行两条规则&#xff1a;

第一条规则是他必须编译源文件并且生成一个目标文件
第二条规则是使用ar命令来修改库&#xff0c;添加新的目标文件

所以&#xff0c;如果我们有一个库fud&#xff0c;包含文件bas.o&#xff0c;在第一条规则中$<被替换为bas.c。在第二条规则中&#xff0c;$&#64;被替换为库fud.a&#xff0c;而$*被替换为bas。

试验&#xff0d;&#xff0d;管理库

实际上&#xff0c;管理库的规则的使用是相当简单的。下面我们修改我们的程序&#xff0c;从而文件2.o与3.o保存在一个名为mylib.a的库中。我们的makefile文件需要一些小的修改&#xff0c;所以Makefile5如下所示&#xff1a;

all: myapp
# Which compiler
CC &#61; gcc
# Where to install
INSTDIR &#61; /usr/local/bin
# Where are include files kept
INCLUDE &#61; .
# Options for development
CFLAGS &#61; -g -Wall -ansi
# Options for release
# CFLAGS &#61; -O -Wall -ansi
# Local Libraries
MYLIB &#61; 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
   &#64;if [ -d $(INSTDIR) ]; /
    then /
      cp myapp $(INSTDIR);/
      chmod a&#43;x $(INSTDIR)/myapp;/
      chmod og-w $(INSTDIR)/myapp;/
      echo “Installed in $(INSTDIR)”;/
   else /
      echo “Sorry, $(INSTDIR) does not exist”;/
   fi

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

$ rm -f myapp *.o mylib.a
$ make -f Makefile5
gcc -g -Wall -ansi   -c -o main.o main.c
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 Makefile5
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
$

工作原理

我们首先删除所有的目标文件以及库&#xff0c;并且允许make构建myapp&#xff0c;他通过编译并且在使用库链接main.o之前创建库&#xff0c;从而创建myapp。然后我们测试3.o的测试规则&#xff0c;他会通知make&#xff0c;如果c.h发生变动&#xff0c;那么3.c必须进行重新编译。他会正确的完成这些工作&#xff0c;在重新链接之前会编译3.c并且更新库&#xff0c;从而创建一个新的可执行文件myapp。

高级主题&#xff1a;Makefile与子目标

如果我们编写一个大工程&#xff0c;有时将组成库的文件由主文件分离并且存储在一个子目录中是十分方便的。使用make可以两种方法来完成这个任务。

首先&#xff0c;我们在此子目录可以有第二个makefile文件来编译文件&#xff0c;将其存储在一个库中&#xff0c;然后将库拷贝到上一层主目录。在高层目录中的主makefile文件然后有一条规则用于构建这个库&#xff0c;其调用第二个makefile文件的语法如下&#xff1a;

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

这就是说我们必须总是尝试构建mylib.a。当make调用这条规则用于构建库时&#xff0c;他会进入子目录mylibdirectory&#xff0c;然后调用一个新的make命令来管理库。因为这会调用一个新的shell&#xff0c;使用makefile的程序并不会执行cd命令。然而&#xff0c;所调用的用于执行规则构建库的shell是在一个不同的目录中。括号可以保证他们都会在一个单独的shell中进行处理。

第二个方法是在一个单独的makefile文件中使用一些额外的宏。这些额外的宏是通过在我们已经讨论过的这些宏的基础上添加D&#xff08;对目录而言&#xff09;或是F&#xff08;就文件而言&#xff09;来生成的。然后我们可以用下面的规则来覆盖内建的.c.o前缀规则&#xff1a;

.c.o:
     $(CC) $(CFLAGS) -c $(&#64;D)/$(
来在子目录中编译文件并且将目标文件留下子目录中。然后我们可以用如下的依赖与规则来更新当前目录中的库&#xff1a;

mylib.a:   mydir/2.o mydir/3.o
     ar -rv mylib.a $?

我们需要决定在我们自己的工程中我们更喜欢哪种方法。许多工程只是简单的避免具有子目录&#xff0c;但是这样会导致在源码目录中有大量的文件。正如我们在前面的概览中所看到的&#xff0c;我们在子目录中使用make只是简单的增加了复杂性。

GNU make与gcc

如果我们正使用GNU make与GNU gcc编译器&#xff0c;还有两个有趣的选项&#xff1a;

第一个就是make的-jN("jobs")选项。这会使用make同时执行N条命令。此时make可以同时调用多条规则&#xff0c;独立的编译工程的不同部分。依据于我们的系统配置&#xff0c;这对于我们重新编译的时候是一个巨大的改进。如果我们有多个源文件&#xff0c;尝试这个选项是很有价值的。通常而言&#xff0c;小的数字&#xff0c;例如-j3&#xff0c;是一个好的起点。如果我们与其他用户共享我们的机器&#xff0c;那么要小心使用这个选项。其他用户也许不会喜欢每次编译时我们启动大量的进程数。

另一个有用的选项就是gcc的-MM选项。这会产生一个适合于make的依赖列表。在一个具有大量源码文件的工程中&#xff0c;每一个文件都会包含不同的头文件组合&#xff0c;要正确的获得依赖关系是非常困难的&#xff0c;但是却是十分重要的。如果我们使用每一个源文件依赖于每一个头文件&#xff0c;有时我们就会编译不必须的文件。另一方面&#xff0c;如果我们忽略一些依赖&#xff0c;问题就会更为严重&#xff0c;因为我们会没有编译那些需要重新编译的文件。

试验&#xff0d;&#xff0d;gcc -MM

下面我们使用gcc的-MM选项来为我们的例子工程生成一个依赖列表&#xff1a;

$ gcc -MM main.c 2.c 3.c
main.o: main.c a.h
2.o: 2.c a.h b.h
3.o: 3.c b.h c.h
$

工作原理

gcc编译只是简单的以适于插入一个makefile文件中的形式输出所需要依赖行。我们所需要做的就是将输出保存到一个临时文件中&#xff0c;然后将其插入makefile文件中&#xff0c;从而得到一个完美的依赖规则集。如果我们有一个gcc的输出拷贝&#xff0c;我们的依赖就没有出错的理由。

如果我们对于makefile文件十分自信&#xff0c;我们可以尝试使用makedepend工具&#xff0c;这些执行与-MM选项类似的功能&#xff0c;但是会将依赖实际添加到指定的makefile文件的尾部。

在我们离开makefile话题之前&#xff0c;也许很值得指出我们并不是只能限制自己使用makefile来编译代码或是创建库。我们可以使用他们来自动化任何任务&#xff0c;例如&#xff0c;有一个序列命令可以使得我们由一些输入文件得到一个输出文件。通常"非编译器"用户也许适用于调用awk或是sed来处理一些文件&#xff0c;或是生成手册页。我们可以自动化任何文件处理&#xff0c;只要是make由文件的日期与时间信息的修改可以处理的。


推荐阅读
  • 利用PaddleSharp模块在C#中实现图像文字识别功能测试
    PaddleSharp 是 PaddleInferenceCAPI 的 C# 封装库,适用于 Windows (x64)、NVIDIA GPU 和 Linux (Ubuntu 20.04) 等平台。本文详细介绍了如何使用 PaddleSharp 在 C# 环境中实现图像文字识别功能,并进行了全面的功能测试,验证了其在多种硬件配置下的稳定性和准确性。 ... [详细]
  • 本文详细介绍了如何在Linux系统中搭建51单片机的开发与编程环境,重点讲解了使用Makefile进行项目管理的方法。首先,文章指导读者安装SDCC(Small Device C Compiler),这是一个专为小型设备设计的C语言编译器,适合用于51单片机的开发。随后,通过具体的实例演示了如何配置Makefile文件,以实现代码的自动化编译与链接过程,从而提高开发效率。此外,还提供了常见问题的解决方案及优化建议,帮助开发者快速上手并解决实际开发中可能遇到的技术难题。 ... [详细]
  • 求助高手:下载的压缩包中包含CMake文件,如何在Windows环境下使用已安装的CMake GUI进行运行?
    从GitHub仓库 `https://github.com/vonmax007/RobotSimulation` 下载的代码包含多种算法,其中算法1的文件目录中包含了CMake文件。为了在Windows环境下使用已安装的CMake GUI运行这些文件,需要先确保CMake已正确安装,并按照以下步骤操作:打开CMake GUI,设置源代码路径和构建路径,点击“Configure”配置项目,然后点击“Generate”生成构建文件。最后,在生成的构建目录中使用命令行或IDE进行编译和运行。 ... [详细]
  • Python与R语言在功能和应用场景上各有优势。尽管R语言在统计分析和数据可视化方面具有更强的专业性,但Python作为一种通用编程语言,适用于更广泛的领域,包括Web开发、自动化脚本和机器学习等。对于初学者而言,Python的学习曲线更为平缓,上手更加容易。此外,Python拥有庞大的社区支持和丰富的第三方库,使其在实际应用中更具灵活性和扩展性。 ... [详细]
  • 基于Node.js的高性能实时消息推送系统通过集成Socket.IO和Express框架,实现了高效的高并发消息转发功能。该系统能够支持大量用户同时在线,并确保消息的实时性和可靠性,适用于需要即时通信的应用场景。 ... [详细]
  • 本文首先对信息漏洞的基础知识进行了概述,重点介绍了几种常见的信息泄露途径。具体包括目录遍历、PHPINFO信息泄露以及备份文件的不当下载。其中,备份文件下载涉及网站源代码、`.bak`文件、Vim缓存文件和`DS_Store`文件等。目录遍历漏洞的详细分析为后续深入研究奠定了基础。 ... [详细]
  • 从无到有,构建个人专属的操作系统解决方案
    操作系统(OS)被誉为程序员的三大浪漫之一,常被比喻为计算机的灵魂、大脑、内核和基石,其重要性不言而喻。本文将详细介绍如何从零开始构建个人专属的操作系统解决方案,涵盖从需求分析到系统设计、开发与测试的全过程,帮助读者深入理解操作系统的本质与实现方法。 ... [详细]
  • 如何将PHP文件上传至服务器及正确配置服务器地址 ... [详细]
  • SQLmap自动化注入工具命令详解(第28-29天 实战演练)
    SQL注入工具如SQLMap等在网络安全测试中广泛应用。SQLMap是一款开源的自动化SQL注入工具,支持12种不同的数据库,具体支持的数据库类型可在其插件目录中查看。作为当前最强大的注入工具之一,SQLMap在实际应用中具有极高的效率和准确性。 ... [详细]
  • 在 Debian 11 系统中部署 CMake 的详细步骤与最佳实践
    CMake是一个免费、开源、跨平台的工具系列,旨在构建、测试和打包软件. CMake用于使用简单的平台和独立于编译器的配置文件来控制软件编译过程,并生成可在您选择的编译器环境中使用 ... [详细]
  • 个人博客https:juejin.cnuser176366088104638和http:blog.wuzhenyu.com.cncmake编译动态库和链接动态库cmake中&#x ... [详细]
  • 在CentOS上部署和配置FreeSWITCH
    在CentOS系统上部署和配置FreeSWITCH的过程涉及多个步骤。本文详细介绍了从源代码安装FreeSWITCH的方法,包括必要的依赖项安装、编译和配置过程。此外,还提供了常见的配置选项和故障排除技巧,帮助用户顺利完成部署并确保系统的稳定运行。 ... [详细]
  • 本文详细介绍了使用响应文件在静默模式下安装和配置Oracle 11g的方法。硬件要求包括:内存至少1GB,具体可通过命令`grep -i memtotal /proc/meminfo`进行检查。此外,还提供了详细的步骤和注意事项,确保安装过程顺利进行。 ... [详细]
  • Go语言实现Redis客户端与服务器的交互机制深入解析
    在前文对Godis v1.0版本的基础功能进行了详细介绍后,本文将重点探讨如何实现客户端与服务器之间的交互机制。通过具体代码实现,使客户端与服务器能够顺利通信,赋予项目实际运行的能力。本文将详细解析Go语言在实现这一过程中的关键技术和实现细节,帮助读者深入了解Redis客户端与服务器的交互原理。 ... [详细]
  • PyFasterRCNN配置详解与优化指南
    本文主要讲解Faster-RCNN的配置过程,以及配置过程中遇到问题的解决方案。 1.下载工程gitclone--recursivehttps:github.comr ... [详细]
author-avatar
鸟的蛋蛋
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有