openwrt: Makefile 框架分析

2023-11-12

本篇的主要目的是想通过分析Makefile,了解openwrt编译过程。着重关注以下几点:

  1. openwrt目录结构
  2. 主Makefile的解析过程,各子目录的目标生成。
  3. kernel编译过程
  4. firmware的生成过程
  5. 软件包的编译过程

openwrt目录结构

官方源下载速度太度,我从github上clone了openwrt的代码仓库。

git clone https://github.com/openwrt-mirror/openwrt.git

openwrt目录结构

上图是openwrt目录结构,其中第一行是原始目录,第二行是编译过程中生成的目录。各目录的作用是:

  • tools - 编译时需要一些工具, tools里包含了获取和编译这些工具的命令。里面是一些Makefile,有的可能还有patch。每个Makefile里都有一句 $(eval $(call HostBuild)),表示编译这个工具是为了在主机上使用的。
  • toolchain - 包含一些命令去获取kernel headers, C library, bin-utils, compiler, debugger
  • target - 各平台在这个目录里定义了firmware和kernel的编译过程。
  • package - 包含针对各个软件包的Makefile。openwrt定义了一套Makefile模板,各软件参照这个模板定义了自己的信息,如软件包的版本、下载地址、编译方式、安装地址等。
  • include - openwrt的Makefile都存放在这里。
  • scripts - 一些perl脚本,用于软件包管理。

  • dl - 软件包下载后都放到这个目录里
  • build_dir - 软件包都解压到build_dir/里,然后在此编译
  • staging_dir - 最终安装目录。tools, toolchain被安装到这里,rootfs也会放到这里。
  • feeds -
  • bin - 编译完成之后,firmware和各ipk会放到此目录下。

OpenWrt Development Guide

main Makefile

openwrt根目录下的Makefile是执行make命令时的入口。从这里开始分析。

world:

ifndef ($(OPENWRT_BUILD),1)
  # 第一个逻辑
   ...
else
  # 第二个逻辑
   ...
endif

上面这段是主Makefile的结构,可以得知:

  1. 执行make时,若无任何目标指定,则默认目标是world
  2. 执行make时,无参数指定,则会进入第一个逻辑。如果执行命令make OPENWRT_BUILD=1,则直接进入第二个逻辑。

编译时一般直接使用make V=s -j5这样的命令,不会指定OPENWRT_BUILD变量

第一个逻辑

  override OPENWRT_BUILD=1
  export OPENWRT_BUILD

更改了OPENWRT_BUILD变量的值。这里起到的作用是下次执行make时,会进入到第二逻辑中。

toplevel.mk中的 %:: 解释world目标的规则。

prereq:: prepare-tmpinfo .config
    @+$(MAKE) -r -s tmp/.prereq-build $(PREP_MK)
    @+$(NO_TRACE_MAKE) -r -s $@

%::
    @+$(PREP_MK) $(NO_TRACE_MAKE) -r -s prereq
    @( \
        cp .config tmp/.config; \
        ./scripts/config/conf --defconfig=tmp/.config -w tmp/.config Config.in > /dev/null 2>&1; \
        if ./scripts/kconfig.pl '>' .config tmp/.config | grep -q CONFIG; then \
            printf "$(_R)WARNING: your configuration is out of sync. Please run make menuconfig, oldconfig or defconfig!$(_N)\n" >&2; \
        fi \
    )
    @+$(ULIMIT_FIX) $(SUBMAKE) -r $@

执行 make V=s 时,上面这段规则简化为:

prereq:: prepare-tmpinfo .config
    @make -r -s tmp/.prereq-build
    @make V=ss -r -s prereq

%::
    @make V=s -r -s prereq
    @make -w -r world

可见其中最终又执行了prereq和world目标,这两个目标都会进入到第二逻辑中。

第二逻辑

首先就引入了target, package, tools, toolchain这四个关键目录里的Makefile文件

  include target/Makefile
  include package/Makefile
  include tools/Makefile
  include toolchain/Makefile

这些子目录里的Makefile使用include/subdir.mk里定义的两个函数来动态生成规则,这两个函数是subdir和stampfile

stampfile

拿target/Makefile举例:

