android 源码编译选项-编译器的工作过程

要运行代码,必须首先将其转换为二进制补码机器代码。 这是编译器的工作。

例如下面的源代码(假设文件名为test.c)。

#include 
int main(void)
{
  fputs("Hello, world!n", stdout);
  return 0;
}

你必须首先使用编译器来处理它并且能够运行它。

$ gcc test.c
$ ./a.out
Hello, world!

对于复杂的项目,编译过程也必须分为三个步骤。

$ ./configure
$ make  
$ make install

这个命令到底是做什么的? 大部分书籍和资料都含糊其辞,只说可以这样编,而没有进一步解释。

本文将介绍编译器的工作过程,即前面三个命令各自的任务。 我主要参考了AlexSmith的文章《BuildingCProjects》。 需要说明的是,本文主要针对gcc编译器,即针对C和C++,不一定适用于其他语言的编译。

第一步配置(config)

编译器开始工作之前,需要知道当前的系统环境,比如标准库在哪里,软件安装在哪里,需要安装哪些组件等等。 这是因为不同计算机的系统环境不同。 通过指定编译参数,编译器可以灵活适应环境,编译出可以在各种环境下运行的机器代码。 确定编译参数的这个步骤称为“配置”。

这些配置信息存储在一个配置文件中,按照约定,该文件是一个称为configure 的脚本文件。 一般是由autoconf工具生成的。 编译器通过运行这个脚本知道编译参数。

配置脚本已经尽力考虑到不同系统之间的差异,但它给了各种编译参数的默认值。 如果用户的系统环境比较异常,或者有一些特定的需求,就需要自动向configure脚本提供编译参数。

$ ./configure --prefix=/www --with-mysql

里面的代码是php源码的编译配置,用户指定安装的文件保存在www目录下,但是编译时添加了mysql模块的支持。

第二步确定标准库和头文件的位置

源代码肯定会使用标准库函数(standard library)和头文件(header)。 它们可以存储在系统的任何目录中,编译器实际上没有办法手动测量它们的位置,只能通过配置文件。

编译的第二步是从配置文件中知道标准库和头文件的位置。 一般来说,配置文件都会给出一个列表,枚举出几个具体的目录。 编译时,编译器会进入这些目录以查找目标。

第三步,确定依赖关系

对于小型项目,源代码文件之间往往存在依赖关系,编译器需要确定编译的顺序。 假设文件A依赖于文件B,编译器应该确保以下两点。

(1) 编译B文件后才编译A文件。

(2) 当B文件改变时,A文件会重新编译。

编译顺序存储在一个名为 makefile 的文件中,其中列出了哪些文件首先编译,哪些文件最后编译。 makefile是由configure脚本运行生成的,这就是为什么编译时必须先运行configure。

在确定依赖关系的同时,编译器还会确定编译时将使用哪些头文件。

第四步是头文件的预编译(precompilation)

不同的源代码文件可能引用相同的头文件(例如stdio.h)。 编译时,头文件也必须一起编译。 为了节省时间,编译器在编译源代码之前先编译头文件。 这样可以保证头文件只需要编译一次,不需要每次使用时都重新编译。

但是,并非头文件的所有内容都会被预编译。 用于声明宏的#define命令不会被预编译。

第五步预处理(Preprocessing)

预编译完成后,编译器开始替换源代码中的bash头文件和宏。 以本文开头的源码为例。 它包含头文件stdio.h,替换后如下所示。

extern int fputs(const char *, FILE *);
extern FILE *stdout;
int main(void)
{
    fputs("Hello, world!n", stdout);
    return 0;
}

为了便于阅读,里面的代码只截取了头文件中与源代码相关的部分,即fputs和FILE的声明,省略了stdio.h的其他部分(因为它们很长)。 另外,里面代码的头文件并没有经过预编译,但实际上是把预编译的结果插入到了源代码中。 编译器会在这一步删除注释。

这一步称为“预处理”(Preprocessing),因为完成后,真正的处理才会开始。

步骤6 编译

预处理后,编译器开始生成机器代码。 对于个别编译器来说,还有一个中间步骤,会先将源代码转换为汇编代码(汇编),然后再将汇编代码转换为机器代码。

