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做的本份工作就是它

原文地址:https://www.cnblogs.com/airlove/p/4681113.html