$(eval \((call stampfile,\)(curdir),target,prereq,.config))

会生成规则:

  target/stamp-prereq:=$(STAGING_DIR)/stamp/.target_prereq

  $$(target/stamp-prereq): $(TMP_DIR)/.build .config
    @+$(SCRIPT_DIR)/timestamp.pl -n $$(target/stamp-prereq) target .config || \
        make $$(target/flags-prereq) target/prereq
    @mkdir -p $$$$(dirname $$(target/stamp-prereq))
    @touch $$(target/stamp-prereq)

  $$(if $(call debug,target,v),,.SILENT: $$(target/stamp-prereq))

  .PRECIOUS: $$(target/stamp-prereq) # work around a make bug

  target//clean:=target/stamp-prereq/clean
  target/stamp-prereq/clean: FORCE
    @rm -f $$(target/stamp-prereq)

所以可以简单的看作: $(eval \((call stampfile,\)(curdir),target,prereq,.config)) 生成了目标 $(target/stamp-prereq)

  • 对于target分别生成了:$(target/stamp-preq), $(target/stamp-copile), $(target/stamp-install)
  • toolchain : $(toolchain/stamp-install)
  • package : \((package/stamp-preq),\)(package/stamp-cleanup), \((package/stamp-compile),\)(package/stamp-install)
  • tools : $(tools/stamp-install)

OpenWrt的主Makefile工作过程

subdir

subdir这个函数写了一大堆东西,看起来很复杂 。

$(call subdir, target) 会遍历下的子目录,执行 make -C 操作。这样就切入子目录中去了。

目录变量

几个重要的目录路径:

  • KERNEL_BUILD_DIR

    build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/linux-3.14.18

  • LINUX_DIR

    build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/linux-3.14.18

  • KDIR

    build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a

  • BIN_DIR

    bin/ramips
    Makefile中包含了rules.mk, target.mk等.mk文件,这些文件中定义了许多变量,有些是路径相关的,有些是软件相关的。这些变量在整个Makefile工程中经常被用到,

  • TARGET_ROOTFS_DIR

    build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2

  • BUILD_DIR

    build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2

  • STAGING_DIR_HOST

    staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2

  • TARGET_DIR

    build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/root-ramips

kernel 编译:

target/linux/ramips/Makefile: $(eval $(call BuildTarget))
target/linux/Makefile : export TARGET_BUILD=1
include/target.mk:

ifeq ($(TARGET_BUILD),1)
  include $(INCLUDE_DIR)/kernel-build.mk
  BuildTarget?=$(BuildKernel)
endif

BuildKernel是include/kernel-build.mk定义的一个多行变量,其中描述了如何编译内核, 主要关注其中install规则的依赖链:

  $(KERNEL_BUILD_DIR)/symtab.h: FORCE
    rm -f $(KERNEL_BUILD_DIR)/symtab.h
    touch $(KERNEL_BUILD_DIR)/symtab.h
    +$(MAKE) $(KERNEL_MAKEOPTS) vmlinux
    ...

  $(LINUX_DIR)/.image: $(STAMP_CONFIGURED) $(if $(CONFIG_STRIP_KERNEL_EXPORTS),$(KERNEL_BUILD_DIR)/symtab.h) FORCE
    $(Kernel/CompileImage)
    $(Kernel/CollectDebug)
    touch $$@


  install: $(LINUX_DIR)/.image
    +$(MAKE) -C image compile install TARGET_BUILD=
1. 触发make vmlinux命令生成vmlinux: install --> $(LINUX_DIR)/.image --> $(KERNEL_BUILD_DIR)/symtab.h --> `$(MAKE) $(KERNEL_MAKEOPTS) vmlinux`

2. 对vmlinux做objcopy, strip操作: $(LINUX_DIR)/.image --> $(Kernel/CompileImage) --> $(call Kernel/CompileImage/Default) --> $(call Kernel/CompileImage/Default)

    $(KERNEL_CROSS)objcopy -O binary $(OBJCOPY_STRIP) -S $(LINUX_DIR)/vmlinux $(LINUX_KERNEL)$(1)
        --> build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/vmlinux

    $(KERNEL_CROSS)objcopy $(OBJCOPY_STRIP) -S $(LINUX_DIR)/vmlinux $(KERNEL_BUILD_DIR)/vmlinux$(1).elf
        --> build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/vmlinux.elf

    $(CP) $(LINUX_DIR)/vmlinux $(KERNEL_BUILD_DIR)/vmlinux.debug
        --> build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/vmlinux.debug

