当前位置: 首页 > news >正文

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

相关文章:

OpenWrt KernelPackage分析

一. 前言 KernelPackage是OpenWrt用来编译内核模块的函数&#xff0c;其实KernelPackage后面会调用BuildPackage&#xff0c;这里会一块将BuildPackage也顺便分析&#xff0c;本文以gpio-button-hotplug驱动模块为例&#xff0c;讲解整个编译过程。 gpio-button-hotplug驱动编译…...

第 363 场 LeetCode 周赛题解

A 计算 K 置位下标对应元素的和 模拟 class Solution { public:int pop_cnt(int x) {//求x的二进制表示中的1的位数int res 0;for (; x; x >> 1)if (x & 1)res;return res;}int sumIndicesWithKSetBits(vector<int> &nums, int k) {int res 0;for (int i…...

ffplay源码解析-main入口函数

main入口函数 初始化 变量、缓存区、SDL窗口初始化等 int main(int argc, char **argv) {int flags;VideoState *is; // av_log_set_level(AV_LOG_TRACE);init_dynload();av_log_set_flags(AV_LOG_SKIP_REPEATED);parse_loglevel(argc, argv, options);/// av_log_set_le…...

这些Coding套路你不会还不知道吧?

对于一名程序员来说&#xff0c;编码进阶是成为优秀工程师非常重要的一步&#xff0c;它可以让我们更加熟练地掌握编程&#xff0c;深入理解数据结构和算法&#xff0c;从而更好地完成复杂的任务&#xff0c;提高工作效率。而我认为熟练使用设计模式就是编码进阶的最好方式之一…...

Spring Boot深度解析:快速开发的秘密

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…...

mysql数据库备份(mysqldump)

mysqldump命令备份数据 mysqldump -u root -p --databases 数据库1 数据库2 > xxx.sqlmysqldump常用操作示例 1. 备份全部数据库的数据和结构 mysqldump -uroot -p123456 -A > /data/mysqlbackup/mydb.sql2. 备份全部数据库的结构&#xff08;加 -d 参数&#xff09; …...

linux Nginx+Tomcat负载均衡、动静分离

linux NginxTomcat负载均衡、动静分离 1、Tomcat的基本介绍1.1Tomcat是什么&#xff1f;1.2Tomcat的构成组件1.3Tomcat的核心功能1.4Tomcat请求过程 2、Tomcat部署2.1安装tomcat2.2优化tomcat启动速度2.4主要目录说明 3、Tomcat 虚拟主机配置3.1创建fsj和mws项目目录和文件3.2修…...

ts 枚举类型原理及其应用详解

ts 枚举类型介绍 TypeScript的枚举类型是一种特殊的数据类型&#xff0c;它允许开发者为一组相关值定义一个共同的名称&#xff0c;使我们可以更清晰、更一致地使用这些值。 枚举类型在TypeScript中用enum关键字定义&#xff0c;每个枚举值默认都是数字类型&#xff0c;从0开…...

腾讯mini项目-【指标监控服务重构】2023-08-23

今日已办 进度和问题汇总 请求合并 feature/venus tracefeature/venus metricfeature/profile-otel-baserunner-stylebugfix/profile-logger-Syncfeature/profile_otelclient_enable_config 完成otel 开关 trace-采样metrice-reader 已经都在各自服务器运行&#xff0c;并接入…...

C- ssize_t size_t

size_t 和 ssize_t 都是在 C 和 C 的标准库中定义的数据类型&#xff0c;它们通常用于表示大小和长度。然而&#xff0c;它们有关键的区别。 size_t: 定义&#xff1a;size_t 是一个无符号整数类型&#xff0c;它是适合表示对象的大小的类型。在 POSIX 中&#xff0c;它也用于…...

ubuntu20.04 Supervisor 开机自启动脚本一文配置

前言: 最近发现一种非常好的开机启动服务方式,不光可以开机自启动,而且还可以进行开机节点的进程守护,这样大大确保了线程的稳定情况,这种服务甚至可以守护开机的进程,所以比之前设置 rc.local 开机自启动脚本一文配置节点好出很多,它甚至可以使用网页登录监管我开机自启…...

【面试刷题】——函数指针和指针函数

“函数指针”&#xff08;function pointer&#xff09;和 “指针函数”&#xff08;pointer to function&#xff09;是两个不同的概念&#xff0c;它们涉及到指针和函数的结合使用。 函数指针&#xff08;Function Pointer&#xff09;&#xff1a; 函数指针是指向函数的指…...

目标分类笔记(一): 利用包含多个网络多种训练策略的框架来完成多目标分类任务(从数据准备到训练测试部署的完整流程)

目标分类 一、目标分类介绍1.1 二分类和多分类的区别1.2 单标签和多标签输出的区别 二、代码获取三、数据集准备四、环境搭建4.1 环境测试 五、模型训练六、模型测试6.1 多标签训练-单标签输出结果6.2 多标签训练-多标签输出结果 一、目标分类介绍 目标分类是一种监督学习任务…...

【100天精通Python】Day61:Python 数据分析_Pandas可视化功能:绘制饼图,箱线图,散点图,散点图矩阵,热力图,面积图等(示例+代码)

目录 1 Pandas 可视化功能 2 Pandas绘图实例 2.1 绘制线图 2.2 绘制柱状图 2.3 绘制随机散点图 2.4 绘制饼图 2.5 绘制箱线图A 2.6 绘制箱线图B 2.7 绘制散点图矩阵 2.8 绘制面积图 2.9 绘制热力图 2.10 绘制核密度估计图 1 Pandas 可视化功能 pandas是一个强大的数…...

2023华为产品测评官-开发者之声 | 华为云CodeArts征文活动,多重好礼邀您发声!

"2023华为产品测评官&#xff0d;开发者之声"活动激发了众多开发者和技术爱好者的热情&#xff0c;他们纷纷递交了精心编写的产品测评报告。活动社群充满活力&#xff0c;参与者们热衷于交流讨论&#xff0c;互相帮助解决问题&#xff0c;一起探索云技术的无限可能。…...

Python 图形化界面基础篇:获取文本框中的用户输入

Python 图形化界面基础篇&#xff1a;获取文本框中的用户输入 引言 Tkinter 库简介步骤1&#xff1a;导入 Tkinter 模块步骤2&#xff1a;创建 Tkinter 窗口步骤3&#xff1a;创建文本框步骤4&#xff1a;获取文本框中的用户输入步骤5&#xff1a;启动 Tkinter 主事件循环 完整…...

【驱动开发】实现三盏灯的控制,编写应用程序测试

head.h #ifndef __HEAD_H__ #define __HEAD_H__//LED1:PE10 //LED2:PF10 //LED3:PE8#define LED_RCC 0X50000A28 //使能GPIO#define LED_MODER 0X50006000 //设置输出模式 #define LED_ODR 0X50006014 //设置输出高低电平#define LED2_MODER 0X50007000 …...

Vue3+ElementUI使用

<!DOCTYPE html> <html> <head><meta charset"UTF-8"><meta name"viewport" content"initial-scale1.0,maximum-scale1.0,minimum-scale1.0,user-scalable0, widthdevice-width"/><!-- 引入样式 --><lin…...

MySQL 和 MariaDB 版本管理的历史背景及差异

目录 MariaDB MySQL 差异 关于 SQLE SQLE 获取 ​ 了解更多 需要说明的是 MySQL 和 MariaDB 都有社区版和企业版。对于 MySQL&#xff0c;这两个版本都是由同一家公司&#xff08;Oracle&#xff09;提供&#xff0c;遵循相同的版本编号体系&#xff0c;企业版包含更丰富…...

linux驱动开发--day4(字符设备驱动注册内部流程、及实现备文件和设备的绑定下LED灯实验)

一、字符设备驱动注册的内部过程 1.分配struct cdev对象空间 2.初始化struct cdev对象 3.注册cdev对象 二、注册字符设备驱动分步实现 1.分配字符设备驱动对象 2.字符设备驱动对象初始化 3.设备号的申请 4.根据申请的设备号和驱动对象注册驱动 三、open函数回调驱动中…...

elasticsearch5-RestAPI操作

个人名片&#xff1a; 博主&#xff1a;酒徒ᝰ. 个人简介&#xff1a;沉醉在酒中&#xff0c;借着一股酒劲&#xff0c;去拼搏一个未来。 本篇励志&#xff1a;三人行&#xff0c;必有我师焉。 本项目基于B站黑马程序员Java《SpringCloud微服务技术栈》&#xff0c;SpringCloud…...

数据结构与算法(一)

文章目录 数据结构与算法(一)1 位运算、算法是什么、简单排序1.1 实现打印一个整数的二进制1.2 给定一个参数N,返回1!+2!+3!+4!+...+N!的结果1.3 简单排序算法2 数据结构大分类、前缀和、对数器2.1 实现前缀和数组2.2 如何用1\~5的随机函数加工出1\~7的随机函数2.3 如何把不…...

Matlab--微积分问题的计算机求解

目录 1.单变量函数的极限问题 1.1.公式例子 1.2.对应例题 1 2.多变量函数的极限问题 3.函数导数的解析解 4.多元函数的偏导数 5.Jacobian函数 6.Hessian矩阵 7.隐函数的偏导 8.不定积分问题的求解 9.定积分的求解问题 10. 多重积分的问题求解 1.单变量函数的极限问题 …...

GRU实现时间序列预测(PyTorch版)

&#x1f4a5;项目专栏&#xff1a;【深度学习时间序列预测案例】零基础入门经典深度学习时间序列预测项目实战&#xff08;附代码数据集原理介绍&#xff09; 文章目录 前言一、基于PyTorch搭建GRU模型实现风速时间序列预测二、时序数据集的制作三、数据归一化四、数据集加载器…...

文本框粘贴时兼容Unix、Mac换行符的方法源码

本篇文章属于《518抽奖软件开发日志》系列文章的一部分。 我在开发《518抽奖软件》&#xff08;www.518cj.net&#xff09;的时候&#xff0c;要在文本框粘贴从别处复制来的名单。发现一个问题&#xff0c;就是一些Unix传过来的多行文本&#xff0c;粘贴后都变成了一行。原来&a…...

2023年华为杯研究生数学建模竞赛辅导

2023年华为杯研究生数学建模竞赛辅导 各研究生培养单位&#xff1a; 中国研究生数学建模竞赛作为教育部学位管理与研究生教育司指导&#xff0c;中国学位与研究生教育学会、中国科协青少年科技中心主办的“中国研究生创新实践系列大赛”主题赛事之一&#xff0c;是一项面向在校…...

post更新,put相当于删除重新增一条

索引数据 //删除后新增 PUT my_dynamic_temp/_doc/1 { “name”:“test”, “class”:“1204” } //覆盖更新 POST my_dynamic_temp/_update/1 { “doc”: { “name”:“test”, “class”:“1203”, “pernum”:“998” } }...

python责任链模式

责任链模式是一种行为设计模式&#xff0c;它允许你将请求沿着处理者链进行传递&#xff0c;直到有一个处理者能够处理它为止。在Python中&#xff0c;你可以使用多线程来实现责任链模式的框架。 首先&#xff0c;你需要定义一个基础的处理者类&#xff0c;它包含处理请求的方…...

大数据技术准备

Hbase&#xff1a;HBase 底层原理详解&#xff08;深度好文&#xff0c;建议收藏&#xff09; - 腾讯云开发者社区-腾讯云 Hbase架构图 同一个列族如果有多个store&#xff0c;那么这些store在不同的region Hbase写流程&#xff08;读比写慢&#xff09; MemStore Flush Hbas…...

【力扣周赛】第 362 场周赛(⭐差分匹配状态压缩DP矩阵快速幂优化DPKMP)

文章目录 竞赛链接Q1&#xff1a;2848. 与车相交的点解法1——排序后枚举解法2——差分数组⭐差分数组相关题目列表&#x1f4d5;1094. 拼车1109. 航班预订统计2381. 字母移位 II2406. 将区间分为最少组数解法1——排序贪心优先队列解法2——差分数组 2772. 使数组中的所有元素…...