OpenWrt KernelPackage分析

一. 前言

        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.mk

PKG_NAME:=gpio-button-hotplug
PKG_RELEASE:=3
PKG_LICENSE:=GPL-2.0

include $(INCLUDE_DIR)/package.mk

define 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 KernelPackage
  NAME:=$(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:=kernel
    CATEGORY:=Kernel modules
    DESCRIPTION:=$(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))
  endef

  ifdef KernelPackage/$(1)/conffiles
    define Package/kmod-$(1)/conffiles
$(call KernelPackage/$(1)/conffiles)
    endef
  endif

  ifdef KernelPackage/$(1)/description
    define Package/kmod-$(1)/description
$(call KernelPackage/$(1)/description)
    endef
  endif

  ifdef KernelPackage/$(1)/config
    define Package/kmod-$(1)/config
$(call KernelPackage/$(1)/config)
    endef
  endif

  $(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)),
    else
      compile: $(1)-disabled
      $(1)-disabled:
		@echo "WARNING: kmod-$(1) is not available in the kernel config - generating empty package" >&2

      define Package/kmod-$(1)/install
		echo "<<1>>"
		true
      endef
  )
  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)
endif

ifndef Package/$(1)/description
define Package/$(1)/description
	$(TITLE)
endef
endif

  BUILD_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/ipkg
    ABIV_$(1):=$(call FormatABISuffix,$(1),$(ABI_VERSION))
    PDIR_$(1):=$(call FeedPackageDir,$(1))
    IPKG_$(1):=$$(PDIR_$(1))/$(1)$$(ABIV_$(1))_$(VERSION)_$(PKGARCH).ipk
    IDIR_$(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)/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

    DEPENDS:=$(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).installed
	mkdir -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)-clean

  endef

        里面一段一段分析,如下:

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: act2

all: compile
        @echo all

result:
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).installed
	mkdir -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/CoreTargets
  STAMP_PREPARED:=$$(STAMP_PREPARED)
  STAMP_CONFIGURED:=$$(STAMP_CONFIGURED)

  $(if $(QUILT),$(Build/Quilt))
  $(call Build/Autoclean)
  $(call DefaultTargets)

  $(DL_DIR)/$(FILE): FORCE

  download:
	$(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)"; \
	fi
	if [ -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); \
	fi
	rm -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 -rf
  endif
endef


define 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/DefaultTargets
  endef
endef

define 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).installed
	mkdir -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,做一些自己需要的操作。

你可能感兴趣的:(openwrt,Makefile,linux,openwrt)