生成firmware

firmware由kernel和rootfs两个部分组成,要对两个部分先分别处理,然后再合并成一个.bin文件。先看一下这个流程。

"target/linux/ramips/image/Makefile" 文件中的最后一句:$(eval $(call BuildImage)),将BuildImage展开在这里。BuildImage定义在 include/image.mk 文件中,其中定义了数个目标的规则。

define BuildImage

    compile: compile-targets FORCE
        **$(call Build/Compile)**

    install: compile install-targets FORCE
        ...
        $(call Image/BuildKernel) ## 处理vmlinux
        ...
        $(call Image/mkfs/squashfs) ## 生成squashfs,并与vmlinux合并成一个.bin文件
        ...

endef

处理vmlinux: Image/BuildKernel

target/linux/ramips/image/Makefile:

define Image/BuildKernel
    cp $(KDIR)/vmlinux.elf $(BIN_DIR)/$(VMLINUX).elf
    cp $(KDIR)/vmlinux $(BIN_DIR)/$(VMLINUX).bin
    $(call CompressLzma,$(KDIR)/vmlinux,$(KDIR)/vmlinux.bin.lzma)
    $(call MkImage,lzma,$(KDIR)/vmlinux.bin.lzma,$(KDIR)/uImage.lzma)
    cp $(KDIR)/uImage.lzma $(BIN_DIR)/$(UIMAGE).bin
ifneq ($(CONFIG_TARGET_ROOTFS_INITRAMFS),)
    cp $(KDIR)/vmlinux-initramfs.elf $(BIN_DIR)/$(VMLINUX)-initramfs.elf
    cp $(KDIR)/vmlinux-initramfs $(BIN_DIR)/$(VMLINUX)-initramfs.bin
    $(call CompressLzma,$(KDIR)/vmlinux-initramfs,$(KDIR)/vmlinux-initramfs.bin.lzma)
    $(call MkImage,lzma,$(KDIR)/vmlinux-initramfs.bin.lzma,$(KDIR)/uImage-initramfs.lzma)
    cp $(KDIR)/uImage-initramfs.lzma $(BIN_DIR)/$(UIMAGE)-initramfs.bin
endif
    $(call Image/Build/Initramfs)
endef
lzma压缩内核

build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/ 目录中:

lzma e vmlinux -lc1 -lp2 -pb2 vmlinux.bin.lzma
MkImage

build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/ 目录中:

mkimage -A mips -O linux -T  kernel -C lzma -a 0x80000000 -e 0x80000000 -n "MIPS OpenWrt Linux-3.14.18" -d vmlinux.bin.lzma uImage.lzma
copy
VMLINUX:=$(IMG_PREFIX)-vmlinux --> openwrt-ramips-mt7620a-vmlinux
UIMAGE:=$(IMG_PREFIX)-uImage --> openwrt-ramips-mt7620a-uImage
cp $(KDIR)/uImage.lzma $(BIN_DIR)/$(UIMAGE).bin

把uImage.lzma复制到bin/ramips/目录下:
cp $(KDIR)/uImage.lzma bin/ramips/openwrt-ramips-mt7620a-uImage

制作squashfs,生成.bin: $(call Image/mkfs/squashfs)

    define Image/mkfs/squashfs
        @mkdir -p $(TARGET_DIR)/overlay
        $(STAGING_DIR_HOST)/bin/mksquashfs4 $(TARGET_DIR) $(KDIR)/root.squashfs -nopad -noappend -root-owned -comp $(SQUASHFSCOMP) $(SQUASHFSOPT) -processors $(if $(CONFIG_PKG_BUILD_JOBS),$(CONFIG_PKG_BUILD_JOBS),1)
        $(call Image/Build,squashfs)
endif
mkdir -p $(TARGET_DIR)/overlay

mkdir -p build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/root-ramips/overlay

