精华内容
下载资源
问答
  • Linux内核Makefile文件

    2012-10-05 00:32:39
    Linux内核Makefile文件(翻译自内 核手册) 转载自:http://blog.chinaunix.net/uid-21651676-id-60377.html Linux 内核Makefile文件 --译自Linux2.6.x Kernel Makefiles 本文档描述了linux内核的...

    Linux内核Makefile文件(翻译自内 核手册)

    转载自:http://blog.chinaunix.net/uid-21651676-id-60377.html

    Linux 内核Makefile文件

    --译自Linux2.6.x Kernel Makefiles

    本文档描述了linux内核的makefile文 件。
    === 目录
         === 1 概述
         === 2 角色分工
         === 3 内核编译文件
            --- 3.1 目标定义
            --- 3.2 内嵌对象 - obj-y
            --- 3.3 可加载模块 - obj-m
            --- 3.4 导出符号
            --- 3.5 库文件 - lib-y
            --- 3.6 目录递归
            --- 3.7 编译标记
            --- 3.8 命令依赖
            --- 3.9 依赖关系
            --- 3.10 特殊规则
         === 4 辅助程序
            --- 4.1 简单辅助程序
            --- 4.2 组合辅助程序
            --- 4.3 定义共享库 
            --- 4.4 C++语言使用方法
            --- 4.5 辅助程序编译控制选项
            --- 4.6 何时建立辅助程序
            --- 4.7 使用hostprogs-$(CONFIG_FOO)
         === 5 编译清除机制
         === 6 体系Makefile文件
            --- 6.1 变量设置
            --- 6.2 增加预设置项
            --- 6.3 目录表
            --- 6.4 引导映像
            --- 6.5 编译非内核目标
            --- 6.6 编译引导映像命令
            --- 6.7 定制编译命令
            --- 6.8 预处理连接脚本
            --- 6.9 $(CC)支持功能
         === 7 Kbuild变量
         === 8 Makefile语言
         === 9 Credits
         === 10 TODO

    === 1 概述


    Makefile包括五部分:
         Makefile            顶层Makefile文件
         .config                  内核配置文件
         arch/$(ARCH)/Makefile      机器体系Makefile文件
         scripts/Makefile.*      所有内核Makefiles共用规则
         kbuild Makefiles      其它makefile文件
    通 过内核配置操作产生.config文件,顶层Makefile文件读取该文件的配置。顶层Makefile文件负责产生两个主要的程序:vmlinux (内核image)和模块。顶层Makefile文件根据内核配置,通过递归编译内核代码树子目录建立这两个文件。顶层Makefile文件文本一个名为 arch/$(ARCH)/Makefile的机器体系makefile文件。机器体系Makefile文件为顶层makefile文件提供与机器相关的 信息。每一个子目录有一个makefile文件,子目录makefile文件根据上级目录makefile文件命令启动编译。这些makefile使 用.config文件配置数据构建各种文件列表,并使用这些文件列表编译内嵌或模块目标文件。scripts/Makefile.*包含了所有的定义和规 则,与makefile文件一起编译出内核程序。

    === 2 角色分工


    人们与内核makefile存在四种不同 的关系:
    *用户* 用户使用"make menuconfig"或"make"命令编译内核。他们通常不读或编辑内核makefile文件或其他源文件。
    *普通开发者* 普通开发者维护设备驱动程序、文件系统和网络协议代码,他们维护相关子系统的makefile文件,因此他们需要内核makefile文件整体性的一般知 识和关于kbuild公共接口的详细知识。
    *体系开发者* 体系开发者关注一个整体的体系架构,比如sparc或者ia64。体系开发者既需要掌握关于体系的makefile文件,也要熟悉内核makefile文 件。
    *内核开发者* 内核开发者关注内核编译系统本身。他们需要清楚内核makefile文件的所有方面。
    本文档的读者对象是普通开发者 和系统开发者。

    === 3 内核编译文件


    内核中大多数makefile文件是使用kbuild基础架构的makefile文件。本章介绍 kbuild的makefile中的语法。
    3.1节“目标定义”是一个快速导引,后面各章有详细介绍和实例。
    --- 3.1 目标定义
         目标定义是makefile文件的主要部分(核心)。这些目标定义行定义了如何编译文件,特殊的兼容选项和递归子目录。
          最简单的makefile文件只包含一行:
         Example: obj-y += foo.o
        这行告诉kbuild在该目录下名为foo.o的目标文件(object),foo.o通过编译foo.c或者foo.S而得到。
        如果foo.o编译成一个模块,则使用obj-m变量,因此常见写法如下:
         Example: obj-$(CONFIG_FOO) += foo.o
         $(CONFIG_FOO)可以代表y(built-in对象)或m(module对象)。
          如果CONFIG_FOO不是y或m,那么这个文件不会被编译和链接。
    --- 3.2 内嵌对象 - obj-y
        makefile文件将为编译vmlinux的目标文件放在$(obj-y)列表中,这些列表依赖于内核配置。
          Kbuild编译所有的$(obj-y)文件,然后调用"$(LD) -r"合并这些文件到一个built-in.o文件中。built-in.o经过父makefile文件链接到vmlinux。$(obj-y)中的文件 顺序很重要。列表中文件允许重复,文件第一次出现将被链接到built-in.o,后续出现该文件将被忽略。
          链接顺序之所以重要是因为一些函数在内核引导时将按照他们出现的顺序被调用,如函数(module_init() / __initcall)。所以要牢记改变链接顺序意味着也要改变SCSI控制器的检测顺序和重数磁盘。
          例如: #drivers/isdn/i4l/Makefile
         # 内核ISDN子系统和设备驱动程序Makefile
         # 每个配置项是一个文件列表
         obj-$(CONFIG_ISDN)         += isdn.o
         obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
    --- 3.3 可加载模块 - obj-m
      $(obj-m)表示对象文件(object files)编译成可加载的内核模块。
      一个模块可以通过一个源文件或几个源文件编译而成。makefile只需简单地它们加到$(obj-m)。
          例如:#drivers/isdn/i4l/Makefile
            obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
        注意:在这个例子中$(CONFIG_ISDN_PPP_BSDCOMP)含义是'm'。
          如果内核模块通过几个源文件编译而成,使用以上同样的方法。
          Kbuild需要知道通过哪些文件编译模块,因此需要设置一个$(<module_name>-objs)变量。
        例如:#drivers/isdn/i4l/Makefile
         obj-$(CONFIG_ISDN) += isdn.o
         isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
         在这个例子中,模块名isdn.o. Kbuild首先编译$(isdn-objs)中的object文件,然后运行"$(LD) -r"将列表中文件生成isdn.o.
      Kbuild使用后缀-objs、-y识别对象文件。这种方法允许makefile使用CONFIG_符号值确定一个object文件是否是另外一个 object的组成部分。
         例如: #fs/ext2/Makefile 
            obj-$(CONFIG_EXT2_FS)     += ext2.o 
            ext2-y := balloc.o bitmap.o 
            ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
         在这个例子中,如果$(CONFIG_EXT2_FS_XATTR)表示'y',则ext2.o只有xattr.o组成部分。
         注意: 当然,当你将对象文件编译到内核时,以上语法同样有效。因此,如果CONFIG_EXT2_FS=y,Kbuild将先编译ext2.o文件,然后链接到 built-in.o。
    --- 3.4 导出符号目标
          在makefile文件中没有特别导出符号的标记。
    --- 3.5 库文件 - lib-y
          obj-*中的object文件用于模块或built-in.o编译。object文件也可能编译到库文件中--lib.a。
          所有罗列在lib-y中的object文件都将编译到该目录下的一个单一的库文件中。
          包含在0bj-y中的object文件如果也列举在lib-y中将不会包含到库文件中,因为他们不能被访问。但lib-m中的object文件将被编译进 lib.a库文件。 
          注意在相同的makefile中可以列举文件到buit-in内核中也可以作为库文件的一个组成部分。因此在同一个目录下既可以有built-in.o也 可以有lib.a文件。
          例如:#arch/i386/lib/Makefile
            lib-y   := checksum.o delay.o
         这样将基于checksum.o、delay.o创建一个lib.a文件。
          对于内核编译来说,lib.a文件被包含在libs-y中。将“6.3 目录表”。
          lib-y通常被限制使用在lib/和arch/*/lib目录中。
    --- 3.6 目录递归
         makefile文件负责编译当前目录下的目标文件,子目录中的文件由子目录中的makefile文件负责编译。编译系统将使用obj-y和obj-m自 动递归编译各个子目录中文件。
         如果ext2是一个子目录,fs目录下的makefile将使用以下赋值语句是编译系统编译ext2子目录。
         例如: #fs/Makefile
            obj-$(CONFIG_EXT2_FS) += ext2/
         如果CONFIG_EXT2_FS设置成'y(built-in)或'm'(modular),则对应的obj-变量也要设置,内核编译系统将进入 ext2目录编译文件。
          内核编译系统只使用这些信息来决定是否需要编译这个目录,子目录中makefile文件规定那些文件编译为模块那些是内核内嵌对象。
          当指定目录名时使用CONFIG_变量是一种良好的做法。如果CONFIG_选项不为'y'或'm',内核编译系统就会跳过这个目录。
    --- 3.7 编译标记
      EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS
      所有的EXTRA_变量只能使用在定义该变量后的makefile文件中。EXTRA_变量被makefile文件所有的执行命令语句所使用。
         $(EXTRA_CFLAGS)是使用$(CC)编译C文件的选项。
         例如: # drivers/sound/emu10k1/Makefile
               EXTRA_CFLAGS += -I$(obj)
               ifdef 
                DEBUG EXTRA_CFLAGS += -DEMU10K1_DEBUG 
                endif
         定义这个变量是必须的,因为顶层makefile定义了$(CFLAGS)变量并使用该变量编译整个代码树。
         $(EXTRA_AFLAGS)是每个目录编译汇编语言源文件的选项。
         例如: #arch/x86_64/kernel/Makefile
               EXTRA_AFLAGS := -traditional
         $(EXTRA_LDFLAGS)和$(EXTRA_ARFLAGS)用于每个目录的$(LD)和$(AR)选项。
         例如: #arch/m68k/fpsp040/Makefile
               EXTRA_LDFLAGS := -x
      CFLAGS_$@, AFLAGS_$@
         CFLAGS_$@和AFLAGS_$@只使用到当前makefile文件的命令中。
         $(CFLAGS_$@)定义了使用$(CC)的每个文件的选项。$@部分代表该文件。
         例如: # drivers/scsi/Makefile
               CFLAGS_aha152x.o =   -DAHA152X_STAT -DAUTOCONF
               CFLAGS_gdth.o   = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ \
      -DGDTH_STATISTICS CFLAGS_seagate.o =   -DARBITRATE -DPARITY -DSEAGATE_USE_ASM
         这三行定义了aha152x.o、gdth.o和seagate.o文件的编译选项。
         $(AFLAGS_$@)使用在汇编语言代码文件中,具有同上相同的含义。
         例如: # arch/arm/kernel/Makefile
               AFLAGS_head-armv.o := -DTEXTADDR=$(TEXTADDR) -traditional
               AFLAGS_head-armo.o := -DTEXTADDR=$(TEXTADDR) -traditional
    --- 3.9 依赖关系
         内核编译记录如下依赖关系:
          1) 所有的前提文件(both *.c and *.h) 
          2) CONFIG_ 选项影响到的所有文件
          3) 编译目标文件使用的命令行
         因此,假如改变$(CC)的一个选项,所有相关的文件都要重新编译。
    --- 3.10 特殊规则
          特殊规则使用在内核编译需要规则定义而没有相应定义的时候。典型的例子如编译时头文件的产生规则。其他例子有体系makefile编译引导映像的特殊规 则。特殊规则写法同普通的Make规则。
         Kbuild(应该是编译程序)在makefile所在的目录不能被执行,因此所有的特殊规则需要提供前提文件和目标文件的相对路径。
         定义特殊规则时将使用到两个变量:
      $(src): $(src)是对于makefile文件目录的相对路径,当使用代码树中的文件时使用该变量$(src)。
      $(obj): $(obj)是目标文件目录的相对路径。生成文件使用$(obj)变量。
         例如: #drivers/scsi/Makefile
         $(obj)/53c8xx_d.h: $(src)/53c7,8xx.scr $(src)/script_asm.pl 
            $(CPP) -DCHIP=810 - < $< | ... $(src)/script_asm.pl
         这就是使用普通语法的特殊编译规则。
         目标文件依赖于两个前提文件。目标文件的前缀是$(obj), 前提文件的前缀是$(src)(因为它们不是生成文件)。

    === 4 辅助程序


        内核编译系统支持在编译(compliation)阶段编译主机可执行程序。为了使用主机程序需要两个步骤:第一个步骤使用hostprogs-y变量告 诉内核编译系统有主机程序可用。第二步给主机程序添加潜在的依赖关系。有两种方法,在规则中增加依赖关系或使用$(always)变量。具体描述如下。
    --- 4.1 简单辅助程序
         在一些情况下需要在主机上编译和运行主机程序。下面这行告诉kbuild在主机上建立bin2hex程序。
         例如: hostprogs-y := bin2hex
         Kbuild假定使用makefile相同目录下的单一C代码文件bin2hex.c编译bin2hex。
    --- 4.2 组合辅助程序
         主机程序也可以由多个object文件组成。定义组合辅助程序的语法同内核对象的定义方法。
         $(<executeable>-objs)包含了所有的用于链接最终可执行程序的对象。
         例如: #scripts/lxdialog/Makefile
            hostprogs-y   := lxdialog 
            lxdialog-objs := checklist.o lxdialog.o
         扩展名.o文件都编译自对应的.c文件。在上面的例子中checklist.c编译成checklist.o,lxdialog.c编译为 lxdialog.o。最后两个.o文件链接成可执行文件lxdialog。
         注意:语法<executable>-y不能用于定义主机程序。
    --- 4.3 定义共享库 
         扩展名为.so的对象是共享库文件,并且是位置无关的object文件。内核编译系统提供共享库使用支持,但使用方法有限制。在下面例子中 libkconfig.so库文件被链接到可执行文件conf中。
         例如: #scripts/kconfig/Makefile
               hostprogs-y   := conf
               conf-objs     := conf.o libkconfig.so
               libkconfig-objs := expr.o type.o
         共享库文件需要对应的-objs定义, 在上面例子中库libkconfig由两个对象组成:expr.o和type.o。expr.o和type.o将被编译为位置无关代码并被链接如 libkconfig.so。共享库不支持C++语言。
    --- 4.4 C++语言使用方法
         内核编译系统提供了对C++主机程序的支持以用于内核配置,但不主张其它方面使用这种方法。
         例如: #scripts/kconfig/Makefile
               hostprogs-y   := qconf
               qconf-cxxobjs := qconf.o
         在上面例子中可执行文件由C++文件qconf.cc组成 - 通过$(qconf-cxxobjs)标识。
         如果qconf由.c和.cc文件混合组成,附加行表示这种情况。
         例如: #scripts/kconfig/Makefile
               hostprogs-y   := qconf
               qconf-cxxobjs := qconf.o
               qconf-objs   := check.o
    --- 4.5 辅助程序编译控制选项
         当编译主机程序时仍然可以使用$(HOSTCFLAGS)设置编译选项传递给$(HOSTCC)。这些选项将影响所有使用变量 HOST_EXTRACFLAG的makefile创建的主机程序。
         例如: #scripts/lxdialog/Makefile
               HOST_EXTRACFLAGS += -I/usr/include/ncurses
         为单个文件设置选项使用下面方式:
         例如: #arch/ppc64/boot/Makefile
         HOSTCFLAGS_piggyback.o := -DKERNELBASE=$(KERNELBASE)
         也可以使用附加链接选项:
         例如: #scripts/kconfig/Makefile
               HOSTLOADLIBES_qconf := -L$(QTDIR)/lib
         当链接qconf时将使用外部选项"-L$(QTDIR)/lib"。
    --- 4.6 何时建立辅助程序
         只有当需要时内核编译系统才会编译主机程序。有两种方式:
         (1) 在特殊规则中作为隐式的前提需求
         例如: #drivers/pci/Makefile
         hostprogs-y := gen-devlist
         $(obj)/devlist.h: $(src)/pci.ids $(obj)/gen-devlist 
                  ( cd $(obj); ./gen-devlist ) < $<
         编译目标文件$(obj)/devlist.h需要先建立$(obj)/gen-devlist。注意在特殊规则中使用主机程序必须加前缀$(obj)。
         (2) 使用$(always)
         当没有合适的特殊规则可以使用,并且在进入makefile文件时就要建立主机程序,可以使用变量$(always)。
         例如: #scripts/lxdialog/Makefile
               hostprogs-y   := lxdialog
               always     := $(hostprogs-y)
         这样就告诉内核编译系统即使没有任何规则使用lxdialog也要编译它。
    --- 4.7 使用hostprogs-$(CONFIG_FOO)
         在Kbuild文件中典型模式如下:
         例如: #scripts/Makefile
               hostprogs-$(CONFIG_KALLSYMS) += kallsyms
         对Kbuild来说'y'用于内嵌对象'm'用于模块。
         因此如果config符号是'm',编译系统也将创建该程序。换句话说内核编译系统等同看待hostprogs-m和hostprogs-y。但如果不涉 及到CONFIG符号仅建议使用hostprogs-y。

    === 5 编译清除机制


      "make clean"命令删除在编译内核生成的大部分文件,例如主机程序,列举在 $(hostprogs-y)、$(hostprogs-m)、$(always)、$(extra-y)和$(targets)中目标文件都将被删除。 代码目录数中的"*.[oas]"、"*.ko"文件和一些由编译系统产生的附加文件也将被删除。
      附加文件可以使用$(clean-files)进行定义。
         例如: #drivers/pci/Makefile
               clean-files := devlist.h classlist.h
      当执行"make clean"命令时, "devlist.h classlist.h"两个文件将被删除。内核编译系统默认这些文件与makefile具有相同的相对路径,否则需要设置以'/'开头的绝对路径。
      删除整个目录使用以下方式:
      例如: #scripts/package/Makefile
         clean-dirs := $(objtree)/debian/
      这样就将删除包括子目录在内的整个debian目录。如果不使用以'/'开头的绝对路径内核编译系统见默认使用相对路径。
      通常内核编译系统根据"obj-* := dir/"进入子目录,但是在体系makefile中需要显式使用如下方式:
         例如: #arch/i386/boot/Makefile
               subdir- := compressed/
      上面赋值语句指示编译系统执行"make clean"命令时进入compressed/目录。
      在编译最终的引导映像文件的makefile中有一个可选的目标对象名称是archclean。
         例如: #arch/i386/Makefile
               archclean: 
                  $(Q)$(MAKE) $(clean)=arch/i386/boot
      当执行"make clean"时编译器进入arch/i386/boot并象通常一样工作。arch/i386/boot中的makefile文件可以使用subdir- 标识进入更下层的目录。
      注意1: arch/$(ARCH)/Makefile不能使用"subdir-",因为它被包含在顶层makefile文件中,在这个位置编译机制是不起作用的。
      注意2: 所有列举在core-y、libs-y、drivers-y和net-y中的目录将被"make clean"命令清除。

    === 6 体系Makefile文件


      在开始进入各个目录编译之前,顶层makefile文件设置编译环境和做些准备工作。顶层makefile文件包含通用部分,arch/$(ARCH) /Makefile包含该体系架构所需的设置。因此arch/$(ARCH)/Makefile会设置一些变量和少量的目标。
      当编译时将按照以下大概步骤执行: 
    1) 配置内核 => 产生 .config文件 
    2) 保存内核版本到include/linux/version.h文件中 
    3) 符号链接include/asm to include/asm-$(ARCH) 
    4) 更新所有目标对象的其它前提文件
      - 附加前提文件定义在arch/$(ARCH)/Makefile文件中 
    5) 递归进入init-* core* drivers-* net-* libs-*中的所有子目录和编译所有的目标对象
      - 上面变量值都引用到arch/$(ARCH)/Makefile文件。 
    6) 链接所有的object文件生成vmlinux文件,vmlinux文件放在代码树根目录下。
      最开始链接的几个object文件列举在arch/$(ARCH)/Makefile文件的head-y变量中。
    7) 最后体系makefile文件定义编译后期处理规则和建立最终的引导映像bootimage。
      - 包括创建引导记录
      - 准备initrd映像和相关处理
    --- 6.1 变量设置
      LDFLAGS      $(LD)一般选项
         选项使用于链接器的所有调用中。通常定义emulation就可以了。
         例如: #arch/s390/Makefile
               LDFLAGS   := -m elf_s390 
          注意: EXTRA_LDFLAGS和LDFLAGS_$@可以进一步订制使用选项,将第7章。
      LDFLAGS_MODULE       $(LD)链接模块的选项
         LDFLAGS_MODULE通常设置$(LD)链接模块的.ko选项。
         默认为"-r"即可重定位输出文件。
      LDFLAGS_vmlinux   $(LD)链接vmlinux选项
         LDFLAGS_vmlinux定义链接最终vmlinux时链接器的选项。
         LDFLAGS_vmlinux支持使用LDFLAGS_$@。
         例如: #arch/i386/Makefile
               LDFLAGS_vmlinux := -e stext
      OBJCOPYFLAGS      objcopy选项
         当使用$(call if_changed,objcopy)转化a .o文件时,OBJCOPYFLAGS中的选项将被使用。
         $(call if_changed,objcopy)经常被用作为vmlinux产生原始的二进制文件。
         例如: #arch/s390/Makefile
               OBJCOPYFLAGS := -O binary
              #arch/s390/boot/Makefile
               $(obj)/image: vmlinux FORCE $(call if_changed,objcopy)
         在上面例子中$(obj)/image是vmlinux的二进制版本文件。$(call if_changed,xxx)
    的使用方法见后。
      AFLAGS   $(AS)汇编选项
         默认值见顶层Makefile文件
         针对每个体系需要另外添加和修改它。
         例如: #arch/sparc64/Makefile
               AFLAGS += -m64 -mcpu=ultrasparc
      CFLAGS      $(CC)编译器选项
         默认值见顶层Makefile文件
         针对每个体系需要另外添加和修改它。
         通常CFLAGS变量值取决于内核配置。
         例如: #arch/i386/Makefile
               cflags-$(CONFIG_M386) += -march=i386
               CFLAGS += $(cflags-y)
         许多体系Makefiles文件动态启动市场目标机器上的C编译器检测支持的选项:
               #arch/i386/Makefile
               ...
               cflags-$(CONFIG_MPENTIUMII)   += $(call cc-option,\
                     -march=pentium2,-march=i686) ...
               # Disable unit-at-a-time mode ...
               CFLAGS += $(call cc-option,-fno-unit-at-a-time)
               ...
         第一个例子当config选项是'y'时将被选中。
      CFLAGS_KERNEL      $(CC)编译built-in对象的选项
         $(CFLAGS_KERNEL)包含外部C编译器选项编译本地内核代码。
      CFLAGS_MODULE      $(CC)编译模块选项
         $(CFLAGS_MODULE)包含外部C编译器选项编译可加载内核代码。
    --- 6.2 增加预设置项
         prepare: 这个规则用于列举开始进入子目录编译前需要的前提文件。通常是些包含汇编常量的头文件。
         例如:
         #arch/s390/Makefile
         prepare: include/asm-$(ARCH)/offsets.h
         在这个例子中include/asm-$(ARCH)/offsets.h将在进入子目录前编译。
          详见XXX-TODO文件描述了kbuild如何产生offset头文件。
    --- 6.3 目录表
         体系makefile文件和顶层makefile文件共同定义了如何建立vmlinux文件的变量。注意没有体系相关的模块对象定义部分:所有的模块对象 都是体系无关的。
      head-y, init-y, core-y, libs-y, drivers-y, net-y
         $(head-y) 列举首先链接到vmlinux的对象文件。
         $(libs-y) 列举了能够找到lib.a文件的目录。
         其余的变量列举了能够找到内嵌对象文件的目录。
         $(init-y) 列举的对象位于$(head-y)对象之后。
         然后是如下位置秩序:
         $(core-y), $(libs-y), $(drivers-y) 和 $(net-y)。
         顶层makefile定义了所有同用目录,arch/$(ARCH)/Makefile文件只需增加体系相关的目录。
         例如: #arch/sparc64/Makefile
               core-y += arch/sparc64/kernel/
               libs-y += arch/sparc64/prom/ arch/sparc64/lib/
               drivers-$(CONFIG_OPROFILE) += arch/sparc64/oprofile/
    --- 6.4 引导映像
         体系makefile文件定义了编译vmlinux文件的目标对象,将它们压缩和封装成引导代码,并复制到合适的位置。这包括各种安装命令。如何定义实际 的目标对象无法为所有的体系结构提供标准化的方法。
         附加处理过程常位于arch/$(ARCH)/下的boot/目录。
         内核编译系统无法在boot/目录下提供一种便捷的方法创建目标系统文件。因此arch/$(ARCH)/Makefile要调用make命令在boot /目录下建立目标系统文件。建议使用的方法是在arch/$(ARCH)/Makefile中设置调用,并且使用完整路径引用arch/$(ARCH) /boot/Makefile。
         例如: #arch/i386/Makefile
               boot := arch/i386/boot
               bzImage: vmlinux 
                  $(Q)$(MAKE) $(build)=$(boot) $(boot)/$@
         建议使用"$(Q)$(MAKE) $(build)=<dir>"方式在子目录中调用make命令。
         没有定义体系目标系统文件的规则,但执行"make help"命令要列出所有目标系统文件,因此必须定义$(archhelp)变量。
         例如: #arch/i386/Makefile
         define 
            archhelp echo '* bzImage     - Image (arch/$(ARCH)/boot/bzImage)' 
          endef
         当执行不带参数的make命令时,将首先编译第一个目标对象。在顶层makefile中第一个目标对象是all:。
         一个体系结构需要定义一个默认的可引导映像。
         "make help"命令的默认目标是以*开头的对象。
         增加新的前提文件给all目标可以设置不同于vmlinux的默认目标对象。
         例如: #arch/i386/Makefile
               all: bzImage 
         当执行不带参数的"make"命令时,bzImage文件将被编译。
    --- 6.5 编译非内核目标
      extra-y
         extra-y定义了在当前目录下创建没有在obj-*定义的附加的目标文件。
         在extra-y中列举目标是处于两个目的: 
          1) 是内核编译系统在命令行中检查变动情况
            - 当使用$(call if_changed,xxx)时 
          2) 内核编译系统知道执行"make clean"命令时删除哪些文件
         例如: #arch/i386/kernel/Makefile
               extra-y := head.o init_task.o
         上面例子extra-y中的对象文件将被编译但不会练接到built-in.o中。
    --- 6.6 编译引导映像命令
         Kbuild提供了一些编译引导映像有用的宏。
      if_changed
         if_changed是后面命令使用的基础。
         用法: 
            target: source(s) 
                FORCE $(call if_changed,ld/objcopy/gzip)
         当这条规则被使用时它将检查哪些文件需要更新,或命令行被改变。后面这种情况将迫使重新编译编译选项被改变的执行文件。使用if_changed的目标对 象必须列举在$(targets)中,否则命令行检查将失败,目标一直会编译。
         赋值给$(targets)的对象没有$(obj)/前缀。
         if_changed也可以和定制命令配合使用,见6.7"kbuild定制命令"。
         注意: 一个常见错误是忘记了FORCE前导词。
      ld 
          链接目标。常使用LDFLAGS_$@作为ld的选项。
      objcopy 
          复制二进制文件。常用于arch/$(ARCH)/Makefile中和使用OBJCOPYFLAGS作为选项。
         也可以用OBJCOPYFLAGS_$@设置附加选项。
      gzip 
          压缩目标文件。使用最大压缩算法压缩目标文件。
         例如: #arch/i386/boot/Makefile
         LDFLAGS_bootsect := -Ttext 0x0 -s --oformat binary
         LDFLAGS_setup   := -Ttext 0x0 -s --oformat binary -e begtext

         targets += setup setup.o bootsect bootsect.o
         $(obj)/setup $(obj)/bootsect: %: %.o FORCE 
                $(call if_changed,ld)
          在上面例子中有两个可能的目标对象,分别需要不同的链接选项。使用LDFLAGS_$@语法为每个目标对象设置不同的链接选项。
         $(targets)包含所有的目标对象,因此内核编译系统知道所有的目标对象并且将:
          1) 检查命令行的改变情况 
          2) 执行make clean命令时删除目标对象
         ": %: %.o"是简写方法,减写setup.o和bootsect.o文件。
         注意: 常犯错误是忘记"target :="语句,导致没有明显的原因目标文件被重新编译。
    --- 6.7 定制编译命令
         当执行带KBUILD_VERBOSE=0参数的编译命令时命令的简短信息会被显示。要让定制命令具有这种功能需要设置两个变量:
         quiet_cmd_<command> - 将被显示的内容 
          cmd_<command>      - 被执行的命令
         例如: #
               quiet_cmd_image = BUILD   $@ 
                cmd_image = $(obj)/tools/build $(BUILDFLAGS) \ 
                        $(obj)/vmlinux.bin > $@
               targets += bzImage
               $(obj)/bzImage: $(obj)/vmlinux.bin $(obj)/tools/build FORCE 
                    $(call if_changed,image)
                    @echo 'Kernel: $@ is ready'
         执行"make KBUILD_VERBOSE=0"命令编译$(obj)/bzImage目标时将显示:
         BUILD   arch/i386/boot/bzImage
    --- 6.8 预处理连接脚本
         当编译vmlinux映像时将使用arch/$(ARCH)/kernel/vmlinux.lds链接脚本。
         相同目录下的vmlinux.lds.S文件是这个脚本的预处理的变体。内核编译系统知晓.lds文件并使用规则*lds.S -> *lds。
         例如: #arch/i386/kernel/Makefile
               always := vmlinux.lds
               #Makefile
               export CPPFLAGS_vmlinux.lds += -P -C -U$(ARCH)
         $(always)赋值语句告诉编译系统编译目标是vmlinux.lds。$(CPPFLAGS_vmlinux.lds)赋值语句告诉编译系统编译 vmlinux.lds目标的编译选项。
         编译*.lds时将使用到下面这些变量:
         CPPFLAGS      : 定义在顶层Makefile
         EXTRA_CPPFLAGS      : 可以设置在编译的makefile文件中
         CPPFLAGS_$(@F) : 目标编译选项。注意要使用文件全名。
    --- 6.9 $(CC)支持功能
         内核可能会用不同版本的$(CC)进行编译,每个版本有不同的性能和选项,内核编译系统提供基本的支持用于验证$(CC)选项。$(CC)通常是gcc编 译器,但其它编译器也是可以。
      cc-option cc-option 用于检测$(CC)是否支持给定的选项,如果不支持就使用第二个可选项。
         例如: #arch/i386/Makefile
               cflags-y += $(call cc-option,-march=pentium-mmx,-march=i586)
         在上面例子中如果$(CC)支持-march=pentium-mmx则cflags-y等于该值,否则等于-march-i586。如果没有第二个可选 项且第一项不支持则cflags-y没有被赋值。
      cc-option-yn cc-option-yn用于检测gcc是否支持给定的选项,支持返回'y'否则'n'。
         例如: #arch/ppc/Makefile
               biarch := $(call cc-option-yn, -m32)
               aflags-$(biarch) += -a32
               cflags-$(biarch) += -m32
         在上面例子中如果$(CC)支持-m32选项则$(biarch)设置为y。当$(biarch)等于y时,变量$(aflags-y) 和$(cflags-y)将分别等于-a32和-m32。
      cc-option-align gcc版本>= 3.0用于定义functions、loops等边界对齐选项。
         gcc < 3.00 
            cc-option-align = -malign 
          gcc >= 3.00 
            cc-option-align = -falign
         例如: 
            CFLAGS += $(cc-option-align)-functions=4
         在上面例子中对于gcc >= 3.00来说-falign-functions=4,gcc < 3.00版本使用-malign-functions=4。
      cc-version cc-version返回$(CC)编译器数字版本号。
         版本格式是<major><minor>,均为两位数字。例如gcc 3.41将返回0341。
         当一个特定$(CC)版本在某个方面有缺陷时cc-version是很有用的。例如-mregparm=3在一些gcc版本会失败尽管gcc接受这个选 项。
         例如: #arch/i386/Makefile
               GCC_VERSION := $(call cc-version)
               cflags-y += $(shell \
               if [ $(GCC_VERSION) -ge 0300 ] ; then echo "-mregparm=3"; fi ;)
         在上面例子中-mregparm=3只使用在版本大于等于3.0的gcc中。
    === 7 Kbuild变量


      顶层Makefile文件导出下面这些变量:
      VERSION, PATCHLEVEL, SUBLEVEL, EXTRAVERSION
         这几个变量定义了当前内核版本号。很少体系体系Makefiles文件直接使用他们,常用$(KERNELRELEASE)代替。
         $(VERSION)、$(PATCHLEVEL)和$(SUBLEVEL)定义了三个基本部分版本号,例如"2", "4",和"0"。这三个变量一直使用数值表示。
         $(EXTRAVERSION)定义了更细的补钉号,通常是短横跟一些非数值字符串,例如"-pre4"。
      KERNELRELEASE
         $(KERNELRELEASE)是一个单一字符如"2.4.0-pre4",适合用于构造安装目录和显示版本字符串。一些体系文件使用它用于以上目的。
      ARCH
         这个变量定义了目标系统体系结构,例如"i386"、“arm"、"sparc". 一些内核编译文件测试$(ARCH)用于确定编译哪个文件。默认情况下顶层Makefile文件设置$(ARCH)为主机相同的系统体系。当交叉编译编译 时,用户可以使用命令行改变$(ARCH)值:
            make ARCH=m68k ...
      INSTALL_PATH
         这个变量定义了体系Makefiles文件安装内核映项和System.map文件的路径。
      INSTALL_MOD_PATH, MODLIB
         $(INSTALL_MOD_PATH)定义了模块安装变量$(MODLIB)的前缀。这个变量通常不在Makefile文件中定义,如果需要可以由用户 添加。
         $(MODLIB)定义了模块安装目录。
         顶层Makefile定义$(MODLIB)为$(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)。用 户可以使用命令行修改这个值。
    === 8 Makefile语言


      内核Makefiles设计目标用于运行GNU Make程序。Makefiles仅使用GNU Make提到的特性,但使用了较多的GNU扩展部分。
      GNU Make程序支持基本的列表处理功能。内核Makefiles文件结合"if"语句使用了简单的列表建立和维护功能。
      GNU Make程序有两种赋值操作符:":="和"="。 ":="执行时立即计算右值并赋值给左值。"="类似公式定义,当每次使用左值要被使用时计算右值并赋给它。
      一些情况中使用"="合适,而一些情况中使用":="才是正确选择。

    展开全文
  • 一、多文件编程 二、Makefile 简单实例 2.1 简介 2.2 格式 2.3 实践 三、使用Makefile制作动态库、静态库 3.1 Linux下的库(动态库、静态库)简介 3.1.1 什么是库 3.1.2库的种类 3.2.3 库存在的意义 3.2.4...

    目录

    一、多文件编程

    二、Makefile 简单实例

    2.1 简介

    2.2 格式

    2.3 实践

    三、使用Makefile制作动态库、静态库

    3.1 Linux下的库(动态库、静态库)简介

    3.1.1 什么是库

    3.1.2 库的种类

    3.1.3 库存在的意义

    3.1.4 在linux下如何制作库文件

    3.1.5 查看可执行程序依赖的库(ldd命令)

    3.1.6 如何让系找到库

    3.2 Makefile制作静态库

    3.3 Makefile 制作动态库

    3.4 Makefile的改进(以动态库为例)

    3.4.1 基本替换和赋值

    3.4.2 对3.2中静态库的Makefile改进


    本文用简单的实践方式,介绍下Makefile多文件编程,以及Makefile制作Linux动态库、静态库的方法。

     

    一、多文件编程

    为了代码工程的具有较好的可维护性、可读性以及低耦合的要求。一般情况下,工程都会以多文件方式存在,将不同功能的方法函数写在不同的源文件中,供其它文件调用。

    写一个简单的例子:

    比如现在,我们要计算两个数的加法,和两个数的乘法。

    那么,“加法”的功能函数实现单独写在add.c源文件中、“乘法”的功能函数实现单独写在mul.c源文件中。另外,源文件中所有的函数声明,应写在源文件的同名头文件中,在这里,add.c中有一个函数add(),其函数声明写在add.h中;mul.c中有一个函数mul(),其函数声明写在mul.h中,供其它源文件调用。

    然后,写一个最简单的main函数,加入两者的头文件,实现对两个功能函数的调用。

    基于此,我们现在得到五个文件:add.h、add.c、mul.h、mul.c、main.c。大概,就是这么个关系吧:

    下面,使用mkdir命令新建一个目录,然后编写一下这五个文件,五个文件的代码,很简单:

    //add.h文件
    int add(const int a, const int b);
    
    
    //add.c文件
    #include<stdio.h>
    #include"add.h"
    
    int add(const int a, const int b)
    {
    	return a + b;
    }
    
    //mul.h文件
    int mul(const int a, const int b);
    
    //mul.c文件
    #include<stdio.h>
    #include"mul.h"
    
    int mul(const int a, const int b)
    {
    	return a * b;
    }
    
    
    //main.c文件
    #include<stdio.h>
    #include"mul.h"
    #include"add.h"
    
    int main(int argc, char* argv[])
    {
    	int a = 10;
    	int b = 20;
    
    	printf("add:%d\tmul:%d\n", add(a,b), mul(a,b));
    }

    完成后,目录下,应该是这样:

    我们通过以下指令,先编译生成各自的目标文件(add.o, mul.o, main.o)。然后进行链接,生成可执行文件main,并执行。

    运行正确。(这里gcc编译的知识,如果不会要补充下呦,gcc -E -s -c -o参数分别代表什么意思~)

    二、Makefile 简单实例

    2.1 简介

    Makefile主要用来在一个工程中,管理多文件的编译。

    如第一张最后的编译过程,我们写了三个gcc -c命令,分别编译生成了三个.o目标文件,然后又通过gcc,将他们链接,最后生成了可执行文件。如果你改了add.c的函数实现,那么,以上编译、链接步骤就要重新手动执行一次。如果是大型的工程,改其中一个源文件,可不就要根据编译逻辑,敲良久~

    Makefile就是你写一个编译规则,这个编译规则用来规定工程中所有源文件的编译顺序,那么以后你改动任何一个源文件,规则是不变的,只需要执行make一下,他就会按照预定的规则将文件重新编译。即便是添加删除文件,只需要在对应的规则上稍作修改即可,一劳永逸,节省时间。

    https://www.jianshu.com/p/29eb94f029c7这里介绍了有关Makefile的基本原理,可以看一下。

    2.2 格式

    Makefile的格式很简单,也很好理解:

    ******

    目标: 依赖

    (tab)规则

    ******

    “目标” 是需要生成的目标文件;“依赖”是生成该目标所需的一些文件;“规则”是由依赖文件生成目标文件的手段。用一句话概括,其实就是:把“依赖”根据“规则”编译成“目标”

    要注意:每条“规则”必须以 tab 开头,不能使用空格等。

    2.3 实践

    在第一章的代码目录下,编写一个Makefile,管理工程:

    main: main.o add.o mul.o
    	gcc main.o add.o mul.o -o main
    main.o: main.c
    	gcc main.c -c
    add.o: add.c
    	gcc add.c -c
    mul.o: mul.c
    	gcc mul.c -c
    
    #.PHONY:clear表示clear是一个伪目标.
    #如果不加.PHONY,当前目录下由clear的文件就会执行不了,且提示该文件是最新的.
    #rm前边多减号表示:不管出现什么问题,都要做后边多事情
    .PHONY:clear
    clear:
    	rm *.o

    保存,当前目录含有六个文件:

    在当前目录下输入make,执行Makefile编译规则,可见,生成了main执行文件,并且执行后,结果正确:

    另外,在我们的Makefile中,有一个clear目标,并且声明了它为.PHONY伪目标。且他没有依赖文件,只有规则,规则是删除所有.o文件。如果我们想这样做的话,直接在当前目录下,输入make+目标名即可,即“make clear”:

    三、使用Makefile制作动态库、静态库

    3.1 Linux下的库(动态库、静态库)简介

    本小节对Linux下的库做了基本的概要总结,摘录博文地址:【C语言】Linux下动态库和静态库详解

    3.1.1 什么是库

    在windows平台和linux平台下都大量存在着库。

    本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

    由于windows和linux的平台不同(主要是编译器、汇编器和连接器的不同),因此二者库的二进制是不兼容的。

     

    3.1.2 库的种类

    Linux下的库有两种:静态库和动态库(共享库)。

     

    静态库的后缀是.a;

    动态库的后缀是.so

     

    静态库的名字一般为libxxxx.a,其中xxxx是该lib的名称

    动态库的名字一般为libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号, minor是副版本号。当然,主、副版本号也可以不写。

     

    二者的不同点在于:代码被载入的时刻不同。

    静态库的代码在编译过程中已经被载入可执行程序,因此体积较大

    动态库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小

     

    3.1.3 库存在的意义

    库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。

    现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。

    共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。

     

    3.1.4 在linux下如何制作库文件

    静态库制作分两步:

    Step 1.由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表

    Step 2.ar命令将很多.o转换成.a,成为静态库

    动态库的制作由gcc加特定参数编译产生。

     

    3.1.5 查看可执行程序依赖的库(ldd命令)

    例如# ldd /bin/lnlibc.so.6

    => /lib/libc.so.6 (0×40021000)/lib/ld-linux.so.2

    => /lib/ld- linux.so.2 (0×40000000)

    可以看到ln命令依赖于libc库和ld-linux库

     

    3.1.6 如何让系找到库

    如果安装在/lib或者/usr/lib下,那么ld默认能够找到,无需其他操作。

    如果安装在其他目录,需要将其添加到/etc/ld.so.cache文件中,步骤如下:

    1.编辑/etc/ld.so.conf文件,加入库文件所在目录的路径

    2.运行ldconfig,该命令会重建/etc/ld.so.cache文件

     

    3.2 Makefile制作静态库

    我们在第二章中,用两个源文件add.c、mul.c分别实现了两个数的相加和相乘操作,他们有一个特点,都是数学运算,我们可以把这两个源文件,添加到一个库中,成为一个math库,这个库,就可以用在任何需要计算两数相加、两数相乘的地方。

    本节先介绍math静态库(libmath.a)的制作和使用方法。

    重写上一章的Makefile:

    a.out: main.c libmath.a
    	gcc main.c -L . -l math -o a.out 
    #上边这一行: -L参数:指定目录; -l参数:要链接的库,"-l math"可以连写成"-lmath";
    #这里注意,math库不要写"labmath.a"
    
    libmath.a: add.o mul.o
    	ar -r libmath.a add.o mul.o
    #上边这一行:将两个.o文件加入math库中
    
    add.o: add.c
    	gcc add.c -c
    mul.o: mul.c
    	gcc mul.c -c
    
    #.PHONY:clear表示clear是一个伪目标.
    #如果不加.PHONY,当前目录下由clear的文件就会执行不了,且提示该文件是最新的.
    #rm前边多减号表示:不管出现什么问题,都要做后边多事情
    .PHONY:clear
    clear:
    	rm *.o a.out libmath.a

    注释应该写的很清楚了,主要过程大概梳理为:编译add.c和mul.c生成目标文件add.o和mul.o,将add.o和mul.o添加到libmath.a库中。最后,让main.c在编译过程中,链接libmath.a静态库,生成最后可执行文件a.out。

    执行下make

     

    3.3 Makefile 制作动态库

    本节介绍math动态库(libmath.so)的制作和使用方法。

    重写Makefile:

    a.out: main.c libmath.so
    	gcc main.c -L . -l math -o a.out 
    #上边这一行: -L参数:指定目录; -l参数:要链接的库,"-l math"可以连写成"-lmath";
    #这里注意,math库不要写"labmath.so"
    
    libmath.so: add.o mul.o
    	gcc -shared add.o mul.o -o libmath.so
    #	ar -r libmath.a add.o mul.o  对比一下静态库的方法.
    #	-shared:表示输出结果是共享库类型
    
    add.o: add.c
    	gcc -c -fpic add.c
    mul.o: mul.c
    	gcc -c -fpic mul.c
    #上边这几行,添加了参数"-fpic",表示将源文件编译成带有PIC标志的目标文件.
    
    
    .PHONY:clear
    clear:
    	rm *.o a.out libmath.so
    
    
    #注意,动态库执行时,要把生成多动态库文件移动到/lib目录下!

    解释都在上述的Makefile文件中了,不再累述了~

    make一下后不能马上执行main可执行文件,因为这时候还没有把动态库放到系统能找到的地方。

    我们需要把make生成的动态库libmath.so拷贝到/lib目录,在执行a.out的时候,系统会去/lib中查找相应的动态库,进行链接。其它方法,请参考本文3.2.6小结。

    操作和执行结果:

     

    3.4 Makefile的改进(以动态库为例)

    前面我们介绍了用Makefile编译多文件、制作动态库和静态库的方法。

    其实,这些Makefile的使用是很初级化的,为了让Makefile更好用,学问还是很深的,这里给出一些基本的概念和改进思想。这样看完,应该算是直到Makefile是个什么东西了,以后做项目需要的时候,可以再逐步深入即可。

    3.4.1 基本替换和赋值

    (1) 变量:
        OJBC:一般用来代表.o文件
        CC:代表编译

    (2) 自动变量:
        $@:表示目标文件
        $%:如果目标是函数库文件,那么$%表示库文件的成员.  比如静态库f.a,$%表示他的成员m.o sort.o.
        $+:所有的依赖文件,不去重复
        $^:所有的依赖文件,去重复
        $<:第一个依赖文件
        $?:所有的依赖文件,空格隔开,这些依赖文件比目标还要新(修改时间比目标晚)


    (3) 赋值:
        :=  简单赋值,常规理解的赋值,只对当前语句的变量有效
            (中间被重新赋值了,前边多不会改变,后边的会改变为新值)
        =    递归赋值,赋值语句可能影响多个变量,所有目标变量相关的其它变量都受影响
            (最后被赋值是什么,全文引用该变量的地方都变成什么)
        ?=    条件赋值,如果变量之前未定义,使用符号中的值定义变量,若已定义(之前被赋值过),该赋值语句无效
        +=    追加复制,原变量用空格隔开的方式追加一个新值

     

    3.4.2 对3.2中静态库的Makefile改进

    CC := gcc
    
    OJBC = add.o 
    OJBC += mul.o
    
    TARGET := a.out
    
    MAIN_C = main.c
    
    #以下是动态库相关的变量  这里使用递归赋值,math头文件在任何一处改变,全文件变量随之改变.
    #libmath.a
    A_LIB_NAME = math
    A_LIB = lib$(A_LIB_NAME).a   
    
    
    
    $(TARGET): $(MAIN_C) $(A_LIB)
    	$(CC) $< -L . -l $(A_LIB_NAME) -o $(TARGET)
    
    $(A_LIB): $(OJBC)
    	ar -r $@ $^
    
    %.o:%.c
    	$(CC) $< -c
    
    .PHONY:clear
    clear:
    	rm $(OJBC) $(TARGET) $(A_LIB)
    
    
    
    #原来的写法:
    #a.out: main.c libmath.a
    #	gcc main.c -L . -l math -o a.out 
    #
    #libmath.a: add.o mul.o
    #	ar -r libmath.a add.o mul.o
    #
    #add.o: add.c
    #	gcc add.c -c
    #mul.o: mul.c
    #	gcc mul.c -c
    #
    #.PHONY:clear
    #clear:
    #	rm *.o a.out libmath.a

    为什么要用定义CC变量($(CC))呢?为了交叉编译好用,比如,如果想在Ubuntu上交叉编译一个arm架构的应用程序,使之能在arm上运行,那么就需要将makefile中所有的gcc改成 arm-none-linux-gnueabi-gcc交叉编译工具链,为了到时候方便改动,可以在文件启示位置,将编译工具设为变量。其它很多变量也是这个问题,为了方便改名、替换。

    为什么要使用%.o、%.c代替文件?很明显,很多相同的.c文件编译成.o文件,他们的规则都是一样的,比如你想在math静态库中增加“减法”方法,是不是makefile中,不用再增加gcc -c生成目标文件的操作了。

    为什么使用${OJBC}使用递归赋值?当你想增加新的文件,比如math静态库中增加“减法”,在makefile中,只需要用+=追加赋值的方法,就可以让全文的${OJBC}都改变,方便增删调试。另外还可以用与Makefile的if..else..条件语句中,实现不同情况的变量差异化。

    自动变量是很常见的,主要用在“规则”中,对“目标”和“依赖”进行一种符号化的引用(此说法可能不太正规哈,主要是表其意思)。也是一样的,为了方便,为了增减文件的时候,可以更少的更改Makefile,更好的一劳永逸

    当然,Makefile还有很多东西可以补充,这里只带大家入门,应对自己写的简单的小工程差不多了,公司中,大的工程一般会有已经写好的Makefile,自己添加代码和文件基本也不会把Makefile结构都改了。总的来说,Makefile看懂,知道思想,用的时候知道查即可吧。

    个人认为,没有统一的模板,只有统一的思想~。没有说非得用自动变量或怎么样就好、主要目的是适合自己的工程。

     

    展开全文
  • ​ 在Linux下编写程序时,可能会多文件编程,如果一个文件修改了,那么与它相关的文件都需要重新编译链接,这样一个一个敲命令很不方便,所以需要写一个Makefile文件,把各个文件在编译链接过程中的依赖方法和依赖...

    Linux下Makefile文件的编写

    ​ 在Linux下编写程序时,可能会多文件编程,如果一个文件修改了,那么与它相关的文件都需要重新编译链接,这样一个一个敲命令很不方便,所以需要写一个Makefile文件,把各个文件在编译链接过程中的依赖方法和依赖关系写清楚。这样每次只需要使用make命令,会自动寻找该文件夹下的命名为makefile或Makefile的文件,并执行。

    ​ 先举一个例子:

    在这里插入图片描述
    ​ 内在原理可以了解程序编译过程.
    注记

    • -o 后边的都是目标文件,(output)
    • 由上到下,可执行文件依赖于.o文件(二进制文件),.o文件依赖于.s文件(汇编语言),.s文件依赖于.i文件(预处理后的文件),.i文件依赖于.c文件(源代码)。
    • 预处理、编译、汇编的选项分别是 ESC。
    展开全文
  • gcc -g 使用gdb gcc -l 指定链接库的位置 gcc -I 指定头文件的位置 gcc -E hello.c -o hello.i 预处理 gcc -S hello.c -o hello.s 编译 gcc -c helli,c -o hello.o 汇编
  • 1、gcc对hello_linux.c进行编译的过程中实际做了四步工作,分别是预处理、编译、汇编、链接 (1)预处理过程:gcc工具调用预处理工具cpp将源代码中的宏定义、头文件包含以及条件编译进行替换,替换过的文件仍然是...

    1、gcc对hello_linux.c进行编译的过程中实际做了四步工作,分别是预处理、编译、汇编、链接
    (1)预处理过程:gcc工具调用预处理工具cpp将源代码中的宏定义、头文件包含以及条件编译进行替换,替换过的文件仍然是C语言形式的文件,扩展名为.i。
    (2)编译过程:gcc把.i文件翻译成汇编语言描述的.s文件,.s文件是汇编语言的语法格式,可用文本编辑器打开。
    (3)汇编过程:gcc调用汇编工具as把.s文件翻译成.o的二进制文件,也就是机器代码,称其为目标文件或者目标模块。
    (4)链接过程:gcc调用链接工具ld把一个或多个目标.o文件以及它们需要的一些函数库链接成一个完整的可执行文件。
    2、gcc 参数
    1)–S 只是进行编译不汇编,生成汇编代码
    2)-c 只是编译不链接,生成目标代码
    3)-o 连接过程
    3、gcc编译总结
    源文件首先会生成中间目标文件,再由中间目标文件生成运行文件。在编译时,编译器仅仅检测程序语法,和函数、变量是否被声明。假设函数未被声明,编译器会给出一个警告,但能够生成.o文件。而在链接程序时,链接器会在全部的.o文件中找寻函数的实现,假设找不到,那到就会报链接错误码(Linker Error)。
    4、Makefile的规则
    target … : prerequisites …
    command


    其中target表示所要生成的.o文件,prerequisites表示生成该文件所依赖的源文件或头文件, command是make要运行的命令(可以是任意的shell命令)。
    5、一个简单的例子
    这里写图片描述

    1)edit使我们最终要生成的文件,生成该文件就需要:之后的文件,生成edit文件的命令为第二行的命令。
    2)假设当前文件夹下没有这些文件,make会在Makefile里找目标为.o文件的依赖性,假设找到则再依据那一个规则生成.o文件。
    6、在下面生成.o文件的命令中使用了gcc -c而不是-o命令,原因是-o命令是是一个连接的过程,而此时可能有文件文编译,所以不一定连接上,所以用-c命令仅进行编译,最终在生成edit文件的时候使用-o命令将所生成的.o文件链接到一起生成一个可执行文件。

    展开全文
  • linux 内核Makefile文件

    2011-03-29 16:09:00
    === 目录 === 1 概述 === 2 角色分工 === 3 内核编译文件 --- 3.1 目标定义 --- 3.2 内嵌对象 - obj-y --- 3.3 可加载模块 - obj-m --- 3.4 导出符号 --- 3.5 库文件 - lib-y --- 3.6 目录递归 --- 3.7 ...
  • Linux内核Makefile文件 --译自Linux2.6.x Kernel Makefiles  本文档描述了linux内核的makefile文件。 === 目录  === 1 概述  === 2 角色分工  === 3 内核编译文件  --- 3.1 目标定义  --- 3.2 ...
  • Linux编写驱动的过程中,有两个文件是我们必须要了解和知晓的。这其中,一个是Kconfig文件,另外一个是Makefile文件。如果大家比较熟悉的话,那么肯定对内核编译需要的.config文件不陌生,在.config文件中,我们...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 44,183
精华内容 17,673
关键字:

linuxmakefile多文件汇编

linux 订阅