精华内容
下载资源
问答
  • 编译原理_编译过程概述

    千次阅读 2019-04-11 01:26:12
    文章目录概述词法分析语法分析语义分析中间代码生成代码优化目标代码生成符号表管理(表格管理)出错处理 概述 编译程序即是将高级语言书写的源程序翻译成与之等价的目标...以中间代码为分水岭的原因是把编译过程分解...

    概述

    编译程序即是将高级语言书写的源程序翻译成与之等价的目标程序(汇编语言或机器语言)。其工作可分为六个阶段,见下图:

    Compile Procedure

    对于编译的各个阶段,逻辑上可以划分为前端和后端两部分。前端包括词法分析到中间代码生成中各个阶段的工作,后端则是优化及目标代码生成的阶段。

    以中间代码为分水岭的原因是把编译过程分解为与机器有关和无关两个部分。这样对同一种程序语言只需要一个前端,只要针对不同的机器开发不同的后端即可,前后端结合形成编译器。当语言改动时也只涉及前端部分的维护。而对于不同的程序语言只要有不同的前端,然后与同一个后端结合就可以得到程序语言在某种机器上的编译器了。

    下面简单介绍一下每个步骤(模块)的目的和工作,注意,下文步骤中使用的X,Y,Z等符号,在词法分析后实际上会是一个内部标识符,之所以照用原文只是为了方便理解。

    词法分析

    源程序可以看做是多行的字符串,词法分析是编译过程的第一阶段,其任务是对源程序逐字符扫描,从中识别出一个个“单词”,“单词”又叫做符号,它是程序语言的基本语法单位,如关键字(保留字)、标识符、常数、运算符、分隔符等。

    词法分析程序输出的“单词”以二元组的形式输出,即其种类和值。词法分析过程依据语言的词法规则,即描述“单词”结构的规则。

    如:

    PASCAL源程序一则:

    VAR X,Y,Z:real;
    X:= Y+Z*60;
    

    词法分析阶段将语句分割成以下单词序列:

    类型类型
    1. 关键字VAR9. 分号;
    2. 标识符X10. 标识符X
    3. 逗号,11. 赋值号:=
    4. 标识符Y12. 标识符Y
    5. 逗号,13. 加号+
    6. 标识符Z14. 标识符Z
    7. 冒号:15. 乘号*
    8. 标准标识符real16. 整常数60
    17. 分号;

    语法分析

    在词法分析结果的基础上,语法分析是根据语言的规则将单词符号序列分解成语法单位,如“表达式”、“语句”、”程序“等。语法规则就是语法单位的构成规则,通过语法分析确定整个输入串能否构成一个语法上正确的程序。如果程序没有错误,语法分析后就能正确的构造出 语法树;否则就会指出语法错误,并给出诊断。

    词法分析和语法分析本质上都是在对源程序的结构进行分析。

    如:

    根据上面词法分析的结果可以构造出的语法树如下图:

    Syntax trees

    语义分析

    语义分析阶段主要分析语法结构的含义,检查源程序是否包含静态语义错误,并收集类型信息供代码生成阶段使用。只有语法和语义都正确的源程序才能翻译成正确的目标程序。

    语义分析的主要工作就是对类型进行分析和检查,一般类型检查包括两点:类型载体及在其上的运算。如,整除取余运算符只能对整数使用,如果运算对象是浮点数就认为是类型错误。

    在确定源程序语法和语义后,就可以对其进行翻译并给出源程序的内部表示。对于声明语句,要记录所遇到的符号信息,此阶段还负责填写校验 符号表,对于可执行语句则分析其结构合理性,并补充必要的步骤。

    如:

    对于上面的变量声明语句VAR X,Y,Z:real;应生成下面的符号表(real类型占4位)

    符号类型逻辑地址
    Xreal0
    Yreal4
    Zreal8

    对于上面变量赋值语句X:= Y+Z*60;生成的语法树经过语义分析后应如下图,其中增加了一个语义处理点 inttoreal,它的作用是将整型数转换为浮点数:

    Syntax tree after analyse

    中间代码生成

    该阶段是根据语义分析的结果生成中间代码,“中间”即意味着并非可执行的机器码,它是一种简单含义明确的记号系统,有若干种形式,但所有中间代码的共同特征均为与具体机器无关,类似于算法伪代码的作用。最常用的中间代码是一种与汇编高度类似的三地址码,采用四元式实现,其形式为:(运算符, 运算对象1, 运算对象2, 运算结果)

    如:

    对于上述提到的赋值语句X:= Y+Z*60;可根据语义分析的结果生成以下四元序列式:

    1. (inttoreal, 60, -, t1)
    2. (*, Z, t1, t2)
    3. (+, Y, t2, t3)
    4. (:=, t3, -, X)

    其中t1, t2, t3均为编译程序生成的临时变量,用于存放临时的结果。

    代码优化

    由于编译器将源程序翻译成中间代码的工作是按固定模式进行的,因此可以发现中间代码中往往在时间和空间上均有较大浪费。当要生成高效的目标代码则必须进行优化,优化可以在中间代码生成阶段进行,也可以在目标代码生成阶段执行。

    由于中间代码与具体机器无关,因此对中间代码的优化主要是对程序的控制流和数据流的分析之上

    如:

    对上述赋值语句X:= Y+Z*60;生成的四元序列式优化,可以发现60是已知的常数,将它转换为浮点数60.0也可以在编译时完成,没有必要生成一个四元式。同时,发现t3的作用只是将结果传递给X,同样也不需要生成一个四元式。因此可优化成如下等价四元序列式:

    1. (*, Z, 60.0, t1)
    2. (+, Y, t1, X)

    当然这只是很简单的优化,实际上的优化要复杂的多,会涉及公共子表达式的提取等更多技术。

    目标代码生成

    目标代码生成是编译器的最后一个阶段,这一阶段将中间代码转化为目标机器上的绝对指令代码、可重定位指令代码或汇编指令代码,这一阶段与具体机器密切相关。

    如:

    使用两个寄存器R1和R2将上述的四元序列式生成下面的目标代码:

    1. MOVF Z, R2
    2. MULF #60.0, R2
    3. MOVF Y, R1
    4. ADDF R2, R1
    5. MOVF R1, X

    符号表管理(表格管理)

    符号表的主要作用就是记录符号的信息,以辅助语义检查和代码生成。在编译过程中需要对符号表进行快速的查找插入、修改、删除等操作。

    符号表的建立一般始于词法分析阶段,但也可以始于词法分析和语义分析阶段。有时候符号表的使用还会伴随到目标代码的运行阶段。

    出错处理

    源程序不可避免的会出现错误,这些错误大致分为静态错误和动态错误。

    动态错误又称为动态语义错误,它们发生在程序运行时,例如变量为0时做除数、引用数组元素下标错误等。

    静态错误是编译阶段发现的程序错误,可分为语法错误和静态语义错误,如关键字拼写错误、标点符号错误、表达式缺少操作数、括号不匹配等问题就是语法错误,语义错误主要指语义分析阶段发现的运算符与运算对象类型不合法等错误。

    在编译发生错误时,编译过程应想办法能够绕过去,以便在一次编译中发现尽可能多的错误。

    展开全文
  • gcc编译过程

    千次阅读 2018-05-31 20:44:39
    Gcc的编译流程分为了四个步骤:  1.预处理,生成预编译文件(.i文件):  Gcc –E hello.c –o hello.i  2.编译,生成汇编代码(.s文件):  Gcc –S hello.i –o hello.s  3.汇编,生成目标文件(.o文件):...

    Gcc的编译流程分为了四个步骤:

        1.预处理,生成预编译文件(.i文件):

            Gcc –E hello.c –o hello.i
        2.编译,生成汇编代码(.s文件):

            Gcc –S hello.i –o hello.s
        3.汇编,生成目标文件(.o文件):
            Gcc –c hello.s –o hello.o
        4.链接,生成可执行文件:
            Gcc hello.o –o hello

        在成功编译之后,就进入了链接阶段。在这里涉及到一个重要的概念:函数库。

    读者可以重新查看这个小程序,在这个程序中并没有定义”printf”的函数实现,且在预编译中包含进的”stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实现”printf”函数的呢?最后的答案是:系统把这些函数实现都被做到名为libc.so.6的库文件中去了,在没有特别指定时,Gcc会到系统默认的搜索路径”/usr/lib”下进行查找,也就是链接到libc.so.6库函数中去,这样就能实现函数”printf”了,而这也就是链接的作用。

        函数库一般分为静态库和动态库两种。静态库是指编译链接时,把库文件的代码全部加入到可执行文件中,因此生成的文件比较大,但在运行时也就不再需要库文件了。其后缀名一般为”.a”。动态库与之相反,在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,这样可以节省系统的开销。动态库一般后缀名为”.so”,如前面所述的libc.so.6就是动态库。Gcc在编译时默认使用动态库。

        整个过程如果想一步到位:

           gcc hello.c -o hello

    即可

        gcc简介
        Linux系统下的gcc(GNU C Compiler)是GNU推出的功能强大、性能优越的多平台编译器,是GNU的代表作品之一。gcc是可以在多种硬体平台上编译出可执行程序的超级编译器,其执行效率与一般的编译器相比平均效率要高20%~30%。gcc编译器能将C、C++语言源程序、汇程式化序和目标程序编译、连接成可执行文件,如果没有给出可执行文件的名字,gcc将生成一个名为a.out的文件。在Linux系统中,可执行文件没有统一的后缀,系统从文件的属性来区分可执行文件和不可执行文件。而gcc则通过后缀来区别输入文件的类别,下面我们来介绍gcc所遵循的部分约定规则。
        .c为后缀的文件,C语言源代码文件;
        .a为后缀的文件,是由目标文件构成的档案库文件;
        .C,.cc或.cxx 为后缀的文件,是C++源代码文件;
        .h为后缀的文件,是程序所包含的头文件;
        .i 为后缀的文件,是已经预处理过的C源代码文件;
        .ii为后缀的文件,是已经预处理过的C++源代码文件;
        .m为后缀的文件,是Objective-C源代码文件;
        .o为后缀的文件,是编译后的目标文件;
        .s为后缀的文件,是汇编语言源代码文件;
        .S为后缀的文件,是经过预编译的汇编语言源代码文件。

        gcc的执行过程
        虽然我们称gcc是C语言的编译器,但使用gcc由C语言源代码文件生成可执行文件的过程不仅仅是编译的过程,而是要经历四个相互关联的步骤∶预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。命令gcc首先调用cpp进行预处理,在预处理过程中,对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析。接着调用cc1进行编译,这个阶段根据输入文件生成以.o为后缀的目标文件。汇编过程是针对汇编语言的步骤,调用as进行工作,一般来讲,.S为后缀的汇编语言源代码文件和汇编、.s为后缀的汇编语言文件经过预编译和汇编之后都生成以.o为后缀的目标文件。当所有的目标文件都生成之后,gcc就调用ld来完成最后的关键性工作,这个阶段就是连接。在连接阶段,所有的目标文件被安排在可执行程序中的恰当的位置,同时,该程序所调用到的库函数也从各自所在的档案库中连到合适的地方。

        gcc的基本用法和选项
        在使用gcc编译器的时候,我们必须给出一系列必要的调用参数和文件名称。gcc编译器的调用参数大约有100多个,其中多数参数我们可能根本就用不到,这里只介绍其中最基本、最常用的参数。
        gcc最基本的用法是∶gcc [options] [filenames]
        其中options就是编译器所需要的参数,filenames给出相关的文件名称。
        -c,只编译,不连接成为可执行文件,编译器只是由输入的.c等源代码文件生成.o为后缀的目标文件,通常用于编译不包含主程序的子程序文件。
        -o output_filename,确定输出文件的名称为output_filename,同时这个名称不能和源文件同名。如果不给出这个选项,gcc就给出预设的可执行文件a.out。
        -g,产生符号调试工具(GNU的gdb)所必要的符号资讯,要想对源代码进行调试,我们就必须加入这个选项。
        -O,对程序进行优化编译、连接,采用这个选项,整个源代码会在编译、连接过程中进行优化处理,这样产生的可执行文件的执行效率可以提高,但是,编译、连接的速度就相应地要慢一些。
        -O2,比-O更好的优化编译、连接,当然整个编译、连接过程会更慢。
        -Idirname,将dirname所指出的目录加入到程序头文件目录列表中,是在预编译过程中使用的参数。
        C程序中的头文件包含两种情况∶
        A)#include
        B)#include “myinc.h”
        其中,A类使用尖括号(< >),B类使用双引号(“ ”)。对于A类,预处理程序cpp在系统预设包含文件目录(如/usr/include)中搜寻相应的文件,而对于B类,cpp在当前目录中搜寻头文件,这个选项的作用是告诉cpp,如果在当前目录中没有找到需要的文件,就到指定的dirname目录中去寻找。在程序设计中,如果我们需要的这种包含文件分别分布在不同的目录中,就需要逐个使用-I选项给出搜索路径。
        -Ldirname,将dirname所指出的目录加入到程序函数档案库文件的目录列表中,是在连接过程中使用的参数。在预设状态下,连接程序ld在系统的预设路径中(如/usr/lib)寻找所需要的档案库文件,这个选项告诉连接程序,首先到-L指定的目录中去寻找,然后到系统预设路径中寻找,如果函数库存放在多个目录下,就需要依次使用这个选项,给出相应的存放目录。-lname,在连接时,装载名字为“libname.a”的函数库,该函数库位于系统预设的目录或者由-L选项确定的目录下。例如,-lm表示连接名为“libm.a”的数学函数库。上面我们简要介绍了gcc编译器最常用的功能和主要参数选项,更为详尽的资料可以参看Linux系统的联机帮助。
    假定我们有一个程序名为test.c的C语言源代码文件,要生成一个可执行文件,最简单的
    办法就是∶
        gcc test.c
        这时,预编译、编译连接一次完成,生成一个系统预设的名为a.out的可执行文件,对于稍为复杂的情况,比如有多个源代码文件、需要连接档案库或者有其他比较特别的要求,就要给定适当的调用选项参数。再看一个简单的例子。整个源代码程序由两个文件testmain.c 和testsub.c组成,程序中使用了系统提供的数学库,同时希望给出的可执行文件为test,这时的编译命令可以是∶
        gcc testmain.c testsub.c -lm -o test
        其中,-lm表示连接系统的数学库libm.a,这个过程可以用图12-1框图描述。

    展开全文
  • 内核单个.o文件的编译过程

    千次阅读 2017-02-23 12:41:45
    kbuid系统,单个.o文件的编译过程

    逐步地我们已经对kbuild有了一定的了解,知道了kbuild还会有自己定义的函数,以及这些函数会在一个类似c语言的头文件中定义。做了这么多准备,该是时候探索真正的代码编译了。先来看看一个.o文件是如何编译的。

    比如我们拿这个文件来做例子:

    make mm/memblock.o

    找到我们的目标

    有了刚才的经验,这次我们顺着之前的思路。先到根目录的Makefile中找找线索。有时候找代码除了经验,还得有点运气。不知道你这次有没有找到呢?

    单个文件的目标还是在根目录Makefile文件中。

    %.o: %.c prepare scripts FORCE
        $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)

    你看,这格式其实和我们平时自己写的规则是差不多的。.o的文件依赖于同名的.c文件,然后执行了一个命令来生成。不过呢,确实还多了些东西,包括一些特殊的依赖条件,以及这个命令长得也有点丑。

    俗话说的好,恶人自有恶人磨,长得丑的代码也有丑办法来解读。

    把上面这段代码添加一个井号

    %.o: %.c prepare scripts FORCE
        #$(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)

    再运行一次

    $ make mm/memblock.o
      CHK     include/config/kernel.release
      CHK     include/generated/uapi/linux/version.h
      CHK     include/generated/utsrelease.h
      CHK     include/generated/timeconst.h
      CHK     include/generated/bounds.h
      CHK     include/generated/asm-offsets.h
      CALL    scripts/checksyscalls.sh
    # @make -f ./scripts/Makefile.build obj=mm mm/memblock.o

    怎么样,通过显示实际执行的命令,是不是你感觉熟悉了些?从显示出的命令来看,生成mm/memblock.o这个目标文件是重新又调用了一次make,而这次使用的是script/Makefile.build这个规则文件,传入的参数是obj=mm,目标还是mm/memblock.o。

    你看,是不是又清晰了一些?

    展开那串命令行

    现在我们来看那串命令行究竟是如何展开的。

        $(Q)$(MAKE) $(build)=$(build-dir) $(target-dir)$(notdir $@)

    第一招,我们先来拆分。这条命令中出现了好几个变量,Q, MAKE, build, build-dir, target-dir。前面两个就是定义成@和make的,基本问题不大也比较好理解。关键是后面三个。

    build-dir 和 target-dir

    可巧,最后两个变量的定义就在这组规则的上方。我们拿来先看一下。

    # Single targets
    # ------------------------------------------------------------
    # Single targets are compatible with:
    # - build with mixed source and output
    # - build with separate output dir 'make O=...'
    # - external modules
    #
    #  target-dir => where to store outputfile
    #  build-dir  => directory in kernel source tree to use
    
    ifeq ($(KBUILD_EXTMOD),)
            build-dir  = $(patsubst %/,%,$(dir $@))
            target-dir = $(dir $@)
    else
            zap-slash=$(filter-out .,$(patsubst %/,%,$(dir $@)))
            build-dir  = $(KBUILD_EXTMOD)$(if $(zap-slash),/$(zap-slash))
            target-dir = $(if $(KBUILD_EXTMOD),$(dir $<),$(dir $@))
    endif

    看注释,这两个变量一个是做编译的路径,一个是目标存放的路径。

    定义本身分为两种情况
    * 当KBUILD_EXTMOD为空,表示不是编译内核模块
    * 当KBUILD_EXTMOD不为空,表示是在编译内核模块

    这次我们并没有编译内核模块,所以采用的是第一种情况的定义。从显示出的命令来看build-dir和target-dir分别定义为mm和mm/。说明指向的路径是一样的,只是相差了最后的一个/。

    build

    五个变量基本理解了四个,那现在来看看最后一个build。

    通过我们比较丑的方法得到最后展开的命令来看,这个build变量包含的是

    -f ./scripts/Makefile.build obj

    关键它在哪里呢?还记得上文找过的那个“头文件”么?对了,还是在那scripts/Kbuild.include。

    ###
    # Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
    # Usage:
    # $(Q)$(MAKE) $(build)=dir
    build := -f $(srctree)/scripts/Makefile.build obj

    瞧,是不是和我们估计得长得差不多。

    再进一步

    终于把第一步的命令看明白了,现在是时候研究规则文件scripts/Makefile.build是如何编译出mm/memblock.o的了。

    按照同样的方法在scripts/Makefile.build文件中找.o的目标。找啊找啊找,终于看到一条像的了。

    # Built-in and composite module parts
    $(obj)/%.o: $(src)/%.c $(recordmcount_source) $(objtool_obj) FORCE
        $(call cmd,force_checksrc)
        $(call if_changed_rule,cc_o_c)

    嗯,究竟是不是这条规则? 还记得我们的丑办法么?留给大家自己试验一次~

    依赖条件我们暂时也不看了,不是关注我们的目标–.o文件是怎么编译出来的。看这个规则中的两条命令,第一条是做代码检查的,那暂时也不关注吧。第二条看着名字就有点像,cc_o_c,把c代码通过cc制作成o文件。这个正是我们要关注的,就分析它了。

    接近真相

    上一节,我们把关注的焦点定在了这条语句。

        $(call if_changed_rule,cc_o_c)

    是不是看着有点丈二和尚摸不少头脑?先别着急,我们还是一点点来分析。

    call的用法在上一篇中也已经解释了,其实就是调用if_changed_rull这个变量。既然如此,那我们就来找找这个变量呗~

    再去我们的头文件scripts/Kbuild.include中找找看。是不是踏破铁鞋无觅处,得来全不费功夫。

    # Usage: $(call if_changed_rule,foo)
    # Will check if $(cmd_foo) or any of the prerequisites changed,
    # and if so will execute $(rule_foo).
    if_changed_rule = $(if $(strip $(any-prereq) $(arg-check) ),      \
        @set -e;                                                      \
        $(rule_$(1)), @:)

    嗯,是不是千万只草泥马又从心中奔腾而过了?刚才还觉得一切易如反掌,瞬间就变成了一头雾水。其实我的内心也是崩溃的,不过没办法,这文章都写了一大半了,总不能在这个时候停掉。自己挖的坑,硬着头皮也得把它填上了。

    静下心来一看,这其实就是一个if语句。当条件为真,执行逗号之前的动作。当条件为假,则执行后面那个@:。然后你再对照一下注释,诶,还真是这么个理儿。关于后面这个@:,我也不是很确认是个神马东西。然后查了一下git记录,发现就是为了减少一些讨厌的输出消息的。具体说明可以看这个kbuild: suppress annoying “… is up to date.” message。而后进一步发现shell中还有一个啥都不干的冒号语句

    感觉又有了一点点的小进步,是不是还是挺开心的。

    这下我们把注意集中在rule_$(1),仍然暂时先不看前置条件。刚才我们说了if_changed_rule是一个函数,传入的参数刚是cc_o_c。你看这时候正好用上,在这里展开后就是rule_cc_o_c。

    这里有点像回调函数的感觉,if_changed_rule负责判断有没有前置条件是新的,是否需要重新生成目标。如果需要,if_changed_rule就会调用所要求的函数再去生成目标。

    这一路走来真是不容易。给自己倒杯咖啡,听听音乐,歇一歇。马上就要看到最后的结果了。

    云开日出

    经过对if_changed_rule的分析,我们停在了rule_cc_o_c上。现在的任务就是找到它。

    这次比较简单,定义就在scripts/Makefile.build中。

    define rule_cc_o_c
        $(call echo-cmd,checksrc) $(cmd_checksrc)      \
        $(call cmd_and_fixdep,cc_o_c)                \
        $(cmd_modversions_c)                         \
        $(cmd_objtool)                               \
        $(call echo-cmd,record_mcount) $(cmd_record_mcount)
    endef

    又是一长串是不是? 不过你有没有发现熟悉的cc_o_c的身影?而且也是作为一个自定义的函数的参数?那我们来猜一下,这个cmd_and_fixdep函数也会在某个地方调用参数所指定的一个函数来完成生成目标的动作。

    那它在哪呢? 对了,和if_changed_rule一样,也是在scripts/Kbuild.include文件中。

    cmd_and_fixdep =                          \
        $(echo-cmd) $(cmd_$(1));              \
        ...

    为了不闹心,我就复制一下最关键的部分吧。你看,还是调用到了我们的参数。这次展开后就是cmd_cc_o_c了。它还是在scripts/Makefile.build文件中。

    cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $<

    好了,可以松一口气了。如果用过makefile的童鞋,看到这个是不是觉得很亲切?这就是我们平时手动编译的时候输入的命令。

    At last, you got it.

    整顿行囊

    刚才在探索如何编译单个.o文件的道路上一路飞奔,虽然终于我们找到了那条最最最后的语句在哪里,但是估计我们自己的行囊丢了一地。中间打下的江山可能自己也记不太清楚了。

    为了更好的理解kbuild,在下一次我们出发探索其他复杂的目标前,我们有必要在此总结这次探索的收获,就好像千里行军总得找个好地方,安营扎寨,整顿行囊,这样才能走得更远,更稳。

    执行顺序

    从文件执行顺序上整理如下:

        Makefile
        ---------------
        %.o: %.c
            make -f scripts/Makefile.build obj=mm mm/memblock.o
    
        scripts/Makefile.build
        ---------------
        $(obj)/%.o: $(src)/%.c
            $(call if_changed_rule,cc_o_c)
    
        scripts/Makefile.build
        ---------------
        rule_cc_o_c
            $(call cmd_and_fixdep,cc_o_c)
    
        scripts/Makefile.build
        ---------------
        cmd_cc_o_c
            $(CC) $(c_flags) -c -o $@ $<

    这么看或许可以更清楚一些。对单个.o文件的编译,一共是四个步骤。在我们平时写的makefile中,可能到第二步我就就可以直接写上cmd_cc_o_c的命令了。而在内核中,又多出了两步为了检查有没有依赖关系的更新及其他的一些工作。

    在探索的过程中或许会有总也没有尽头的感觉,而现在整理完一看,一共也就四个层次,是不是觉得好像也没有那么难了?是不是觉得不过如此了?

    scripts/Makefile.build

    这个文件几乎包含了所有重要的规则,rule_cc_o_c, cmd_cc_o_c都在这个文件中定义。以后我们会看到,凡事要进行编译工作,都会使用这个规则文件。

    scripts/Kbuild.include

    这个文件包含了一些有意思的函数,if_changed_rule, cmd_and_fixdep。而且这个文件被根目录Makefile和scripts/Makefile.build都包含使用。

    好了,这下真的要歇一歇了。吃个大餐慰劳一下自己吧~

    展开全文
  • Android 项目编译过程

    千次阅读 2013-05-17 23:58:19
    Android 工程构建的持续集成,需要搭建一套编译和打包自动化流程,比如建立每日构建系统、...首先,假定你的系统(Windows、Linux、Mac OS都行,本文默认使用Linux系统来举例子,但 Windows几乎没有什么差别)已

    Android 工程构建的持续集成,需要搭建一套编译和打包自动化流程,比如建立每日构建系统、自动生成发布文件等等。这些都需要我们对Android工程的编译和打包有一个比较深入的理解,例如知道它的每一步都做了什么,需要什么环境和工具,输入和输出是什么,等等。


    首先,假定你的系统(Windows、Linux、Mac OS都行,本文默认使用Linux系统来举例子,但在 Windows中几乎没有什么差别)已经安装了JDK和Android SDK, 如果没有安装,可以参考我先前的博客: Ubuntu搭建Eclipse+JDK+SDK的Android 和 Windows搭建Eclipse+JDK+SDK的Android

    再假定你的Android SDK的路径是ANDROID_SDK_HOME,你想要编译的Android OS版本是ANDROID_OS_VERSION(比如android-1.6、android-8、android-10等)。

    我们重点关心的是:

        (1)这个过程的输入是什么?

        (2)这个过程的输出是什么?

        (3)这个过程使用了什么工具?

    至于使用什么参数,可以自己去看对应命令的帮助文件,或者在网上搜索,这不是本文的重点。




    提前列出下列步骤中需要用到的工具,如下表:

    名称功能介绍在操作系统中的路径
    aaptAndroid资源打包工具${ANDROID_SDK_HOME}/platform-tools/appt
    aidlAndroid接口描述语言转化为.java文件的工具${ANDROID_SDK_HOME}/platform-tools/aidl
    javacJava Compiler${JDK_HOME}/javac或/usr/bin/javac
    dex转化.class文件为Davik VM能识别的.dex文件${ANDROID_SDK_HOME}/platform-tools/dx
    apkbuilder生成apk包${ANDROID_SDK_HOME}/tools/opkbuilder
    jarsigner.jar文件的签名工具${JDK_HOME}/jarsigner或/usr/bin/jarsigner
    zipalign字节码对齐工具${ANDROID_SDK_HOME}/tools/zipalign


    第一步:打包资源文件,生成R.java文件
    【输入】Resource文件(就是工程中res中的文件)、Assets文件(相当于另外一种资源,这种资源Android系统并不像对res中的文件那样优化它)、AndroidManifest.xml文件(包名就是从这里读取的,因为生成R.java文件需要包名)、Android基础类库(Android.jar文件)
    【输出】打包好的资源(一般在Android工程的bin目录可以看到一个叫resources.ap_的文件就是它了)、R.java文件(在gen目录中,大家应该很熟悉了)
    【工具】aapt工具,它的路径在${ANDROID_SDK_HOME}/platform-tools/aapt(如果你使用的是Windows系统,按惯例路径应该这样写:%ANDROID_SDK_HOME%\platform-tools\aapt.exe,下同)。


    第二步:处理AIDL文件,生成对应的.java文件(当然,有很多工程没有用到AIDL,那这个过程就可以省了)
    【输入】源码文件、aidl文件、framework.aidl文件
    【输出】对应的.java文件
    【工具】aidl工具


    第三步:编译Java文件,生成对应的.class文件
    【输入】源码文件(包括R.java和AIDL生成的.java文件)、库文件(.jar文件)
    【输出】.class文件
    【工具】javac工具


    第四步:把.class文件转化成Davik VM支持的.dex文件
    【输入】 .class文件(包括Aidl生成.class文件,R生成的.class文件,源文件生成的.class文件),库文件(.jar文件)
    【输出】.dex文件
    【工具】dex工具


    第五步:打包生成未签名的.apk文件
    【输入】打包后的资源文件、打包后类文件(.dex文件)、libs文件(包括.so文件,当然很多工程都没有这样的文件,如果你不使用C/C++开发的话)
    【输出】未签名的.apk文件
    【工具】apkbuilder工具


    第六步:对未签名.apk文件进行签名
    【输入】未签名的.apk文件
    【输出】签名的.apk文件
    【工具】jarsigner


    第七步:对签名后的.apk文件进行对齐处理(不进行对齐处理是不能发布到Google Market的)
    【输入】签名后的.apk文件
    【输出】对齐后的.apk文件
    【工具】zipalign工具


    知道了上面这些细节后,就可以实现很多我们想实现东西了,比如:编译流程自动化,例如我们可以使用某种脚本,像Windows下的批处理,linux下的Bash,Java下的Ant,Python、Perl这样的脚本语言,甚至直接用Java、.Net这们的强类型语言也是可以的。

    如果真正弄懂了上面的步骤,了解了编译打包过程的本质,你完全可以以你想要的任何方式实现它的自动化,这才是真正的“举一反三,以不变应万变”。再比如,对Android SDK的精简,大家知道现在Android SDK动辙几百兆,我们完全可以应用上面的知识,只保留必要的工具,把SDK精简到10M以下。当然,还可以做很多事情,前提是你真正弄懂了它。



    参考推荐:

    Android 工程的编译过程

    Android使用ANT打包,签名,混淆


    展开全文
  • Linux-C C语言编译过程

    千次阅读 2018-07-18 20:32:55
    Linux-C C语言编译过程 一、简述  GCC(GNU Compiler Collection,即 GNU 编译器套装),是一套由 GNU 开发的编程 语言编译器。简单介绍使用gcc编译器将hello.c文件编译成为hello可执行文件的过程。 伪...
  • 计算机编译过程

    千次阅读 2011-10-18 10:09:38
    计算机编译程序的基本过程编译程序是将高级语言的源程序翻译成与之等价的目标程序(汇编语言或机器语言) 1:词法分析词法分析的任务就是把源程序构成的字符串转换成单词序号序列.如:标识符,常数,关键字,运算符,分界符...
  • MTK的编译过程

    千次阅读 2010-10-12 17:37:00
    MTK 编译过程 常见问题
  • openwrt单个ipk编译过程

    千次阅读 2017-01-23 22:35:56
    前一篇博客,我们已经知道整个openwrt的编译顺序,本文我们来探讨与开发者息息相关的单个ipk的编译过程.开发者进行二次开发的时候,我们既可以单个编译ipk也可以完整编译整个镜像文件.完整编译的时候,我们选中的...
  • ANDROID系统编译过程详解

    万次阅读 2019-06-17 16:08:23
    Make命令执行的时候,默认会当前目录找到一个Makefile文件,然后根据Makefile文件的指令来对代码进行编译。也就是说,make命令执行的是Makefile文件的指令。Makefile文件的指令可以是编译命令,...
  • 编译原理学习(一)--编译以及编译过程

    万次阅读 多人点赞 2018-05-22 21:01:00
    【龙书】编译原理(第二版)学习与理解:1.也许我们这辈子都不会去实现一个编译器,但是我们至少要知道编译器是什么?为什么会需要编译器? ①编译器首先也是一种电脑程序。它会将用某种编程语言写成的源代码(原始...
  • chromium编译过程记录

    千次阅读 2016-04-12 15:10:21
    “墙”内编译chromium的方法。记录了需要修改代码的地方和设置代理的方法。以及简要的从下载源码到编译结束的流程。
  • 标签(空格分隔): 杂七杂八的问题有必要写一个博文记录自己Latex编译时遇到的各种问题,希望可以帮到遇到同样错误的亲故。讲真,一直没有系统的学习Latex,都是投哪个会直接拿那个会的模板来套,然后每次需要...
  • sqc文件的编译过程

    千次阅读 2015-08-26 10:21:43
    针对DB2的嵌入式开发的SQC代码文件,从SQC文件编译成可实行文件的过程,一般情况下都是通过这本程序的make文件进行编译的,这里对这个编译过程做一个简单的说明。 1.首先把自己编辑好的SQC文件(如:test.sqc...
  • 如题!想了解编译必要性还有不编译会怎么样 --------------------------------------------------
  • iOS Command + R 编译过程详解

    千次阅读 2018-10-02 23:27:53
    目录 编译器介绍 Clang+LLVM编译过程 记录Xcode编译一次全过程 ...国庆期间,晚上有空把知识点重新梳理下,方便以后查阅,毕竟看懂了不是真的懂,可能睡了一觉就乱了思路,很有必要把思路整理写出来...
  • libtorrentwindows下编译过程

    千次阅读 2010-03-29 15:27:00
    http://www.cppblog.com/hblhs/archive/2010/03/03/108769.html 上一篇关于libtorrent的随笔是有错误的,我的项目里中文路径的问题主要是多字节编码和宽字节编码混合,这样libtorrent里面面转换的时候怎么转都会...
  • GCC编译经过哪些过程

    千次阅读 2016-04-19 21:53:07
    gcc编译器是我们常用的一款交叉编译软件(交叉编译指的是:一个平台下可以编译出另一个平台执行的代码),因此,我们有必要了解gcc在编译过程中,做了哪些事?总的来说,gcc编译过程主要经过4个部分,分别是...
  • 搜索引擎 nutch2.3.1 编译 配置 底层存储HBase
  • Linux内核配置和编译过程详解

    万次阅读 2016-05-24 12:48:00
    还有每一个大项和文档的最后会有一个经验谈,它是一些高手们应对问题和处理特有硬件时的一些经验(这个还得靠各位)。 文档最后会发到网上,到时会根据网友们的回复随时进行更新。  我们的目的是让我们有一个
  • C++模板编译过程

    千次阅读 2010-11-10 12:10:00
    如何组织编写模板程序 <br />前言 常遇到询问使用模板到底是否容易的问题,我的回答是:...可是我需要自己编写模板类时,我首先遇到的事实却是 “传统”编程方法(*.h文件声明,*.cpp文件定义
  • UBOOT的编译过程

    千次阅读 2018-11-02 19:23:01
    一切从顶层目录的makefile 开始寻找答案: %_config:: unconfig @$(MKCONFIG) -A $(@:_config=) 其中%是通配符代表所有可能的字符 当make 之后先执行unconfig: unconfig: @rm -f $(obj)include/config.h...
  • JSP的编译过程

    万次阅读 2007-03-25 21:17:00
    而了解其中的变异方法和规则,对我们学习JSP是非常有好处的,可以说学习好了这个编译原理,就已经学习好了大部分的JSP知识,剩下的工作就只剩下熟记一些tablib和反复应用以使自己更加熟练而已了JSP会被编译成.java放...
  • DB2数据库的sqc程序编译过程

    千次阅读 2017-01-16 11:39:42
    这里的sqc程序是指用到db2数据库的应用程序,sql的嵌入式C编程。用到Oracle数据库的应用程序,sql的嵌入式c/c++编程则是pc程序,叫做Proc*c/c++编程。  1 DB2的嵌入sql程序处理过程 嵌入SQL程序处理,由...
  • linux内核编译过程的最终总结版

    万次阅读 2018-09-15 19:01:28
    Linux操作系统环境下重新编译内核。实验主要内容: A. 查找并且下载一份内核源代码,本实验使用最新的Linux内核2.6.36。 B. 配置内核。 C. 编译内核和模块。 D. 配置启动文件。 本次实验环境是Linux...
  • 编译过程分两步1. ./configure 2. make,当然可以通过配置只编译特定平台代码,这样速度会快很多,但所依赖的库都是差不多的,我编译时所有平台,这样方便以后的使用。 由于是新安装的ubuntu,肯定会缺少很多必要...
  • 使用命令“ionic cordova build android”编译打包Android apk时遇到不少问题,为了方便以后查阅,先将这些问题及解决方案记录下来(解决方案并不是唯一的)。   先说说本人开发环境的版本吧:ionic framework ...
  • Android系统编译过程分析

    万次阅读 2014-05-14 18:20:25
    envsetup.mk主要会读取由envsetup.sh写入环境变量的一些变量来配置 编译过程中的输出目录,combo里面主要定义了各种Host和Target结合的编译器和编译选项。 配置部分主要完成以下几个工作: a) 基于Android...
  • Android应用程序(APK)的编译打包过程

    万次阅读 多人点赞 2013-06-08 21:58:12
    现在很多人想对Android工程的编译和打包进行自动化,比如建立每日...那么我们就来挖掘一下Android的编译过程中的细节。 首先,我们假定你的系统(什么系统都行,不限于Linux还是Windows系统,当然,我这里默认使用Li

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 285,317
精华内容 114,126
关键字:

在编译过程中不是必要的