mksquashfs4
$(STAGING_DIR_HOST)/bin/mksquashfs4 $(TARGET_DIR) $(KDIR)/root.squashfs -nopad -noappend -root-owned -comp $(SQUASHFSCOMP) $(SQUASHFSOPT) -processors $(if $(CONFIG_PKG_BUILD_JOBS),$(CONFIG_PKG_BUILD_JOBS),1)

制作squashfs文件系统,生成root.squashfs:

mksquashfs4 root-ramips root.squashfs -nopad -noappend -root-owned -comp gzip -b 256k -p '/dev d 755 0 0' -p '/dev/console c 600 0 0 5 1' -processors 1
$(call Image/Build,squashfs)

在 target/linux/ramips/image/Makefile 中:

define Image/Build
    $(call Image/Build/$(1))
    dd if=$(KDIR)/root.$(1) of=$(BIN_DIR)/$(IMG_PREFIX)-root.$(1) bs=128k conv=sync
    $(call Image/Build/Profile/$(PROFILE),$(1))
endef
  • dd if=\((KDIR)/root.squashfs of=\)(BIN_DIR)/$(IMG_PREFIX)-root.squashfs bs=128k conv=sync

dd if=build_dir/target-mipsel_24kec+dsp_uClibc-0.9.33.2/linux-ramips_mt7620a/root.squashfs of=bin/ramips/openwrt-ramips-mt7620-root.squashfs bs=128k conv=sync

  • \((call Image/Build/Profile/\)(PROFILE),squashfs)

target/linux/ramips/mt7620a/profiles/00-default.mk, 中调用 Profile 函数:$(eval $(call Profile,Default))

include/target.mk 中定义了 Profile 函数, 其中令 PROFILE=Default

define Image/Build/Profile/Default
    $(call Image/Build/Profile/MT7620a,$(1))
    ...
endef

规则依赖序列如下:

$(call Image/Build/Profile/$(PROFILE),squashfs)
  --> $(call BuildFirmware/Default8M/squashfs,squashfs,mt7620a,MT7620a)
      --> $(call BuildFirmware/OF,squashfs,mt7620a,MT7620a,8060928)
          --> $(call MkImageLzmaDtb,mt7620a,MT7620a)
              --> $(call PatchKernelLzmaDtb,mt7620a,MT7620a)
              --> $(call MkImage,lzma,$(KDIR)/vmlinux-mt7620a.bin.lzma,$(KDIR)/vmlinux-mt7620a.uImage)
      --> $(call MkImageSysupgrade/squashfs,squashfs,mt7620a,8060928)

其中的主要步骤:

  • 复制: cp $(KDIR)/vmlinux $(KDIR)/vmlinux-mt7620a
  • 生成dtb文件: $(LINUX_DIR)/scripts/dtc/dtc -O dtb -o $(KDIR)/MT7620a.dtb ../dts/MT7620a.dts
  • 将内核与dtb文件合并:$(STAGING_DIR_HOST)/bin/patch-dtb $(KDIR)/vmlinux-mt7620a $(KDIR)/MT7620a.dtb
  • 使用lzma压缩:\((call CompressLzma,\)(KDIR)/vmlinux-mt7620a,$(KDIR)/vmlinux-mt7620a.bin.lzma)
  • 将lzma压缩后的文件经过mkimage工具处理,即在头部添加uboot可识别的信息。

接下来就是合并生成firmware固件了:

MkImageSysupgrade/squashfs, squashfs, mt7620a,8060928

cat vmlinux-mt7620a.uImage root.squashfs > openwrt-ramips-mt7620-mt7620a-squashfs-sysupgrade.bin
--> 制作squashfs bin文档, 并确认它的大小 < 8060928 才是有效的,否则报错。


总结: 整个流程下来,其实最烦索的还是对内核生成文件vmlinux的操作,经过了objcopy, patch-dtb, lzma, mkimage 等过程生成一个uImage,再与mksquashfs工具制作的文件系统rootfs.squashfs合并。

转载于:https://www.cnblogs.com/sammei/p/3968916.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

