江苏扬州建设工程信息网站代写新闻稿
一. 前言
KernelPackage是OpenWrt用来编译内核模块的函数,其实KernelPackage后面会调用BuildPackage,这里会一块将BuildPackage也顺便分析,本文以gpio-button-hotplug驱动模块为例,讲解整个编译过程。
gpio-button-hotplug驱动编译命令如下:
make package/kernel/gpio-button-hotplug/compile V=s
二. 编译过程分析
1. 编译命令运行分析
由前面的文章介绍了subdir的作用后,我们可以知道,subdir会将make package/kernel/gpio-button-hotplug/compile V=s命令转换为如下命令:
make -C package/kernel/gpio-button-hotplug compile
make会去到package/kernel/gpio-button-hotplug下找Makfile,并且找其中的compile目标运行,gpio-button-hotplug的Makefile如下:
include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mkPKG_NAME:=gpio-button-hotplug
PKG_RELEASE:=3
PKG_LICENSE:=GPL-2.0include $(INCLUDE_DIR)/package.mkdefine KernelPackage/gpio-button-hotplug
......
endef
......
MAKE_OPTS:= $(KERNEL_MAKE_FLAGS) M="$(PKG_BUILD_DIR)"define Build/Compile$(MAKE) -C "$(LINUX_DIR)" $(MAKE_OPTS) modules
endef$(eval $(call KernelPackage,gpio-button-hotplug))
可以看到,这里会调用到KernelPackage,KernelPackage内容如下:
include/kernel.mk:
define KernelPackageNAME:=$(1)$(eval $(call Package/Default))$(eval $(call KernelPackage/Defaults))$(eval $(call KernelPackage/$(1)))$(eval $(call KernelPackage/$(1)/$(BOARD)))$(eval $(call KernelPackage/$(1)/$(BOARD)/$(if $(SUBTARGET),$(SUBTARGET),generic)))define Package/kmod-$(1)TITLE:=$(TITLE)SECTION:=kernelCATEGORY:=Kernel modulesDESCRIPTION:=$(DESCRIPTION)EXTRA_DEPENDS:=kernel (=$(LINUX_VERSION)-$(LINUX_RELEASE)-$(LINUX_VERMAGIC))VERSION:=$(LINUX_VERSION)$(if $(PKG_VERSION),+$(PKG_VERSION))-$(if $(PKG_RELEASE),$(PKG_RELEASE),$(LINUX_RELEASE))PKGFLAGS:=$(PKGFLAGS)$(call KernelPackage/$(1))$(call KernelPackage/$(1)/$(BOARD))$(call KernelPackage/$(1)/$(BOARD)/$(if $(SUBTARGET),$(SUBTARGET),generic))endefifdef KernelPackage/$(1)/conffilesdefine Package/kmod-$(1)/conffiles
$(call KernelPackage/$(1)/conffiles)endefendififdef KernelPackage/$(1)/descriptiondefine Package/kmod-$(1)/description
$(call KernelPackage/$(1)/description)endefendififdef KernelPackage/$(1)/configdefine Package/kmod-$(1)/config
$(call KernelPackage/$(1)/config)endefendif$(call KernelPackage/depends)$(call KernelPackage/hooks)ifneq ($(if $(filter-out %=y %=n %=m,$(KCONFIG)),$(filter m y,$(foreach c,$(filter-out %=y %=n %=m,$(KCONFIG)),$($(c)))),.),)define Package/kmod-$(1)/install@for mod in $$(call version_filter,$$(FILES)); do \if grep -q "$$$$$$$${mod##$(LINUX_DIR)/}" "$(LINUX_DIR)/modules.builtin"; then \echo "<<2>>" \echo "NOTICE: module '$$$$$$$$mod' is built-in."; \elif [ -e $$$$$$$$mod ]; then \echo "<<3>> $$$$$$$$mod" ; \mkdir -p $$(1)/$(MODULES_SUBDIR) ; \$(CP) -L $$$$$$$$mod $$(1)/$(MODULES_SUBDIR)/ ; \else \echo "ERROR: module '$$$$$$$$mod' is missing." >&2; \exit 1; \fi; \done;$(call ModuleAutoLoad,$(1),$$(1),$(filter-out 0-,$(word 1,$(AUTOLOAD))-),$(filter-out 0,$(word 2,$(AUTOLOAD))),$(sort $(wordlist 3,99,$(AUTOLOAD))))$(call KernelPackage/$(1)/install,$$(1))endef$(if $(CONFIG_PACKAGE_kmod-$(1)),elsecompile: $(1)-disabled$(1)-disabled:@echo "WARNING: kmod-$(1) is not available in the kernel config - generating empty package" >&2define Package/kmod-$(1)/installecho "<<1>>"trueendef)endif$$(eval $$(call BuildPackage,kmod-$(1)))$$(IPKG_kmod-$(1)): $$(wildcard $$(call version_filter,$$(FILES)))endef
细节先不分析,先找compile目标,可以看到KernelPackage下没有compile目标,原因如下:
ifneq ($(if $(filter-out %=y %=n %=m,$(KCONFIG)),$(filter m y,$(foreach c,$(filter-out %=y %=n %=m,$(KCONFIG)),$($(c)))),.),)
如上代码KCONFIG变量(package/kernel/linux/modules/*.mk才有定义)为空,所以以上表达式可以写为ifneq (.,),结果为真,执行上面的分支,上面分支没有compile目标。
此时,就要到BuildPackage里面寻找了。BuildPackage代码如下:
define BuildPackage$(eval $(Package/Default))$(eval $(Package/$(1)))ifdef DESCRIPTION
$$(error DESCRIPTION:= is obsolete, use Package/PKG_NAME/description)
endififndef Package/$(1)/description
define Package/$(1)/description$(TITLE)
endef
endifBUILD_PACKAGES += $(1)$(STAMP_PREPARED): $$(if $(QUILT)$(DUMP),,$(call find_library_dependencies,$(1)))$(foreach FIELD, TITLE CATEGORY SECTION VERSION,ifeq ($($(FIELD)),)$$(error Package/$(1) is missing the $(FIELD) field)endif)$(if $(DUMP), \$(if $(CHECK),,$(Dumpinfo/Package)), \$(foreach target, \$(if $(Package/$(1)/targets),$(Package/$(1)/targets), \$(if $(PKG_TARGETS),$(PKG_TARGETS), ipkg) \), $(BuildTarget/$(target)) \) \)$(if $(PKG_HOST_ONLY),,$(call Build/DefaultTargets,$(1)))
endef
这里也没有compile目标。我们分析里面如下代码:
$(if $(DUMP), \
$(if $(CHECK),,$(Dumpinfo/Package)), \
$(foreach target, \
$(if $(Package/$(1)/targets),$(Package/$(1)/targets), \
$(if $(PKG_TARGETS),$(PKG_TARGETS), ipkg) \
), $(BuildTarget/$(target)) \
) \
)
以上代码不难分析,结果为$(BuildTarget/ipkg)。接下来分析BuildTarget/ipkg,代码如下:
define BuildTarget/ipkgABIV_$(1):=$(call FormatABISuffix,$(1),$(ABI_VERSION))PDIR_$(1):=$(call FeedPackageDir,$(1))IPKG_$(1):=$$(PDIR_$(1))/$(1)$$(ABIV_$(1))_$(VERSION)_$(PKGARCH).ipkIDIR_$(1):=$(PKG_BUILD_DIR)/ipkg-$(PKGARCH)/$(1)KEEP_$(1):=$(strip $(call Package/$(1)/conffiles))ifeq ($(BUILD_VARIANT),$$(if $$(VARIANT),$$(VARIANT),$(BUILD_VARIANT)))do_install=ifdef Package/$(1)/installdo_install=yesendififdef Package/$(1)/install-overlaydo_install=yesendififdef do_installifneq ($(CONFIG_PACKAGE_$(1))$(DEVELOPER),)IPKGS += $(1)$(_pkg_target)compile: $$(IPKG_$(1)) $(PKG_INFO_DIR)/$(1).provides $(PKG_BUILD_DIR)/.pkgdir/$(1).installedprepare-package-install: $$(IPKG_$(1))compile: $(STAGING_DIR_ROOT)/stamp/.$(1)_installedelse$(if $(CONFIG_PACKAGE_$(1)),$$(info WARNING: skipping $(1) -- package not selected))endif.PHONY: $(PKG_INSTALL_STAMP).$(1)ifeq ($(CONFIG_PACKAGE_$(1)),y)compile: $(PKG_INSTALL_STAMP).$(1)endif$(PKG_INSTALL_STAMP).$(1): prepare-package-installecho "$(1)" >> $(PKG_INSTALL_STAMP)else$(if $(CONFIG_PACKAGE_$(1)),$$(warning WARNING: skipping $(1) -- package has no install section))endifendifDEPENDS:=$(call PKG_FIXUP_DEPENDS,$(1),$(DEPENDS))IDEPEND_$(1):=$$(call filter_deps,$$(DEPENDS))IDEPEND += $$(patsubst %,$(1):%,$$(IDEPEND_$(1)))$(FixupDependencies)$(FixupReverseDependencies)$(eval $(call BuildIPKGVariable,$(1),conffiles))$(eval $(call BuildIPKGVariable,$(1),preinst,,1))$(eval $(call BuildIPKGVariable,$(1),postinst,-pkg,1))$(eval $(call BuildIPKGVariable,$(1),prerm,-pkg,1))$(eval $(call BuildIPKGVariable,$(1),postrm,,1))$(PKG_BUILD_DIR)/.pkgdir/$(1).installed : export PATH=$$(TARGET_PATH_PKG)$(PKG_BUILD_DIR)/.pkgdir/$(1).installed: $(STAMP_BUILT)rm -rf $$@ $(PKG_BUILD_DIR)/.pkgdir/$(1)mkdir -p $(PKG_BUILD_DIR)/.pkgdir/$(1)$(call Package/$(1)/install,$(PKG_BUILD_DIR)/.pkgdir/$(1))$(call Package/$(1)/install_lib,$(PKG_BUILD_DIR)/.pkgdir/$(1))touch $$@$(STAGING_DIR_ROOT)/stamp/.$(1)_installed: $(PKG_BUILD_DIR)/.pkgdir/$(1).installedmkdir -p $(STAGING_DIR_ROOT)/stamp$(if $(ABI_VERSION),echo '$(ABI_VERSION)' | cmp -s - $(PKG_INFO_DIR)/$(1).version || { \echo '$(ABI_VERSION)' > $(PKG_INFO_DIR)/$(1).version; \$(foreach pkg,$(filter-out $(1),$(PROVIDES)), \cp $(PKG_INFO_DIR)/$(1).version $(PKG_INFO_DIR)/$(pkg).version; \) \} )$(call locked,$(CP) $(PKG_BUILD_DIR)/.pkgdir/$(1)/. $(STAGING_DIR_ROOT)/,root-copy)touch $$@Package/$(1)/DEPENDS := $$(call mergelist,$$(foreach dep,$$(filter-out @%,$$(IDEPEND_$(1))),$$(dep)$$(call GetABISuffix,$$(dep))))ifneq ($$(EXTRA_DEPENDS),)Package/$(1)/DEPENDS := $$(EXTRA_DEPENDS)$$(if $$(Package/$(1)/DEPENDS),$$(comma) $$(Package/$(1)/DEPENDS))endif$(_define) Package/$(1)/CONTROL
Package: $(1)$$(ABIV_$(1))
Version: $(VERSION)
$$(call addfield,Depends,$$(Package/$(1)/DEPENDS)
)$$(call addfield,Conflicts,$$(call mergelist,$(CONFLICTS))
)$$(call addfield,Provides,$$(call mergelist,$$(filter-out $(1)$$(ABIV_$(1)),$(PROVIDES)$$(if $$(ABIV_$(1)), $(1) $(foreach provide,$(PROVIDES),$(provide)$$(ABIV_$(1))))))
)$$(call addfield,Alternatives,$$(call mergelist,$(ALTERNATIVES))
)$$(call addfield,Source,$(SOURCE)
)$$(call addfield,SourceName,$(1)
)$$(call addfield,License,$(LICENSE)
)$$(call addfield,LicenseFiles,$(LICENSE_FILES)
)$$(call addfield,Section,$(SECTION)
)$$(call addfield,Require-User,$(USERID)
)$$(call addfield,SourceDateEpoch,$(PKG_SOURCE_DATE_EPOCH)
)$$(if $$(ABIV_$(1)),ABIVersion: $$(ABIV_$(1))
)$(if $(PKG_CPE_ID),CPE-ID: $(PKG_CPE_ID)
)$(if $(filter hold,$(PKG_FLAGS)),Status: unknown hold not-installed
)$(if $(filter essential,$(PKG_FLAGS)),Essential: yes
)$(if $(MAINTAINER),Maintainer: $(MAINTAINER)
)Architecture: $(PKGARCH)
Installed-Size: 0
$(_endef)$$(IPKG_$(1)) : export CONTROL=$$(Package/$(1)/CONTROL)$$(IPKG_$(1)) : export DESCRIPTION=$$(Package/$(1)/description)$$(IPKG_$(1)) : export PATH=$$(TARGET_PATH_PKG)$$(IPKG_$(1)) : export PKG_SOURCE_DATE_EPOCH:=$(PKG_SOURCE_DATE_EPOCH)$(PKG_INFO_DIR)/$(1).provides $$(IPKG_$(1)): $(STAMP_BUILT) $(INCLUDE_DIR)/package-ipkg.mk@rm -rf $$(IDIR_$(1)); \$$(call remove_ipkg_files,$(1),$$(call opkg_package_files,$(call gen_ipkg_wildcard,$(1))))mkdir -p $(PACKAGE_DIR) $$(IDIR_$(1))/CONTROL $(PKG_INFO_DIR)$(call Package/$(1)/install,$$(IDIR_$(1)))$(if $(Package/$(1)/install-overlay),mkdir -p $(PACKAGE_DIR) $$(IDIR_$(1))/rootfs-overlay)$(call Package/$(1)/install-overlay,$$(IDIR_$(1))/rootfs-overlay)-find $$(IDIR_$(1)) -name 'CVS' -o -name '.svn' -o -name '.#*' -o -name '*~'| $(XARGS) rm -rf@( \find $$(IDIR_$(1)) -name lib\*.so\* -or -name \*.ko | awk -F/ '{ print $$$$NF }'; \for file in $$(patsubst %,$(PKG_INFO_DIR)/%.provides,$$(IDEPEND_$(1))); do \if [ -f "$$$$file" ]; then \cat $$$$file; \fi; \done; $(Package/$(1)/extra_provides) \) | sort -u > $(PKG_INFO_DIR)/$(1).provides$(if $(PROVIDES),@for pkg in $(filter-out $(1),$(PROVIDES)); do cp $(PKG_INFO_DIR)/$(1).provides $(PKG_INFO_DIR)/$$$$pkg.provides; done)$(CheckDependencies)$(RSTRIP) $$(IDIR_$(1))ifneq ($$(CONFIG_IPK_FILES_CHECKSUMS),)(cd $$(IDIR_$(1)); \( \find . -type f \! -path ./CONTROL/\* -exec mkhash sha256 -n \{\} \; 2> /dev/null | \sed 's|\([[:blank:]]\)\./| \1/|' > $$(IDIR_$(1))/CONTROL/files-sha256sum \) || true \)endif(cd $$(IDIR_$(1))/CONTROL; \( \echo "$$$$CONTROL"; \printf "Description: "; echo "$$$$DESCRIPTION" | sed -e 's,^[[:space:]]*, ,g'; \) > control; \chmod 644 control; \( \echo "#!/bin/sh"; \echo "[ \"\$$$${IPKG_NO_SCRIPT}\" = \"1\" ] && exit 0"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_postinst \$$$$0 \$$$$@"; \) > postinst; \( \echo "#!/bin/sh"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_prerm \$$$$0 \$$$$@"; \) > prerm; \chmod 0755 postinst prerm; \$($(1)_COMMANDS) \)ifneq ($$(KEEP_$(1)),)@( \keepfiles=""; \for x in $$(KEEP_$(1)); do \[ -f "$$(IDIR_$(1))/$$$$x" ] || keepfiles="$$$${keepfiles:+$$$$keepfiles }$$$$x"; \done; \[ -z "$$$$keepfiles" ] || { \mkdir -p $$(IDIR_$(1))/lib/upgrade/keep.d; \for x in $$$$keepfiles; do echo $$$$x >> $$(IDIR_$(1))/lib/upgrade/keep.d/$(1); done; \}; \)endif$(INSTALL_DIR) $$(PDIR_$(1))$(FAKEROOT) $(SCRIPT_DIR)/ipkg-build -m "$(FILE_MODES)" $$(IDIR_$(1)) $$(PDIR_$(1))@[ -f $$(IPKG_$(1)) ]$(1)-clean:$$(call remove_ipkg_files,$(1),$$(call opkg_package_files,$(call gen_ipkg_wildcard,$(1))))clean: $(1)-cleanendef
里面一段一段分析,如下:
ifeq ($(BUILD_VARIANT),$$(if $$(VARIANT),$$(VARIANT),$(BUILD_VARIANT)))
do_install=
ifdef Package/$(1)/install
do_install=yes
endif
ifdef Package/$(1)/install-overlay
do_install=yes
endif
ifdef do_install
ifneq ($(CONFIG_PACKAGE_$(1))$(DEVELOPER),)
IPKGS += $(1)
$(_pkg_target)compile: $$(IPKG_$(1)) $(PKG_INFO_DIR)/$(1).provides $(PKG_BUILD_DIR)/.pkgdir/$(1).installed
prepare-package-install: $$(IPKG_$(1))
compile: $(STAGING_DIR_ROOT)/stamp/.$(1)_installed
else
$(if $(CONFIG_PACKAGE_$(1)),$$(info WARNING: skipping $(1) -- package not selected))
endif.PHONY: $(PKG_INSTALL_STAMP).$(1)
ifeq ($(CONFIG_PACKAGE_$(1)),y)
compile: $(PKG_INSTALL_STAMP).$(1)
endif
$(PKG_INSTALL_STAMP).$(1): prepare-package-install
echo "$(1)" >> $(PKG_INSTALL_STAMP)
else
$(if $(CONFIG_PACKAGE_$(1)),$$(warning WARNING: skipping $(1) -- package has no install section))
endif
endif
由于BUILD_VARIANT和VARIANT都未定义,都为空,自然相等,所以条件成立。
由于Package/$(1)/install定义在include/kernel.mk中,如下:
include/kernel.mk:
define Package/kmod-$(1)/install@for mod in $$(call version_filter,$$(FILES)); do \if grep -q "$$$$$$$${mod##$(LINUX_DIR)/}" "$(LINUX_DIR)/modules.builtin"; then \echo "NOTICE: module '$$$$$$$$mod' is built-in."; \elif [ -e $$$$$$$$mod ]; then \mkdir -p $$(1)/$(MODULES_SUBDIR) ; \$(CP) -L $$$$$$$$mod $$(1)/$(MODULES_SUBDIR)/ ; \else \echo "ERROR: module '$$$$$$$$mod' is missing." >&2; \exit 1; \fi; \done;$(call ModuleAutoLoad,$(1),$$(1),$(filter-out 0-,$(word 1,$(AUTOLOAD))-),$(filter-out 0,$(word 2,$(AUTOLOAD))),$(sort $(wordlist 3,99,$(AUTOLOAD))))$(call KernelPackage/$(1)/install,$$(1))endef
所以do_install变量的值是yes。以上的结果可以通过$(warning $(Package/$(1)/install))打印出来证实。
由于,do_install=yes,此时,可以找到compile目标了,这里有两个compile目标,两个compile目标会依次运行,compile目标和为什么这么运行的例子如下:
include/package-ipkg.mk:
compile: $(STAGING_DIR_ROOT)/stamp/.$(1)_installed
......
compile: $(PKG_INSTALL_STAMP).$(1)example:
act1:@echo act1;
act2:@echo act2;compile: act1
compile: act2all: compile@echo allresult:
act1
act2
all
2. 先来分析第一个compile目标
$(STAGING_DIR_ROOT)/stamp/.$(1)_installed依赖于$(PKG_BUILD_DIR)/.pkgdir/$(1).installed,实现如下:
include/package-ipkg.mk:
$(STAGING_DIR_ROOT)/stamp/.$(1)_installed: $(PKG_BUILD_DIR)/.pkgdir/$(1).installedmkdir -p $(STAGING_DIR_ROOT)/stamp$(if $(ABI_VERSION),echo '$(ABI_VERSION)' | cmp -s - $(PKG_INFO_DIR)/$(1).version || { \echo '$(ABI_VERSION)' > $(PKG_INFO_DIR)/$(1).version; \$(foreach pkg,$(filter-out $(1),$(PROVIDES)), \cp $(PKG_INFO_DIR)/$(1).version $(PKG_INFO_DIR)/$(pkg).version; \) \} )$(call locked,$(CP) $(PKG_BUILD_DIR)/.pkgdir/$(1)/. $(STAGING_DIR_ROOT)/,root-copy)touch $$@
$(PKG_BUILD_DIR)/.pkgdir/$(1).installed依赖于STAMP_BUILT,实现如下:
include/package-ipkg.mk:
$(PKG_BUILD_DIR)/.pkgdir/$(1).installed: $(STAMP_BUILT)rm -rf $$@ $(PKG_BUILD_DIR)/.pkgdir/$(1)mkdir -p $(PKG_BUILD_DIR)/.pkgdir/$(1)$(call Package/$(1)/install,$(PKG_BUILD_DIR)/.pkgdir/$(1))$(call Package/$(1)/install_lib,$(PKG_BUILD_DIR)/.pkgdir/$(1))touch $$@
STAMP_BUILT定义在Build/CoreTargets,Build/CoreTargets实现如下:
include/package.mk:
define Build/CoreTargetsSTAMP_PREPARED:=$$(STAMP_PREPARED)STAMP_CONFIGURED:=$$(STAMP_CONFIGURED)$(if $(QUILT),$(Build/Quilt))$(call Build/Autoclean)$(call DefaultTargets)$(DL_DIR)/$(FILE): FORCEdownload:$(foreach hook,$(Hooks/Download),$(call $(hook))$(sep))$(STAMP_PREPARED) : export PATH=$$(TARGET_PATH_PKG)$(STAMP_PREPARED): $(STAMP_PREPARED_DEPENDS)@-rm -rf $(PKG_BUILD_DIR)@mkdir -p $(PKG_BUILD_DIR)touch $$@_check$(foreach hook,$(Hooks/Prepare/Pre),$(call $(hook))$(sep))$(Build/Prepare)$(foreach hook,$(Hooks/Prepare/Post),$(call $(hook))$(sep))touch $$@$(call Build/Exports,$(STAMP_CONFIGURED))$(STAMP_CONFIGURED): $(STAMP_PREPARED) $(STAMP_CONFIGURED_DEPENDS)rm -f $(STAMP_CONFIGURED_WILDCARD)$(CleanStaging)$(foreach hook,$(Hooks/Configure/Pre),$(call $(hook))$(sep))$(Build/Configure)$(foreach hook,$(Hooks/Configure/Post),$(call $(hook))$(sep))touch $$@$(call Build/Exports,$(STAMP_BUILT))$(STAMP_BUILT): $(STAMP_CONFIGURED) $(STAMP_BUILT_DEPENDS)rm -f $$@touch $$@_check$(foreach hook,$(Hooks/Compile/Pre),$(call $(hook))$(sep))$(Build/Compile)$(foreach hook,$(Hooks/Compile/Post),$(call $(hook))$(sep))$(Build/Install)$(foreach hook,$(Hooks/Install/Post),$(call $(hook))$(sep))touch $$@$(STAMP_INSTALLED) : export PATH=$$(TARGET_PATH_PKG)$(STAMP_INSTALLED): $(STAMP_BUILT)rm -rf $(TMP_DIR)/stage-$(PKG_DIR_NAME)mkdir -p $(TMP_DIR)/stage-$(PKG_DIR_NAME)/host $(STAGING_DIR)/packages$(foreach hook,$(Hooks/InstallDev/Pre),\$(call $(hook),$(TMP_DIR)/stage-$(PKG_DIR_NAME),$(TMP_DIR)/stage-$(PKG_DIR_NAME)/host)$(sep)\)$(call Build/InstallDev,$(TMP_DIR)/stage-$(PKG_DIR_NAME),$(TMP_DIR)/stage-$(PKG_DIR_NAME)/host)$(foreach hook,$(Hooks/InstallDev/Post),\$(call $(hook),$(TMP_DIR)/stage-$(PKG_DIR_NAME),$(TMP_DIR)/stage-$(PKG_DIR_NAME)/host)$(sep)\)if [ -f $(STAGING_DIR)/packages/$(STAGING_FILES_LIST) ]; then \$(SCRIPT_DIR)/clean-package.sh \"$(STAGING_DIR)/packages/$(STAGING_FILES_LIST)" \"$(STAGING_DIR)"; \fiif [ -d $(TMP_DIR)/stage-$(PKG_DIR_NAME) ]; then \(cd $(TMP_DIR)/stage-$(PKG_DIR_NAME); find ./ > $(TMP_DIR)/stage-$(PKG_DIR_NAME).files); \$(call locked, \mv $(TMP_DIR)/stage-$(PKG_DIR_NAME).files $(STAGING_DIR)/packages/$(STAGING_FILES_LIST) && \$(CP) $(TMP_DIR)/stage-$(PKG_DIR_NAME)/* $(STAGING_DIR)/; \,staging-dir); \firm -rf $(TMP_DIR)/stage-$(PKG_DIR_NAME)touch $$@ifdef Build/InstallDev$(_pkg_target)compile: $(STAMP_INSTALLED)endif$(_pkg_target)prepare: $(STAMP_PREPARED)$(_pkg_target)configure: $(STAMP_CONFIGURED)$(_pkg_target)dist: $(STAMP_CONFIGURED)$(_pkg_target)distcheck: $(STAMP_CONFIGURED)ifneq ($(CONFIG_AUTOREMOVE),)compile:-touch -r $(PKG_BUILD_DIR)/.built $(PKG_BUILD_DIR)/.autoremove 2>/dev/null >/dev/null$(FIND) $(PKG_BUILD_DIR) -mindepth 1 -maxdepth 1 -not '(' -type f -and -name '.*' -and -size 0 ')' -and -not -name '.pkgdir' | \$(XARGS) rm -rfendif
endefdefine Build/DefaultTargets$(if $(USE_SOURCE_DIR)$(USE_GIT_TREE)$(USE_GIT_SRC_CHECKOUT),,$(if $(strip $(PKG_SOURCE_URL)),$(call Download,default)))$(if $(DUMP),,$(Build/CoreTargets))define Build/DefaultTargetsendef
endefdefine BuildPackage
......$(if $(PKG_HOST_ONLY),,$(call Build/DefaultTargets,$(1)))
endef
其中
$(STAMP_BUILT): $(STAMP_CONFIGURED) $(STAMP_BUILT_DEPENDS)
rm -f $$@
touch $$@_check
$(foreach hook,$(Hooks/Compile/Pre),$(call $(hook))$(sep))
$(Build/Compile)
$(foreach hook,$(Hooks/Compile/Post),$(call $(hook))$(sep))
$(Build/Install)
$(foreach hook,$(Hooks/Install/Post),$(call $(hook))$(sep))
touch $$@
STAMP_BUILT依赖于STAMP_CONFIGURED和STAMP_BUILT_DEPENDS,STAMP_CONFIGURED实现如下:
$(STAMP_CONFIGURED): $(STAMP_PREPARED) $(STAMP_CONFIGURED_DEPENDS)
rm -f $(STAMP_CONFIGURED_WILDCARD)
$(CleanStaging)
$(foreach hook,$(Hooks/Configure/Pre),$(call $(hook))$(sep))
$(Build/Configure)
$(foreach hook,$(Hooks/Configure/Post),$(call $(hook))$(sep))
touch $$@
STAMP_CONFIGURED依赖于STAMP_PREPARED,STAMP_PREPARED实现如下:
(STAMP_PREPARED) : export PATH=$$(TARGET_PATH_PKG)
$(STAMP_PREPARED): $(STAMP_PREPARED_DEPENDS)
@-rm -rf $(PKG_BUILD_DIR)
@mkdir -p $(PKG_BUILD_DIR)
touch $$@_check
$(foreach hook,$(Hooks/Prepare/Pre),$(call $(hook))$(sep))
$(Build/Prepare)
$(foreach hook,$(Hooks/Prepare/Post),$(call $(hook))$(sep))
touch $$@
经过这几个步骤,kmod-*.ipkg的代码就编译好了。步骤分别是BUild/Prepare,CleanStaging,Build/Configure,Build/Compile和Build/Install。
在之后,执行如下代码
include/package-ipkg.mk:
$(PKG_BUILD_DIR)/.pkgdir/$(1).installed : export PATH=$$(TARGET_PATH_PKG)$(PKG_BUILD_DIR)/.pkgdir/$(1).installed: $(STAMP_BUILT)rm -rf $$@ $(PKG_BUILD_DIR)/.pkgdir/$(1)mkdir -p $(PKG_BUILD_DIR)/.pkgdir/$(1)$(call Package/$(1)/install,$(PKG_BUILD_DIR)/.pkgdir/$(1))$(call Package/$(1)/install_lib,$(PKG_BUILD_DIR)/.pkgdir/$(1))touch $$@
实际运行代码:
rm -rf /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug.installed /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug
mkdir -p /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug
mkdir -p /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug/etc/modules.d; ( echo "gpio-button-hotplug"; ) > /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug/etc/modules.d/30-gpio-button-hotplug; mkdir -p /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug/etc/modules-boot.d; ln -sf ../modules.d/30-gpio-button-hotplug /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug/etc/modules-boot.d/;
touch /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug.installed
创建build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug.installed文件。
$(STAGING_DIR_ROOT)/stamp/.$(1)_installed: $(PKG_BUILD_DIR)/.pkgdir/$(1).installedmkdir -p $(STAGING_DIR_ROOT)/stamp$(if $(ABI_VERSION),echo '$(ABI_VERSION)' | cmp -s - $(PKG_INFO_DIR)/$(1).version || { \echo '$(ABI_VERSION)' > $(PKG_INFO_DIR)/$(1).version; \$(foreach pkg,$(filter-out $(1),$(PROVIDES)), \cp $(PKG_INFO_DIR)/$(1).version $(PKG_INFO_DIR)/$(pkg).version; \) \} )$(call locked,$(CP) $(PKG_BUILD_DIR)/.pkgdir/$(1)/. $(STAGING_DIR_ROOT)/,root-copy)touch $$@
执行如下:
mkdir -p /root/mt7981/openwrt/staging_dir/target-aarch64_cortex-a53_musl/root-mediatek/stamp
SHELL= flock /root/mt7981/openwrt/tmp/.root-copy.flock -c 'cp -fpR /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/.pkgdir/kmod-gpio-button-hotplug/. /root/mt7981/openwrt/staging_dir/target-aarch64_cortex-a53_musl/root-mediatek/'
touch /root/mt7981/openwrt/staging_dir/target-aarch64_cortex-a53_musl/root-mediatek/stamp/.kmod-gpio-button-hotplug_installed
至此,第一个compile目标就分析完成了,接下来第二个compile主要是生成ipkg包。
3. 第二个compile分析
第二个compile代码如下:
include/package-ipkg.mk:
compile: $(PKG_INSTALL_STAMP).$(1)
$(PKG_INSTALL_STAMP).$(1)依赖于prepare-package-install,prepare-package-install代码如下:
include/package-ipkg.mk:
prepare-package-install: $$(IPKG_$(1))
$$(IPKG_$(1))的定义如下:
include/package-ipkg.mk:$$(IPKG_$(1)) : export CONTROL=$$(Package/$(1)/CONTROL)$$(IPKG_$(1)) : export DESCRIPTION=$$(Package/$(1)/description)$$(IPKG_$(1)) : export PATH=$$(TARGET_PATH_PKG)$$(IPKG_$(1)) : export PKG_SOURCE_DATE_EPOCH:=$(PKG_SOURCE_DATE_EPOCH)$(PKG_INFO_DIR)/$(1).provides $$(IPKG_$(1)): $(STAMP_BUILT) $(INCLUDE_DIR)/package-ipkg.mk@rm -rf $$(IDIR_$(1)); \$$(call remove_ipkg_files,$(1),$$(call opkg_package_files,$(call gen_ipkg_wildcard,$(1))))mkdir -p $(PACKAGE_DIR) $$(IDIR_$(1))/CONTROL $(PKG_INFO_DIR)$(call Package/$(1)/install,$$(IDIR_$(1)))$(if $(Package/$(1)/install-overlay),mkdir -p $(PACKAGE_DIR) $$(IDIR_$(1))/rootfs-overlay)$(call Package/$(1)/install-overlay,$$(IDIR_$(1))/rootfs-overlay)-find $$(IDIR_$(1)) -name 'CVS' -o -name '.svn' -o -name '.#*' -o -name '*~'| $(XARGS) rm -rf@( \find $$(IDIR_$(1)) -name lib\*.so\* -or -name \*.ko | awk -F/ '{ print $$$$NF }'; \for file in $$(patsubst %,$(PKG_INFO_DIR)/%.provides,$$(IDEPEND_$(1))); do \if [ -f "$$$$file" ]; then \cat $$$$file; \fi; \done; $(Package/$(1)/extra_provides) \) | sort -u > $(PKG_INFO_DIR)/$(1).provides$(if $(PROVIDES),@for pkg in $(filter-out $(1),$(PROVIDES)); do cp $(PKG_INFO_DIR)/$(1).provides $(PKG_INFO_DIR)/$$$$pkg.provides; done)$(CheckDependencies)$(RSTRIP) $$(IDIR_$(1))ifneq ($$(CONFIG_IPK_FILES_CHECKSUMS),)(cd $$(IDIR_$(1)); \( \find . -type f \! -path ./CONTROL/\* -exec mkhash sha256 -n \{\} \; 2> /dev/null | \sed 's|\([[:blank:]]\)\./| \1/|' > $$(IDIR_$(1))/CONTROL/files-sha256sum \) || true \)endif(cd $$(IDIR_$(1))/CONTROL; \( \echo "$$$$CONTROL"; \printf "Description: "; echo "$$$$DESCRIPTION" | sed -e 's,^[[:space:]]*, ,g'; \) > control; \chmod 644 control; \( \echo "#!/bin/sh"; \echo "[ \"\$$$${IPKG_NO_SCRIPT}\" = \"1\" ] && exit 0"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_postinst \$$$$0 \$$$$@"; \) > postinst; \( \echo "#!/bin/sh"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_prerm \$$$$0 \$$$$@"; \) > prerm; \chmod 0755 postinst prerm; \$($(1)_COMMANDS) \)ifneq ($$(KEEP_$(1)),)@( \keepfiles=""; \for x in $$(KEEP_$(1)); do \[ -f "$$(IDIR_$(1))/$$$$x" ] || keepfiles="$$$${keepfiles:+$$$$keepfiles }$$$$x"; \done; \[ -z "$$$$keepfiles" ] || { \mkdir -p $$(IDIR_$(1))/lib/upgrade/keep.d; \for x in $$$$keepfiles; do echo $$$$x >> $$(IDIR_$(1))/lib/upgrade/keep.d/$(1); done; \}; \)endif$(INSTALL_DIR) $$(PDIR_$(1))$(FAKEROOT) $(SCRIPT_DIR)/ipkg-build -m "$(FILE_MODES)" $$(IDIR_$(1)) $$(PDIR_$(1))@[ -f $$(IPKG_$(1)) ]
逐行代码分析,
@rm -rf $$(IDIR_$(1)); \$$(call remove_ipkg_files,$(1),$$(call opkg_package_files,$(call gen_ipkg_wildcard,$(1))))
这句指令实际执行命令如下:
rm -rf /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug;
删除ipkg目录。
mkdir -p $(PACKAGE_DIR) $$(IDIR_$(1))/CONTROL $(PKG_INFO_DIR)
实际执行命令如下:
mkdir -p /root/mt7981/openwrt/bin/targets/mediatek/mt7981/packages /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/CONTROL /root/mt7981/openwrt/staging_dir/target-aarch64_cortex-a53_musl/pkginfo
新建ipkg以及其下面的目录。
$(call Package/$(1)/install,$$(IDIR_$(1)))
实际执行命令如下:
mkdir -p /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/etc/modules.d; ( echo "gpio-button-hotplug"; ) > /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/etc/modules.d/30-gpio-button-hotplug; mkdir -p /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/etc/modules-boot.d; ln -sf ../modules.d/30-gpio-button-hotplug /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/etc/modules-boot.d/;
其上面的命令都在include/kernel.mk的Package/kmod-$(1)/install中的ModuleAutoLoad实现的。
-find $$(IDIR_$(1)) -name 'CVS' -o -name '.svn' -o -name '.#*' -o -name '*~'| $(XARGS) rm -rf@( \find $$(IDIR_$(1)) -name lib\*.so\* -or -name \*.ko | awk -F/ '{ print $$$$NF }'; \for file in $$(patsubst %,$(PKG_INFO_DIR)/%.provides,$$(IDEPEND_$(1))); do \if [ -f "$$$$file" ]; then \cat $$$$file; \fi; \done; $(Package/$(1)/extra_provides) \) | sort -u > $(PKG_INFO_DIR)/$(1).provides
实际执行命令如下:
find /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug -name 'CVS' -o -name '.svn' -o -name '.#*' -o -name '*~'| xargs -r rm -rf
删除掉ipkg目录下一些无关的文件。
$(RSTRIP) $$(IDIR_$(1))
实际执行命令如下:
export CROSS="aarch64-openwrt-linux-musl-" NO_RENAME=1 ; NM="aarch64-openwrt-linux-musl-nm" STRIP="/root/mt7981/openwrt/staging_dir/host/bin/sstrip -z" STRIP_KMOD="/root/mt7981/openwrt/scripts/strip-kmod.sh" PATCHELF="/root/mt7981/openwrt/staging_dir/host/bin/patchelf" /root/mt7981/openwrt/scripts/rstrip.sh /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug
rstrip.sh: /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/lib/modules/5.4.203/gpio-button-hotplug.ko: relocatable
去掉ipkg中文件的符号表。
(cd $$(IDIR_$(1))/CONTROL; \( \echo "$$$$CONTROL"; \printf "Description: "; echo "$$$$DESCRIPTION" | sed -e 's,^[[:space:]]*, ,g'; \) > control; \chmod 644 control; \( \echo "#!/bin/sh"; \echo "[ \"\$$$${IPKG_NO_SCRIPT}\" = \"1\" ] && exit 0"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_postinst \$$$$0 \$$$$@"; \) > postinst; \( \echo "#!/bin/sh"; \echo "[ -s "\$$$${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; \echo ". \$$$${IPKG_INSTROOT}/lib/functions.sh"; \echo "default_prerm \$$$$0 \$$$$@"; \) > prerm; \chmod 0755 postinst prerm; \$($(1)_COMMANDS) \)
实际执行命令如下:
(cd /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug/CONTROL; ( echo "$CONTROL"; printf "Description: "; echo "$DESCRIPTION" | sed -e 's,^[[:space:]]*, ,g'; ) > control; chmod 644 control; ( echo "#!/bin/sh"; echo "[ \"\${IPKG_NO_SCRIPT}\" = \"1\" ] && exit 0"; echo "[ -s "\${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; echo ". \${IPKG_INSTROOT}/lib/functions.sh"; echo "default_postinst \$0 \$@"; ) > postinst; ( echo "#!/bin/sh"; echo "[ -s "\${IPKG_INSTROOT}/lib/functions.sh" ] || exit 0"; echo ". \${IPKG_INSTROOT}/lib/functions.sh"; echo "default_prerm \$0 \$@"; ) > prerm; chmod 0755 postinst prerm; )
生成ipkg下的control,postinst和prerm文件。
$(INSTALL_DIR) $$(PDIR_$(1))
实际执行命令如下:
install -d -m0755 /root/mt7981/openwrt/bin/targets/mediatek/mt7981/packages
创建bin下的packages目录。
$(FAKEROOT) $(SCRIPT_DIR)/ipkg-build -m "$(FILE_MODES)" $$(IDIR_$(1)) $$(PDIR_$(1))
实际执行命令如下:
/root/mt7981/openwrt/staging_dir/host/bin/fakeroot /root/mt7981/openwrt/scripts/ipkg-build -m "" /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug /root/mt7981/openwrt/bin/targets/mediatek/mt7981/packages
Packaged contents of /root/mt7981/openwrt/build_dir/target-aarch64_cortex-a53_musl/linux-mediatek_mt7981/gpio-button-hotplug/ipkg-aarch64_cortex-a53/kmod-gpio-button-hotplug into /root/mt7981/openwrt/bin/targets/mediatek/mt7981/packages/kmod-gpio-button-hotplug_5.4.203-3_aarch64_cortex-a53.ipk
调用ipkg-build命令生成ipkg文件。
echo "$(1)" >> $(PKG_INSTALL_STAMP)
实际执行命令如下:
echo "kmod-gpio-button-hotplug" >> /root/mt7981/openwrt/staging_dir/target-aarch64_cortex-a53_musl/pkginfo/gpio-button-hotplug.default.install
三. 总结
1. kmod的ipkg和普通的应用程序的ipkg相同点
毫无疑问,它们都必须包含ipkg的基本元素,符合ipkg的规范,有CONTROL目录,CONTROL目录下有对应的control,postinst和prerm文件,还有其他的目录,例如内核驱动有etc和lib目录,应用程序有usr目录等等。
它们都要调用include/package.mk中的BuildPackage,再调用到include/package-ipkg.mk的BuildTarget/ipkg,BuildPackage的子功能定义编译ipkg源代码的一整套流程,Prepare,Configure,Compile和Install步骤,虽然两者区别是其中一个一些步骤会省略(Build/xxx为空),例如,kmod就不需要Configure。
2. kmod的ipkg和普通的应用程序的ipkg不同点
编译kmod和普通应用的代码方式不一样。这个就体现在Build/Compile中,kmod的Makefile需要在自己的Makefile中定义这个宏,表示如何编译kmod,而应用代码Build/Compile是不推荐自定义的。
安装的方式不一样。kmod需要etc/modules.d/这样的目录,而应用程序不需要,在kmod的安装中,include/package-ipkg.mk中$(call Package/$(1)/install,$$(IDIR_$(1)))会调用到include/kernel.mk中的Package/kmod-$(1)/install,而如果再有需要,由于KernelPackage/$(1)/install定义在Package/kmod-$(1)/install,kmod的Makefile可以自定义KernelPackage/$(1)/install,做一些特殊处理。但是普通应用程序要在自己的Makefile中定义Package/$(1)/install,做一些自己需要的操作。