zoukankan      html  css  js  c++  java
  • gcc makefile

    GCC程序编译

    GCC (GNU C Compiler) 编译器,GNU 本身是一个计划,目标是开发出一套完全免费的操作系统,GCC就是他推出很好的多平台编译器,不管是嵌入式应用程序开发 还是做驱动开发内核开发 嵌入式内核开发 都需要用到它,用它可以编译链接C C++等程序,

    GCC 支持的体系结构有40余种,常见的有X86  ARM POWERPC 等等同时GCC还能运行在不同的操作系统中,如LINUX WINDOWS Solaris 等,GCC除支持C语言外还支持多种语言,如C++ ,Ada,Java 等

    在LINUX系统中,可执行的文件没有统一的后缀的,系统从文件的属性(x r w )来区分可行文件 和 不可执行文件

    GCC编译程序时,编译过程可以分为四个阶段:预处理 编译 汇编(Assembling)链接

    预处理:这个阶段 主要处理源文件中的 #ifdef  #include  #define 等命令,这个中间阶段会生产一个 *.i文件 实际工作通常不会专门生产这种文件, gcc -E test.c -o test.i

    编译:这个阶段把预处理后的结果编译成汇编或者目标模块,输入的是中间文件 *.i,编译后生产的是 汇编语言文件 *.s  这个阶段对应的GCC命令如下所示。

    汇编:这个阶段把编译出来的结果汇编成具体的CPU上的目标代码模块,输入的是汇编文件*.s,输出的是机器语言*.o,*o这个也叫做目标文件,这个文件虽然也是机器代码,但是不可执行, gcc -c test.s -o test.o

    链接:这个阶段 把多个目标代码模块连接生产一个大的目标模块,输入的是机器代码文件*.o 还有其他的机器代码文件和库文件  输出的是一个可执行的二进制代码文件 gcc test.o  -o test

    gcc -o test  first.c  second.c  third.c 该命令同时编译三个源文件 再将它们连成一个可执行的程序test( 这里不论是一个原文件还是多个原文件  被编译和连接的原文件中必须有且仅有一个main函数)

    GCC 通过后缀来区分输入文件的类别,.c .a(库文件) .C( .cc .cxx C++源文件)  .h .l(经过预处理的的C源文件) .ii(经过预处理的的C++源文件) .o(编译后的目标文件).s (汇编源文件) .S(经过预编译的汇编源代码文件)

    用makefile本质也使用gcc,没有差别。你完全可以用gcc命令编译模块

    GCC的使用

    GCC [options] [filenames] ,options 编译器所需要的编译选项,gcc大约有100多个编译选项, filenames要编译的文件名

    介绍编译选项:

    -D MACRO 定义宏 等于在程序中使用#define MACRO  “-DMODULE -D__KERNEL__ -DLINUX” 其实是GCC命令行的用法,等效于在一个头文件中定义:#define  MODULE     #define  __KERNEL__  #define  LINUX

    -pipe 在编译过程中 生产各种临时文件

    -o output_filename 确定可执行文件的名称为output_filename 默认是a.o(ut)

    -v 把整个过程的输出信息都打印出来

    -E 输出预处理的结果,不进行编译 汇编 和 连接

    -C(大写) 配合 -E使用 让预处理后的结果把注释保留  以方便阅读 

    -S 只将源文件编译成汇编代码,不进行汇编 和 连接

    -c 只编译 不连接成为可执行文件 编译器只是由输入的.c等代码文件生成.o后缀的目标文件

    -g 产生调试工具(GNU 的gdb)所必要的符号信息,要想对编译出的程序进行调试,就必须加入这个选项,也就是把调试开关打开

    -O 对程序进行优化编译,链接

    -O2 优化程度更深一些 ,优化后的程序 执行时候,要的时间比没有优化的要小,运行程序时 可以time 来计算程序的运行时间 time ./outimize(程序名)

    –I 当我们在编译一个程序的时候,包含了一个头文件假设叫做<a.h>,而a.h没有在include这个目录中,一个我们可以把这个头文件拷到这个目录中,另一个我们可以用指令来添加一个头文件的路径,可以有多个 -I 来指定过个路径 ,如指定 gcc –I (大写艾) gcc –I/home/lesson/part/3(头文件所在的目录) 编译的文件名 –o 输出的文件名 

    -L  添加库的 同理-I 搜寻库文件( *.a )的路径 ( -Ldir ) 

    -l(小艾尔) 在链接时 GCC默认链接C库,其他不会链接的,如果你要用到其他的库如数学库,你要先指明,或者你自己的库,如数学库是libm.a 我们写时可以省略掉前面lib 与后面的.a 他会自动加 写成 –lm 就行 gcc foo.c –L/home.lib –lfoo –o foo

    -static 静态链接库文件 静态链接区别于动态链接:

    Gcc 默认采用动态链接,.so为动态链接库 .a为静态库,静态链接时,连接器找出程序所需的函数,然后将他们拷贝到可执行文件中,组成一个文件,一旦链接成功,这个静态库也就不需要了,而动态链接,是在程序内留下一个标记指明当程序执行时首先必须载入这个库,动态链接节省空间,gcc –static hello.c –o hello

    -wall  生产所以警告信息 推荐大家使用这个选项

    -w(小)  不生产任何警告信息

    -D MACRO 定义宏 等于在程序中使用#define MACRO

    ///////////////////////////////////////////////////////////

    LINUX 2.4 内核文件 包括头文件 大概有一万个 2.6大概有2万个

    Linux 程序员 必须学会使用GNU make来构建和管理 自己的软件工程,GNU的make 能够使整个软件工程的编译链接只需要一个命令就可以完成。Make 只是一个命令,在执行时,需要一个命名为makefile的文件,makefile 文件描述了整个工程的编译连接等的规则,包括工程中的哪些源文件需要编译以及如何编译,需要创建那些库文件,以及如何创建这些库文件,如何最后产生我们想要得可执行文件

    在大型的开发项目中,通常有几十到上百个的源文件,如果每次均手工键入 gcc 命令进行编译的话,则会非常不方便。因此,人们通常利用 make 工具来自动完成编译工作。这些工作包括:如果仅修改了某几个源文件,则只重新编译这几个源文件;如果某个头文件被修改了,则重新编译所有包含该头文件的源文件。利用这种自动编译可大大简化开发工作,避免不必要的重新编译。实际上,make 工具通过一个称为 makefile 的文件来完成并自动维护编译工作。makefile 需要按照某种语法进行编写,其中说明了如何编译各个源文件并连接生成可执行文件,并定义了源文件之间的依赖关系。当修改了其中某个源文件时,如果其他源文件依赖于该文件,则也要重新编译所有依赖该文件的源文件。makefile 文件是许多编译器,包括 Windows NT 下的编译器维护编译信息的常用方法,只是在集成开发环境中,用户通过友好的界面修改 makefile 文件而已。默认情况下,GNU make 工具在当前工作目录中按如下顺序搜索 makefile:我的2.6.18的内核默认的呃是 Makefile 而不是makefile
    * GNUmakefile
    * makefile
    * Makefile


    在 UNIX 系统中,习惯使用 Makefile 作为 makfile 文件。如果要使用其他文件作为 makefile,则可利用类
    似下面的 make 命令选项指定 makefile 文件:
    $ make -f Makefile.debug

    -----------------------------------------------------------------------

    makefile 基本结构
    makefile 中一般包含如下内容:
    * 需要由 make 工具创建的项目,通常是目标文件和可执行文件。通常使用“目(target)”一词来表示要创建的项目。
    * 要创建的项目依赖于哪些文件。
    * 创建每个项目时需要运行的命令。

    Makefile里面包含了很多条规则:规则用于说明如何生存一个或者多个目标文件,描述的是在什么情况下,如何重建规则的目标文件 通常规则包含 目标的依赖关系  和重建的目标命令 

    一般规则的格式是:

    目标:依赖

    命令

    一个规则可以有多个命令行,每一条命令占一行,每一个命令行必须以TAB字符开始,TAB字符告诉make此行是一个命令行,make按照命令完成相应的动作

    # This makefile just is a example. #表注释

    //////////////////////////////////////////////////

    edit: main.o kbd.o  command.o

           insert.o ....

    (TAB)cc -o edit  main.o kbd.o  command.o

         insert.o ....

    main.o: main.c  defs.h

    (TAB)cc -c main.c

    insert.o:insert.c dfs.h

    (TAB)cc -c insert.c

    .......

    ////////////上面写法不好 因为不方便更新 改动/////////////////

    obj = main.o kbd.o command.o

         insert.o .....

    edit: $(obj)

         cc -o edit $(obj)

    //////////////////make 的编译隐含规则/////////////////////////

    编译时make会自动为这个.o文件寻找合适的依赖文件(对应得.c文件 对应是指除了后缀 其余部分都相同的两个文件)同时使用正确的命令来建立这个目标文件 这样写规则时 可以省略掉命令行 同时也可以省略掉 与*.o同名的C文件 只要在依赖关系中给出 包含的各个头文件 就可以

    obj = main.o kbd.o command.o

         insert.o .....

    edit: $(obj)

         cc -o edit $(obj)

    main.o:defs.h

    insert.o:dfs.h

    /////////////////////另一类根据依赖关系分类法/////////////////////

    obj = main.o kbd.o command.o

         insert.o .....

    edit: $(obj)

         cc -o edit $(obj)

    $(obj):defs.h

     main.o kbd.o command.o:d.h

     insert.o  command.o:k.h

    //////////////////////////////////////////////////

    在makefile中规则的顺序是很重要的,因为makefile中只应该有一个最终目标,其他的目标都是被这个目标所连带出来的,所以一定要让make知道你的最终目标是什么,makefile中目标可能有很多,如果没有指定,默认第一条规则的目标将被确立为最终的目标。

    当在shell中输入 make命令后,make将读取当前目录下 的Makefile 文件,并将Makefile中的第一个目标做为其执行的“终极目标”,开始处理第一个规则“终极目标”,处理这个过程 先处理他的依赖条件 根据这个条件 做出相应得动作 对后面的文件

    Makefile中 把那些没有任何依赖只有执行动作的目标称为“伪目标”,phony targets

    .PHONY:clean PHONY指明clean为一个伪目标

    Clean:

     Rm –f hello …

    -------------------------------------------------------------------------

    makefile 变量
    GNU的make 工具有许多便于表达依赖性关系以及建立目标的命令的特色。其中之一就是变量或宏的定义能力。如果你要以相同的编译选项同时编译十几个 C 源文件,而为每个目标的编译指定冗长的编译选项的话,将是非常乏味的。但利用简单的变量定义,可避免这种乏味的工作:

    # Define macros for name of compiler
    CC = gcc

    # Define a macr o for the CC flags
    CCFLAGS = -D_DEBUG -g -m486

    # A rule for building a object file
    test.o: test.c test.h
        $(CC) -c $(CCFLAGS) test.c

    在上面的例子中,CC 和 CCFLAGS 就是 make 的变量。GNU make 通常称之为变量,而其他 UNIX 的 make
    工具称之为宏,实际是同一个东西。在 makefile 中引用变量的值时,只需变量名之前添加 $ 符号,如
    上面的 $(CC) 和 $(CCFLAGS)。

    GNU make 的主要预定义变量
    GNU make 有许多预定义的变量,这些变量具有特殊的含义,可在规则中使用。下面给出了一些主要的
    预定义变量,除这些变量外,GNU make 还将所有的环境变量作为自己的预定义变量。
    预定义变量                含义
    $*              不包含扩展名的目标文件名称。
    $+              所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件。
    $<              第一个依赖文件的名称。
    $?              所有的依赖文件,以空格分开,这些依赖文件的修改日期比目标的创建日期晚。
    $@              目标的完整名称。$@代表目标
    $^              所有的依赖文件,以空格分开,不包含重复的依赖文件。
    $%              如果目标是归档成员,则该变量表示目标的归档成员名称。例如,如果目标名称
                    为 mytarget.so(image.o),则 $@ 为 mytarget.so,而 $% 为 image.o。
    AR              归档维护程序的名称,默认值为 ar。
    ARFLAGS         归档维护程序的选项。
    AS              汇编程序的名称,默认值为 as (编译器)。
    ASFLAGS         汇编程序的选项。
    CC              C 编译器的名称,默认值为 cc(编译器)。
    CCFLAGS         C 编译器的选项。
    CPP             C 预编译器的名称,默认值为 $(CC) -E。
    CPPFLAGS        C 预编译的选项。
    CXX             C++ 编译器的名称,默认值为 g++。
    CXXFLAGS        C++ 编译器的选项。
    FC              FORTRAN 编译器的名称,默认值为 f77。
    FFLAGS          FORTRAN 编译器的选项。

    //////////////////////////////////////////////////////////////////

    我们系统中装了gcc编译器,功能强大,但我们在编译一个文件时 即可用cc 也可以用gcc 他们的关系是

     

    Linux CC与Linux GCC的区别概括介绍。从名字上看,老的unix系统的CC程序叫做C Compiler。但GCC这个名字按GNU的说法叫做Gnu Compiler Collection。因为gcc包含很多编译器(C, C++, Objective-C, Ada, Fortran,and   Java)。所以它们是不一样的,一个是一个古老的C编译器,一个是编译器的Gnu的编译器的集合(Gcc里的C编译器比CC强大太多了,所以你没必要用CC)。当你调用gcc时不一定是调用的C/C++编译器,是gcc根据文件扩展名自动识别并调用对应的编译器,具体可查阅$man gcc。

    你是下载不到CC的,原因是:CC来自于昂贵的Unix系统,CC是商业软件,要想用你需要打电话,写订单,而不是打开你的Browser去download。

    linux下的cc是gcc的符号链接。可以通过$ls –l /usr/bin/cc来简单察看.而编译时看到的控制台输出CC则是一个指向gcc的变量,该变量是make程序的内建变量,就算你在Makefile中没有CC= ,该变量也会存在,并默认指向gcc。cc的符号链接和变量存在的意义在于源码的移植性,可以方便的用GCC来编译老的用cc编译的unix软件,甚至连Makefile都不要改。而且也便于linux程序在unix下编译。

    近几年的一个新情况是越来越多的unix用户,据我所知像solaris,bsd用户也不太使用CC了,人们都一定要装一个gcc,用它来编译C/C++程序。原因显而易见,gcc足够强大,健壮。支持估计目前为止只有它支持的ISO c/c++ 新特性。当然你最好不要使用night版本的gcc

     make是用来编译的,它从Makefile中读取指令,然后编译。

    make install是用来安装的,它也从Makefile中读取指令,安装到指定的位置,make install 就是读取makefile文件中 install:对应得语句 类似于 make clean

     

    Make是一个解释Makefile文件中指令的命令工具,其最基本的功能就是通过Makefile文件来描述源程序之间的相互关系并自动维护编译工作,它会告知系统以何种方式编译和链接程序。一旦确定完成Makefile文件,剩下的工作就只是在Linux终端下输入make这样的一个命令,就可以自动完成所有的编译任务,并生成目标程序。工程情况下GNU make的工作流程如下。

    1、查找当前目录下的Makefile文件

    2、初始化文件中的变量

    3、分析Makefile中的所有规则

    4、为所有的目标文件建立依赖关系

    5、根据依赖关系,决定哪些目标文件要重新生成

    6、执行生成命令

    //////////////////////////////////////////////////////////////////

    隐含规则
    GNU make 包含有一些内置的或隐含的规则,这些规则定义了如何从不同的依赖文件建立特定类型的目标。
    GNU make 支持两种类型的隐含规则:
    1: 后缀规则(Suffix Rule)。后缀规则是定义隐含规则的老风格方法。后缀规则定义了将一个具有某个
    后缀的文件(例如,.c 文件)转换为具有另外一种后缀的文件(例如,.o 文件)的方法。每个后缀规
    则以两个成对出现的后缀名定义,例如,将 .c 文件转换为 .o 文件的后缀规则可定义为:

    .c.o:
    $(CC) $(CCFLAGS) $(CPPFLAGS) -c -o $@ $<

    2:模式规则(pattern rules)。这种规则更加通用,因为可以利用模式规则定义更加复杂的依赖性规则。
    模式规则看起来非常类似于正则规则,但在目标名称的前面多了一个 % 号,同时可用来定义目标和依赖
    文件之间的关系,例如下面的模式规则定义了如何将任意一个 X.c 文件转换为 X.o 文件:

    %.c:%.o
    $(CC) $(CCFLAGS) $(CPPFLAGS) -c -o $@ $<

    ------------------------------------------------------------------------------

    运行 make
    我们知道,直接在 make 命令的后面键入目标名可建立指定的目标,如果直接运行 make,则建立第一个
    目标。我们还知道可以用 make -f mymakefile 这样的命令指定 make 使用特定的 makefile,而不是
    默认的 GNUmakefile、makefile 或 Makefile。但 GNU make 命令还有一些其他项,下面列举了一些make的命令行参数选项                       

    命令行选项              含义

    -C DIR              在读取 makefile 之前改变到指定的目录 DIR。

    -f FILE             以指定的 FILE 文件作为 makefile。
    -h                  显示所有的 make 选项。
    -i                  忽略所有的命令执行错误。
    -I DIR              当包含其他 makefile 文件时,可利用该选项指定搜索目录。
    -n                  只打印要执行的命令,但不执行这些命令。
    -p                  显示 make 变量数据库和隐含规则。
    -s                  在执行命令时不显示命令。
    -w                  在处理 makefile 之前和之后,显示工作目录。
    -W FILE             假定文件 FILE 已经被修改。

    ------------------------------------------------------------------------------

    变量 如果我们要为一个目标添加一个依赖 用变量就方便 只要修改变量赋值处就行

    如给hello 添加一个依赖 func3.o

    我们可以两种方法修改:

    1: hello:main.o func1.o func3.o

    Gcc main.o func1.o func3.o –o hello

    2: 用变量 obj=main.o func1.o func3.o

              Hello:$(obj)  gcc $(obj) –o hello

    Makefile 中系统默认的自动化变量

    $^ 代表所有的依赖文件

    $<代表第一个依赖文件

    如: hello:main.o func1.o func2.o

         Gcc main.o func1.o func2.o –o hello

    è  Hello: main.o func1.o func2.o

    è  Gcc  $^ -o $@

    Makefile 中 #后面的内容视为注释

    @取消回显

    !!!!!!!!!!!!!!

    特别注意:

    在编写makefile时 写命令时 前面的空白不是空格键 而是一个tab键 注意了 ,同时 用于分解多行的反斜线""后面不能有空格 这些很容易就犯错

    gcc前一定要 有一个tab分隔符,不能有空格;否则会出现“makefile:2: *** 遗漏分隔符  make中规定每一Shell命令之前的开头必须使用字符  当时第九行处我是顶格写的 所以不对 rm....所以在开头处加了一个TAB制表符 就OK了
    ////////////////////////////////////////////////////////////////////////////////////
    范例

    #include <linux/init.h>
    #include <linux/kernel.h>
    #include <linux/module.h>

    MODULE_LICENSE("GPL");

    static int hello_init(void)
    {
      printk(KERN_ALERT "hello module init ");
      return 0;
    }

    static void hello_exit(void)
    {
      printk(KERN_ALERT "hello module exit ");
    }

    module_init(hello_init);
    module_exit(hello_exit);

    //////////////////////////////////////
    ifneq ($(KERNELRELEASE),)
    obj-m := hello.o
    else
    KERNELDIR := /lib/modules/$(shell uname -r)/build
    PWD := $(shell pwd)

    default:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    endif

    clean:
        rm -f *.ko *.mod.c *.mod.o *.o

    编译模块

    #make

    清除

    #make clean

    /////////////////////说明如下/////////////////////////

    hello.c文件中调用的头文件

    init.h中的module_init(),module_exit()
    kernel.h中的printk(),KERN_ALERT
    module.h中的MODULE_LICENSE()

    Makefile文件中的核心

    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

    1)-C $(KERNELDIR)
    表示在$(KERNELDIR)目录下执行make命令。 编译内核模块时需要依赖内核代码的
    2)M=$(PWD) 表示当前目录
    表示包含$(PWD)下的Makefile文件。
    3)modules
    表示模块编译。
    4)用到了ifneq...else...endif语句
    由于开始还没定义KERNELRELEASE,所以只能执行else分支。
    而在执行
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    后,会在内核的Makefile中定义KERNELRELEASE,当再次进入本Makefile时,
    则只会执行ifneq的第一个分支,即
    obj-m := hello.o
    这一句话是非常重要的。事实上,这个Makefile做的本份工作就是它

  • 相关阅读:
    字符串转换的UnicodeDecodeError—— ‘\xa0’问题
    linux下nginx+uwsgi部署python应用
    字符串转换的UnicodeDecodeError—— ‘\xa0’问题
    python的get和post方式请求详解
    MindManager使用说明
    进入程序员的自由天地
    悦读上品 得乎益友
    C++ 是一门难学易用的语言!
    合上More Exceptional C++的瞬间
    从零开始学ASP.NET
  • 原文地址:https://www.cnblogs.com/airlove/p/4681113.html
Copyright © 2011-2022 走看看