openwrt: Makefile 框架分析 的相关文章

  • 如何使用 AWS Lambda 安装 Git?

    我在代码提交存储库中有代码 我正在编写一个 lambda 函数来为代码提交存储库的每个签入 事件 构建代码 我无法安装 git 因此无法克隆存储库 我该怎么办呢 正如其他人提到的 在 lambda 上安装 git 要么非常困难 要么完全不可
  • Git 提交失败:“请使用 -m 或 -F 选项提供消息。”

    当我键入 git commit 命令来提交文件时 我收到以下错误消息 Microsoft Visual Studio 微软 找不到命令 错误 核心编辑器 Microsoft Visual Studio 存在问题 请使用 m 或 F 选项提供
  • 如何使用 Git 跟踪目录而不是文件?

    我最近开始使用 Git 但只有一件事遇到了麻烦 如何在不跟踪目录内容的情况下跟踪目录 例如 我正在开发的网站允许上传 我想跟踪上传目录 以便在分支等时创建它 但显然不是其中的文件 在开发分支中的测试文件或主控中的真实文件 在我的 gitig
  • Git 到 TFS 源代码管理迁移

    我想看看 TFS 如何为我的命令工作 所以我想将我们当前的 GIT 存储库移动到 TFS 数据库 我们使用 GIT 来获得普遍的分支支持 因此我想使用 TFS 2010 来解决该问题 现在的问题是 如何将 GIT 存储库导出到 TFS 显然
  • 如何让 Aptana Studio 记住 git ssh 密码

    我找不到任何有关如何获得 Aptana Studio 的内置 git 支持来记住执行推 拉操作的 ssh 密码的指南 信息 有人有什么想法吗 Aptana Studio 实际上是内置的 GIT 程序 它将在 Windows 上的 C Use
  • `git push` -- 没有输出,什么也没有发生

    touch test git add test git commit m test git push u origin master 这奏效了 该文件已上传到存储库 rm test cp R website website git rm t
  • Gerrit 和 Active Directory

    我正在尝试设置 Gerrit 以使用我们的公司 Active Directory 进行身份验证 我知道很多人都设法让它发挥作用 但它对我来说不起作用 如果我运行一个ldapsearch命令如下我得到了正确的结果 所以我知道我的搜索字符串是正
  • Phonegap使用命令行工具添加插件

    我是phonegap的新手 我按照phonegap官方网站中定义的步骤使用命令行工具创建项目 nodejs 我成功创建项目并添加平台 但是 当我尝试添加插件时出现以下错误 命令 cordova plugin add https git wi
  • 如何创建名称中带有正斜杠的标签

    当我已经有了类似的标签时 有什么方法可以创建名称中带有正斜杠的 git 标签吗 假设我有 1 16 0 标签 并且我想创建 1 16 0 1 0 0 标签 git tag 1 16 0 1 0 0 error refs tags 1 16
  • 尝试克隆一个 git 存储库,但它卡在克隆到中

    我使用的是 Windows 10版本 10 0 19042 内部版本 19042 GIT Ver 2 32当尝试使用 git bash 执行以下命令时git clone depth 1 b carla https github com Ca
  • Git:如何正确合并两个功能完全不同的分支?

    想象一下这样一种情况 同一个项目有两个分支 第一个分支的一部分对另一个分支进行了大幅重构 但有一段时间 您需要保持两个分支的功能 因此您需要对两个分支进行错误修复和关键功能添加 有时是以不对称的方式 在某些时候 您必须将重构的分支合并到原始
  • 哪些 git hooks 适用于“git rebase --continue”?

    我正在尝试为我的组织构建一组 git hook 脚本 我想使用的一个脚本 仅用于我自己的多个项目 将是检查git rebase continue我的代码中没有留下任何冲突标记 lt lt lt lt lt or gt gt gt gt gt
  • Gitolite git 克隆错误

    我正在尝试在我的服务器 Macos 服务器 上设置 gitolite 我按照此处找到的安装文档中的说明进行操作 http sitaramc github com gitolite doc 1 INSTALL html http sitara
  • 在推送后检索孤立的提交对象 --force

    Doing push force总是有点冒险 这里有一个例子 说明它如何产生一些问题 例如远程丢失修订版本 假设 有一个人Bob已更新远程master分支来自B to C 还有另外一个人Mike还没有获取此更新并且HEAD of his m
  • 以非交互式方式查找合并提交的正确父级

    我正在准备 svn2git 迁移 同时https github com nirvdrum svn2git https github com nirvdrum svn2git虽然非常有用 但我仍然遇到了一些恶作剧 我已经清理掉了大部分 但还剩
  • GitLab:无法将代码推送到服务器?

    我已经在 Ubuntu 服务器上安装了 GitLab 一切似乎都工作正常 除了我无法向服务器推送 拉取 克隆 当我按下时 我收到一般错误消息 fatal Could not read from remote repository Pleas
  • PHP Github Pull 脚本错误“权限被拒绝(公钥)”

    我已经设置了一个 PHP 脚本来执行 GitHub 拉取 这包含在我的 Github 文件夹中 home mysite public html github github pull php 我的服务器已经有 SSH 公钥 就像我执行git
  • BitBucket 应用程序密码:git 命令行访问有哪些权限?

    我了解如何为 BitBucket 创建应用程序密码 如中所述Atlassian 的应用程序密码信息 https support atlassian com bitbucket cloud docs app passwords and 这个答
  • 如何在 EGit 中创建正确的新本地和远程分支组合?

    我想在 Egit 中执行以下操作 git checkout b newbranch git push u origin newbranch 这给了我一个新的本地分支 将其推送到上游服务器并创建正确的跟踪参考 我如何在 Egit 中做同样的事
  • 如何从 Git 存储库中删除选定的提交日志条目,同时保留其更改?

    我想从线性提交树中删除选定的提交日志条目 以便这些条目不会显示在提交日志中 我的提交树看起来像 R A B C D E HEAD 我想删除 B 和 C 条目 以便它们不会显示在提交日志中 但应保留从 A 到 D 的更改 也许通过引入单个提交

