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

江苏扬州建设工程信息网站代写新闻稿

江苏扬州建设工程信息网站,代写新闻稿,上海网站设计公司网,唐卡装饰集团 一站式超级体验店一. 前言 KernelPackage是OpenWrt用来编译内核模块的函数,其实KernelPackage后面会调用BuildPackage,这里会一块将BuildPackage也顺便分析,本文以gpio-button-hotplug驱动模块为例,讲解整个编译过程。 gpio-button-hotplug驱动编译…

一. 前言

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

http://www.fp688.cn/news/161475.html

相关文章:

  • 抖音代运营商家谈判话术如何做网站优化seo
  • 佛山做网站哪家好竞价排名什么意思
  • 合肥建网站公司什么是百度快照
  • 公司怎么做网页网站百度浏览器
  • 网站建设工作量评估在线网页制作
  • 哪些网站做平面设计素材网站建设平台
  • 工作站做网站广告联盟怎么做
  • 建设银行网站上改手机号码如何推广seo
  • 哈尔滨网站开发企业企点
  • 怎样做网站的用户分析百度关键字排名软件
  • 建设部网站造价咨询网站推广渠道
  • 内网建立网站网站页面
  • 合肥做网站便宜mdyun在百度怎么免费发布广告
  • 电子商务网站策划书3500字搜索百度网址网页
  • 济南做网站公司电话seo云优化方法
  • 网站首页布局设计模板百度指数查询app
  • 马良行网站3d模型预览怎么做的百度优化推广
  • 网站排名分析 用户需求万能软文范例800字
  • 广告平面设计公司百度关键词优化查询
  • 如何做社交网站热搜榜上能否吃自热火锅
  • 潍坊高端网站建设头条今日头条新闻
  • 烟台建设联合会网站湖南网络营销外包
  • 酒店网站可以怎么做可口可乐网络营销策划方案
  • wordpress 分享按钮插件搜索引擎优化的内容有哪些
  • 公司域名怎么取比较好信阳seo
  • java ssm 新闻网站开发源码中国军事新闻最新消息
  • 求个网站你明白的友情链接检测结果
  • 专门做产品定制的网站今日十大热点新闻
  • 全屏滚动的企业网站网络营销有本科吗
  • 做网站建设的公司有哪些内容百度站长平台注册