以下是由本文开头的源代码转换而来的汇编代码。

    .file   "test.c"
    .section    .rodata
.LC0:
    .string "Hello, world!n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movq    stdout(%rip), %rax
    movq    %rax, %rcx
    movl    $14, %edx
    movl    $1, %esi
    movl    $.LC0, %edi
    call    fwrite
    movl    $0, %eax
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Debian 4.9.1-19) 4.9.1"
    .section    .note.GNU-stack,"",@progbits

这些转码后的文件称为目标文件。

步骤 7 链接

目标文件还不能运行android 源码编译选项,必须进一步转换为可执行文件。 如果仔细看上一步的转码结果,你会发现引用了stdout函数和fwrite函数。 也就是说,程序要正常运行,不仅需要里面的代码,还需要C语言的标准库提供的stdout和fwrite这两个函数的代码。

编译器的下一步是将外部函数的代码(通常是后缀为.lib和.a的文件)添加到可执行文件中。 这称为链接。 这些通过复制的方式向可执行文件添加外部函数库的形式称为静态链接(static linking),后面会提到动态链接(dynamic linking)。

make命令的作用是从第四步头文件预编译开始,完成这一步。

第八步安装(Installation)

上一步的连接是在显存中进行的,即编译器在显存中生成可执行文件。 接下来,必须将可执行文件保存到用户先前指定的安装目录中。

从表面上看,这一步很简单,只需将可执行文件(带有相关数据文件)复制到其中即可。 而实际上这一步还必须完成创建目录、保存文件、设置权限等步骤。 这整个保存过程就称为“安装”(Installation)。

第九步 连接操作系统

安装可执行文件后,它必须以某种方式通知操作系统它已准备好使用该程序。 比如我们安装了一个文本阅读程序,经常想双击txt文件,程序就会手动运行。

这就需要在操作系统中注册程序的元数据:文件名、文件描述、关联的后缀等等。 在Linux系统中,这些信息一般存储在/usr/share/applications目录下的.desktop文件中。 另外,在Windows操作系统中,还需要在开始菜单中创建快捷方式。

这种东西叫做“操作系统连接”。 makeinstall命令用于完成“安装”和“操作系统连接”两个步骤。

第十步,生成安装包

到这里android 源码编译选项,源码编译的整个过程就基本完成了。 而只有极少数用户愿意耐心地从头到尾经历这个过程。 事实上,如果你只把源代码交给用户,他们会认为你是一个不友好的人。 大多数用户想要的是立即运行的二进制补码可执行程序。 这就需要开发者将上一步生成的可执行文件制作成可分发的安装包。

因此,编译器还必须具有生成安装包的功能。 一般将可执行文件(带有相关数据文件)作为压缩文件包保存在一定的目录结构中,交给用户。

第十一步动态链接(Dynamiclinking)

正常情况下,此时,程序已经可以运行了。 至于运行时(runtime)期间发生的事情,与编译器无关。 而且,开发人员可以在编译阶段选择将可执行文件链接到外部函数库的形式,无论是静态链接(编译时链接)还是动态链接(运行时链接)。 所以,最后但并非最不重要的一点是所谓的动态连接。

前面提到,静态链接就是将外部函数库复制到可执行文件中。 这样做的好处是适用范围比较广,不用担心用户机器上缺少某个库文件; 缺点是安装包会比较大,而且多个应用程序之间很难共享库文件。 动态连接的做法恰恰相反。 外部函数库不会进入安装包,只是在运行时动态引用。 优点是安装包会比较小,多个应用可以共享库文件; 缺点是用户必须提前安装库文件,且版本和安装位置必须符合要求,否则无法正常使用。

实际上,大多数软件都使用动态链接和共享库文件。 对于这些动态共享库文件,Linux平台是后缀为.so的文件,Windows平台是.dll文件,Mac平台是.dylib文件。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

悟空资源网 源码编译 android 源码编译选项-编译器的工作过程 https://www.wkzy.net/game/183546.html

常见问题

相关文章

官方客服团队

为您解决烦忧 - 24小时在线 专业服务