linux kernel 4.18.16 Makefile註釋
阿新 • • 發佈:2018-12-19
前言:
本人的作業系統作業,要求註釋最新版本的GNU Linux kernel 的Makefile檔案,我還沒用過Linux,一開啟這個Makefile真的是下了一跳……好多東西要註釋啊,忙活了一個下午,成果就寫個部落格吧。
參考:
Makefile:
# SPDX-License-Identifier: GPL-2.0
# SPDX-許可證-識別符:GPL-2.0
# 版本 = 4
# 修補版本號 = 18
# 次級版本號 = 15
# 附加資訊,一般預設為空,可以自行設定
# 此版本的名字
VERSION = 4
PATCHLEVEL = 18
SUBLEVEL = 15
EXTRAVERSION =
NAME = Merciless Moray
# *DOCUMENTATION*
# To see a list of typical targets execute "make help"
# More info can be located in ./README
# Comments in this file are targeted only to the developer, do not
# expect to learn how to build the kernel reading this file.
# That's our default target when none is given on the command line
PHONY := _all
_all:
# o Do not use make's built-in rules and variable
# (this increases performance and avoids hard-to-debug behaviour);
# o Look for make include files relative to root of kernel src
# o 禁止使用內建的規則和變數
# (這提高了效能並避免了難以除錯的行為);
# o 查詢與核心src有關的生成包含檔案
MAKEFLAGS += -rR --include-dir=$( CURDIR)
# 非傳遞環境變數,不會傳遞到下一級Makefile
# Avoid funny character set dependencies
unexport LC_ALL
LC_COLLATE=C
LC_NUMERIC=C
export LC_COLLATE LC_NUMERIC
# 傳遞環境變數,環境變數會傳遞到下一級Makefile
# Avoid interference with shell env settings
unexport GREP_OPTIONS
# We are using a recursive build, so we need to do a little thinking
# to get the ordering right.
#
# Most importantly: sub-Makefiles should only ever modify files in
# their own directory. If in some directory we have a dependency on
# a file in another dir (which doesn't happen often, but it's often
# unavoidable when linking the built-in.a targets which finally
# turn into vmlinux), we will call a sub make in that other dir, and
# after that we are sure that everything which is in that other dir
# is now up to date.
#
# The only cases where we need to modify files which have global
# effects are thus separated out and done before the recursive
# descending is started. They are now explicitly listed as the
# prepare rule.
# Beautify output //格式化的回顯控制
# ---------------------------------------------------------------------------
#
# Normally, we echo the whole command before executing it. By making
# that echo $($(quiet)$(cmd)), we now have the possibility to set
# $(quiet) to choose other forms of output instead, e.g.
#
# quiet_cmd_cc_o_c = Compiling $(RELDIR)/[email protected] //這個是簡化版的輸出
# cmd_cc_o_c = $(CC) $(c_flags) -c -o [email protected] $< //這個是原版的輸出
#
# If $(quiet) is empty, the whole command will be printed. //表示如果為空則完全回顯
# If it is set to "quiet_", only the short version will be printed. //$(quiet)=quiet_表示只有版本顯示
# If it is set to "silent_", nothing will be printed at all, since //表示完全不回顯
# the variable $(silent_cmd_cc_o_c) doesn't exist.
#
# 下面是$(Q)用作字首,如果$(Q)[email protected]表示不回顯,為空表示回顯
# A simple variant is to prefix commands with $(Q) - that's useful
# for commands that shall be hidden in non-verbose mode.
#
# $(Q)ln [email protected] :<
#
# If KBUILD_VERBOSE equals 0 then the above command will be hidden.
# If KBUILD_VERBOSE equals 1 then the above command is displayed.
#
# 如果$(quiet)為空,則輸出原版編譯命令
# 如果$(quiet)為quiet_,則輸出簡化版本的
# 如果$(quiet)為silent_,則任何都不輸出,因為slient_cmd_cc_o_cc根本不存在
# 不過有個全域性的控制,如果KBUILD_VERBOSE為0,則輸出簡化版本,如果為1,則輸出簡化版本
#
# To put more focus on warnings, be less verbose as default
# Use 'make V=1' to see the full commands
# 設定列印訊息級別,通過呼叫 make 時新增的引數 V 來修改
ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
# 預設的KBUILD_VERBOSE的值被設定為0,也就意味著在編譯過程中,只輸出
# 預設我們是不回顯的,回顯即在命令執行前顯示要執行的命令
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif
ifeq ($(KBUILD_VERBOSE),1) #這裡就是定義是否要回顯
quiet =
Q = #不以@開頭表示回顯
else
quiet=quiet_
Q = @ #這裡以@開頭表示不回顯
endif
# If the user is running make -s (silent mode), suppress echoing of
# commands
ifneq ($(findstring s,$(filter-out --%,$(MAKEFLAGS))),)
# 如果MAKEFLAGS中帶了-s表示完全不要回顯
quiet=silent_
tools_silent=s
endif
# 輸出quiet給子make
export quiet Q KBUILD_VERBOSE
# kbuild supports saving output files in a separate directory.
# To locate output files in a separate directory two syntaxes are supported.
# In both cases the working directory must be the root of the kernel src.
# 1) O=
# Use "make O=dir/to/store/output/files/"
#
# 2) Set KBUILD_OUTPUT
# Set the environment variable KBUILD_OUTPUT to point to the directory
# where the output files shall be placed.
# export KBUILD_OUTPUT=dir/to/store/output/files/
# make
#
# The O= assignment takes precedence over the KBUILD_OUTPUT environment
# variable.
# 上面這裡是介紹可以設定kbuild過程中的輸出檔案目的地址,有兩種方式:
# 第一種是在 make 引數中設定(make -O dir")
# 第二種方式是設定環境變數(export KBUILD_OUTPU=dir)
# KBUILD_SRC is not intended to be used by the regular user (for now),
# it is set on invocation of make with KBUILD_OUTPUT or O= specified.
ifeq ($(KBUILD_SRC),)
# 如果KBUILD_SRC值為空,就執行下面的操作
# 上面說的這個功能當前對一般使用者沒有用
# OK, Make called in directory where kernel src resides
# Do we want to locate output files in a separate directory?
# 如果命令列中定義了O變數來修改輸出目錄,就用使用者定義的這個目錄
ifeq ("$(origin O)", "command line")
KBUILD_OUTPUT := $(O)
# 用於指定我們的輸出檔案的輸出目錄
endif
# Cancel implicit rules on top Makefile
# 取消隱式推導規則
$(CURDIR)/Makefile Makefile: ;
# CURDIR值為當前的核心原始碼目錄current.dir
# 上面操作是檢視當前目錄的Makefile是否為最新
ifneq ($(words $(subst :, ,$(CURDIR))), 1)
$(error main directory cannot contain spaces nor colons)
endif
# 如果輸出目錄值不為空,正常情況下進入
ifneq ($(KBUILD_OUTPUT),)
# check that the output directory actually exists
# 上面說在輸出目錄中執行第二個make命令,傳入相關變數,並且檢查輸出目錄是否存在
saved-output := $(KBUILD_OUTPUT)
KBUILD_OUTPUT := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) \
&& pwd)
$(if $(KBUILD_OUTPUT),, \
$(error failed to create output directory "$(saved-output)"))
# MAKECMDGOALS 是make的一個環境變數,儲存所有目標列表
# 下面這個命令將所有指明的目標列表和sub-make加入到PHONY中
PHONY += $(MAKECMDGOALS) sub-make
# 下面這個命令很是不明白,大概是用來過濾,太複雜了
# 簡單分析下,過濾掉MAKECMDGOALS中的_all sub-make和$(CURDIR)/Makefile,並將它們都賦值為sub-make
$(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make
@:
# Invoke a second make in the output directory, passing relevant variables
# 這個就是sub-make的值,大致就是將頂層Makefile定義的規則傳給下層資料夾中的Makefile中
sub-make:
$(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \
-f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS))
# Leave processing to above invocation of make
# 表示執行完了子makefile,返回頂層make
skip-makefile := 1
endif # ifneq ($(KBUILD_OUTPUT),) #結束ifneq
endif # ifeq ($(KBUILD_SRC),) #結束ifeq
# We process the rest of the Makefile if this is the final invocation of make
# 表示如果skip-makefile為空,這按照分類執行,也就是不是整體進行make
ifeq ($(skip-makefile),)
# Do not print "Entering directory ...",
# but we want to display it when entering to the output directory
# so that IDEs/editors are able to understand relative filenames.
MAKEFLAGS += --no-print-directory
# Call a source code checker (by default, "sparse") as part of the
# C compilation. //呼叫一個靜態分析工具作為c編譯器的部分
#
# Use 'make C=1' to enable checking of only re-compiled files.
# Use 'make C=2' to enable checking of *all* source files, regardless
# of whether they are re-compiled or not.
#
# See the file "Documentation/dev-tools/sparse.rst" for more details,
# including where to get the "sparse" utility.
# 設定語法檢查級別,預設級別為0
ifeq ("$(origin C)", "command line")
KBUILD_CHECKSRC = $(C)
endif
ifndef KBUILD_CHECKSRC
KBUILD_CHECKSRC = 0
endif
# 用M來指定外部模組的目錄
# Use make M=dir to specify directory of external module to build
# Old syntax make ... SUBDIRS=$PWD is still supported
# Setting the environment variable KBUILD_EXTMOD take precedence
# 下面是為了支援舊語法格式的Makefile的設定
ifdef SUBDIRS
KBUILD_EXTMOD ?= $(SUBDIRS)
endif
ifeq ("$(origin M)", "command line")
KBUILD_EXTMOD := $(M)
endif
ifeq ($(KBUILD_SRC),)
# building in the source tree
srctree := .
else
ifeq ($(KBUILD_SRC)/,$(dir $(CURDIR)))
# building in a subdirectory of the source tree
srctree := ..
else
srctree := $(KBUILD_SRC)
endif
endif
export KBUILD_CHECKSRC KBUILD_EXTMOD KBUILD_SRC
objtree := .
src := $(srctree) # 將src設定為原始碼根目錄
obj := $(objtree) #將obj設定位當前目錄
VPATH := $(srctree)$(if $(KBUILD_EXTMOD),:$(KBUILD_EXTMOD))
# 這裡是說當前目錄找不到原始檔時的搜尋目錄
export srctree objtree VPATH
# export用於要將指定變數輸出給子make
# To make sure we do not include .config for any of the *config targets
# catch them early, and hand them over to scripts/kconfig/Makefile
# It is allowed to specify more targets when calling make, including
# mixing *config targets and build targets.
# For example 'make oldconfig all'.
# Detect when mixed targets is specified, and make a second invocation
# of make so .config is not included in this case either (for *config).
# 下面這裡是保證呼叫make的時候輸入了*config目標,這是就不能包含.config檔案
version_h := include/generated/uapi/linux/version.h
old_version_h := include/linux/version.h
clean-targets := %clean mrproper cleandocs
no-dot-config-targets := $(clean-targets) \
cscope gtags TAGS tags help% %docs check% coccicheck \
$(version_h) headers_% archheaders archscripts \
kernelversion %src-pkg
no-sync-config-targets := $(no-dot-config-targets) install %install
config-targets := 0
mixed-targets := 0
dot-config := 1
may-sync-config := 1
ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
dot-config := 0
endif
endif
ifneq ($(filter $(no-sync-config-targets), $(MAKECMDGOALS)),)
ifeq ($(filter-out $(no-sync-config-targets), $(MAKECMDGOALS)),)
may-sync-config := 0
endif
endif
ifneq ($(KBUILD_EXTMOD),)
may-sync-config := 0
endif
ifeq ($(KBUILD_EXTMOD),)
ifneq ($(filter config %config,$(MAKECMDGOALS)),)
config-targets := 1
ifneq ($(words $(MAKECMDGOALS)),1)
mixed-targets := 1
endif
endif
endif
# For "make -j clean all", "make -j mrproper defconfig all", etc.
ifneq ($(filter $(clean-targets),$(MAKECMDGOALS)),)
ifneq ($(filter-out $(clean-targets),$(MAKECMDGOALS)),)
mixed-targets := 1
endif
endif
# install and modules_install need also be processed one by one
ifneq ($(filter install,$(MAKECMDGOALS)),)
ifneq ($(filter modules_install,$(MAKECMDGOALS)),)
mixed-targets := 1
endif
endif
ifeq ($(mixed-targets),1)
# ===========================================================================
# We're called with mixed targets (*config and build targets).
# Handle them one by one.
PHONY += $(MAKECMDGOALS) __build_one_by_one
$(filter-out __build_one_by_one, $(MAKECMDGOALS)): __build_one_by_one
@:
__build_one_by_one:
$(Q)set -e; \
for i in $(MAKECMDGOALS); do \
$(MAKE) -f $(srctree)/Makefile $$i; \
done
else
# We need some generic definitions (do not try to remake the file).
# 這裡是定義了一些通用的srctree變數定義,這裡執行remake操作
scripts/Kbuild.include: ;
# 這裡的檔案包含了大量通用的函式和變數,已經為這個檔案建立了筆記,抽個時間進行分析,
# 還是先對那個進行分析吧,因為下面會用到它定義的很多變數
include scripts/Kbuild.include
# Read KERNELRELEASE from include/config/kernel.release (if it exists)
KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
# 設定版本號
KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)
export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION
include scripts/subarch.include
# Cross compiling and selecting different set of gcc/bin-utils
# ---------------------------------------------------------------------------
#
# When performing cross compilation for other architectures ARCH shall be set
# to the target architecture. (See arch/* for the possibilities).
# ARCH can be set during invocation of make:
# make ARCH=ia64
# Another way is to have ARCH set in the environment.
# The default ARCH is the host where make is executed.
# 當執行交叉編譯的時候,要將ARCH的值設定為目標的架構,支援的交媾可以在arch目錄下看到
# ARCH可以在執行make命令時設定,如make ARCH=arm
# 預設的ARCH是和Host相同
# CROSS_COMPILE specify the prefix used for all executables used
# during compilation. Only gcc and related bin-utils executables
# are prefixed with $(CROSS_COMPILE).
# CROSS_COMPILE can be set on the command line
# make CROSS_COMPILE=ia64-linux-
# Alternatively CROSS_COMPILE can be set in the environment.
# Default value for CROSS_COMPILE is not to prefix executables
# Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile
# 下面是處理交叉編譯這塊的,交叉編譯需要知道Host的架構和Target的架構吧
# CROSS_COMPILE是編譯時所有命令的字首,可以在執行make的時候指定如make CROSS_COMPILE=ia64-linux-
# CROSS_COMPILE也可以通過環境變數來設定
# 預設情況下是不需要CROSS_COMPILE來執行的,也就是不交叉編譯
ARCH ?= $(SUBARCH) # 預設Host的架構與Target架構相同
# Architecture as present in compile.h
UTS_MACHINE := $(ARCH)
SRCARCH := $(ARCH)
# 下面是對x86結構的特殊照顧,不明白原因
# Additional ARCH settings for x86
ifeq ($(ARCH),i386)
SRCARCH := x86
endif
ifeq ($(ARCH),x86_64)
SRCARCH := x86
endif
# Additional ARCH settings for sparc
ifeq ($(ARCH),sparc32)
SRCARCH := sparc
endif
ifeq ($(ARCH),sparc64)
SRCARCH := sparc
endif
# Additional ARCH settings for sh
ifeq ($(ARCH),sh64)
SRCARCH := sh
endif
# 聲稱的配置檔名
KCONFIG_CONFIG ?= .config
export KCONFIG_CONFIG
# 設定kbuild使用的Shell
# SHELL used by kbuild
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
HOST_LFS_CFLAGS := $(shell getconf LFS_CFLAGS 2>/dev/null)
HOST_LFS_LDFLAGS := $(shell getconf LFS_LDFLAGS 2>/dev/null)
HOST_LFS_LIBS := $(shell getconf LFS_LIBS 2>/dev/null)
# 設定編譯器,和全域性編譯FLAGS
HOSTCC = gcc
HOSTCXX = g++
HOSTCFLAGS := -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 \
-fomit-frame-pointer -std=gnu89 $(HOST_LFS_CFLAGS)
HOSTCXXFLAGS := -O2 $(HOST_LFS_CFLAGS)
HOSTLDFLAGS := $(HOST_LFS_LDFLAGS)
HOST_LOADLIBES := $(HOST_LFS_LIBS)
# Make variables (CC, etc...)
# 定義一系列Make變數,大部分都是編譯階段使用的一些工具,如as,cc,ar,nm,objcopy等
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
LEX = flex
YACC = bison
AWK = awk
GENKSYMS = scripts/genksyms/genksyms
INSTALLKERNEL := installkernel
DEPMOD = /sbin/depmod
PERL = perl
PYTHON = python
PYTHON2 = python2
PYTHON3 = python3
CHECK = sparse # 預設的字串檢查
CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
-Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF)
NOSTDINC_FLAGS =
CFLAGS_MODULE =
AFLAGS_MODULE =
LDFLAGS_MODULE =
CFLAGS_KERNEL =
AFLAGS_KERNEL =
LDFLAGS_vmlinux =
# Use USERINCLUDE when you must reference the UAPI directories only.
USERINCLUDE := \
-I$(srctree)/arch/$(SRCARCH)/include/uapi \
-I$(objtree)/arch/$(SRCARCH)/include/generated/uapi \
-I$(srctree)/include/uapi \
-I$(objtree)/include/generated/uapi \
-include $(srctree)/include/linux/kconfig.h
# Use LINUXINCLUDE when you must reference the include/ directory.
# Needed to be compatible with the O= option
LINUXINCLUDE := \
-I$(srctree