随机推荐

  • 使用FFMPEG将WebM转为MP4或MKV

    PS5 自带的录像功能导出的格式是WebM 同时视频是HDR默认60帧 我们有时需要转成其他格式保存 下面两个命令可以将其转成mkv或者mp4格式 ffmpeg i test webm vf zscale t linear npl 100
  • C++基础之const

    C 中什么是常量 常量 在程序运行期间不能发生改变的变量 常量不限制类型 但是在定义之后值不可修改 c 中定义常量有两种方法 使用宏定义 define预处理器 来自C语言 define DEF WINDOW WIDTH 720 使用cons
  • java简单毕设_[手把手教你做毕设](专栏介绍)

    引子 我并没有写过毕设相关的博文 但是每周都有不少人咨询我毕设相关的系统开发问题 OK 好像很久很久以前 写过一个 JSP Servlet培训班作业管理系统 说实话写得一般 确实一般 不是谦虚 以致于心有愧欠 感觉貌似大概可能也许差不多 误
  • 55个mes项目解决方案及案例_我调查了 20 个 MES 项目实施情况,发现了这些

    本文首发于 智能制造社区 作者Raylan 昨天智能制造社区的 Raylan 同学设计了个简单的问卷 想分析下 MES 项目的执行情况 所以就在我们社区微信群里简单做了抽样 此次调研是匿名调研 问卷接收数量大约在44 63之间 问卷返回数量
  • 从0开始写Vue项目-Vue实现用户数据批量上传和数据导出

    从0开始写Vue项目 环境和项目搭建 慕言要努力的博客 CSDN博客 从0开始写Vue项目 Vue2集成Element ui和后台主体框架搭建 慕言要努力的博客 CSDN博客 从0开始写Vue项目 Vue页面主体布局和登录 注册页面 慕言要
  • IDEA常用快捷键(Windows)

    Ctrl S 保存文件 Ctrl C 复制 Ctrl X 剪切 Ctrl V 粘贴 Ctrl Z 撤销 Ctrl Y 重做 Ctrl F 查找 Ctrl Shift F 全局查找 Ctrl R 替换 Ctrl D 复制当前行或选中区域到下一
  • 编写一个名为collatz()的函数,它有一个名为number的参数。如果参数是偶数,那么collatz()就打印出number // 2,并返回该值。如果number是奇数,collatz()就打印

    要求 编写一个名为collatz 的函数 它有一个名为number的参数 如果参数是偶数 那么collatz 就打印出number 2 并返回该值 如果number是奇数 collatz 就打印并返回3 number 1 使用软件 vsco
  • 深度学习 情感分析_使用深度学习进行情感分析

    深度学习 情感分析 介绍 Introduction The growth of the internet due to social networks such as Facebook Twitter Linkedin Instagram
  • Mule的学习(一、mule的认知)

    参考 https blog csdn net a victory article details 70216772 https blog csdn net lishehe article details 33394895 https www
  • U盘读写速度优化

    从android设备上向U盘上传文件 时间比较长 优化后 时间大幅度缩短 经过几次测试 8k的缓存可以达到传输速度和容错率的平衡点 超过了太容易出错 低于8k传输速度会下降 byte buffer new byte 1024 8 使用的第三
  • MMEditing如何添加自己的新模型

    如何使用商汤的框架MMEditing添加一个自己的新模型嘞 因为自己平时做超分辨率 所以这里用BasicVSR的改动作为例子 一般需要在MMEditing中添加三个文件 配置 backbones以及restores BasicSR原始的地址
  • LeetCode-312.戳气球、动态规划

    有 n 个气球 编号为0 到 n 1 每个气球上都标有一个数字 这些数字存在数组 nums 中 现在要求你戳破所有的气球 如果你戳破气球 i 就可以获得 nums left nums i nums right 个硬币 这里的 left 和
  • C语言文件包含

    一个C语言程序由若干源程序文件组成 而一个源文件还可以将另一个源文件的全部内容包含进来 即将指定的源文件包含在当前文件中 例如 下有两个源文件file1 c和file2 c file1 c int max int x int y int z
  • 因易用性导致的TongWeb使用误区

    误区一 使用TongWeb企业版本 即按照 TongWeb7企业版用户手册 pdf 手册操作 安装好TongWeb后doc目录下有手册 TongWeb手册的正确观看顺序 1 最先看 TongWeb7快速使用手册 pdf 了解基本的安装 使用
  • 【基于用户的】协同过滤推荐算法(UserCF算法的实现)

    协同过滤算法在推荐算法领域应用十分广泛 主要有基于用户 UserCF 和基于物品 ItemCF 两种不同的类型 基于用户的推荐算法 它是一种发现兴趣相似的用户的算法 假如你正在建设的是一个学习资源共享平台 你的用户群体有着大致稳定的专业与相
  • JAVA烟花原理

    java烟花原理 0 缘由 两天前无聊玩了下蜘蛛纸牌 发现最后胜利时的烟花效果挺漂亮的 琢磨Java能不能弄出来类似的效果 上网搜了一下 整合资料 基本全是抄的 之后自己写了一份 重点讲原理 源码放文末 上一个效果图 1 概述 其实烟花说白
  • Springboot整合Mybatis(注解开发)

    前言 Springboot整合Mybatis实现一个最基本的增删改查功能 整合的方式有两种一种是注解形式的 也就是没有Mapper xml文件 还有一种是XML形式的 我推荐的是使用注解形式 为什么呢 因为更加的简介 减少不必要的错误 1
  • 基于TensorFlow的花卉识别

    概要设计 数据分析 本次设计的主题是花卉识别 数据为TensorFlow的官方数据集flower photos 包括5种花卉 雏菊 蒲公英 玫瑰 向日葵和郁金香 的图片 并有对应类别的标识 daisy dandelion roses sun
  • ChatGPT研究分析:GPT-4做了什么

    前脚刚研究了一轮GPT3 5 OpenAI很快就升级了GPT 4 整体表现有进一步提升 追赶一下潮流 研究研究GPT 4干了啥 本文内容全部源于对OpenAI公开的技术报告的解读 通篇以PR效果为主 实际内容不多 主要强调的工作 是 Pre
  • openwrt: Makefile 框架分析

    本篇的主要目的是想通过分析Makefile 了解openwrt编译过程 着重关注以下几点 openwrt目录结构 主Makefile的解析过程 各子目录的目标生成 kernel编译过程 firmware的生成过程 软件包的编译过程 open