ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

VLC contrib 构建系统完全指南:编写 rules.mak 的规则、依赖与最佳实践

VLC contrib 构建系统完全指南:编写 rules.mak 的规则、依赖与最佳实践 VLC contrib 构建系统完全指南编写 rules.mak 的规则、依赖与最佳实践【免费下载链接】vlcVLC media player - plays everything, runs anywhere. Code here: https://code.videolan.org/videolan/vlc项目地址: https://gitcode.com/gh_mirrors/vl/vlc本文以 VLC 官方仓库中 contrib/src/README.md 为骨架结合 contrib/src/main.mak 的实现细节与真实第三方包规则文件系统讲解 VLC contrib 第三方库构建系统的编写规范从每个包必须提供的两个 Makefile 目标.foo与.sum-foo到源码 tarball 的获取与校验、编译安装流程、条件化构建PKGS/PKGS_FOUND与依赖声明DEPS_*。读完本文你将能够为 VLC 新增一个第三方依赖包并编写出符合上游规范的rules.mak也能深刻理解make fetch、make install等命令背后的依赖解析机制。一、什么是 contribVLC 的第三方库构建层VLC 的源码树中有一个专门的contrib目录它负责为 VLC 核心与各模块准备所有第三方依赖库FFmpeg、libass、dav1d、x264、Qt 等上百个包。其入口位于 contrib/src/README.md而真正驱动整个构建流程的是 contrib/src/main.mak。每个第三方包在contrib/src/包名/目录下有一个rules.mak文件声明该包的下载、校验、编译与安装规则。main.mak通过一行include $(SRC)/*/rules.mak见 main.mak把全部包的规则汇总起来再统一对外暴露fetch、install、fetch-all、tools等顶层目标fetch: $(PKGS:%.sum-%) fetch-all: $(PKGS_ALL:%.sum-%) install: $(PKGS:%.%) tools: $(PKGS_TOOLS:%.dep-%)也就是说make fetch会为所有需要构建的包执行.sum-*目标下载并校验源码make install会为所有包执行.*目标编译并安装这与 contrib/src/help.txt 中列出的顶层命令一一对应make install 与 make 等价开始编译并安装全部依赖 make prebuilt 下载并安装预编译二进制 make tools 构建本机工具二进制 make list 列出所有包及其选择状态 make fetch 获取所需源码 tarball make fetch-all 获取全部源码 tarball make distclean 清理一切并撤销 bootstrap make mostlyclean 清理除源码 tarball 外的一切 make clean 全部清理 make package 打包预编译产物 make help 显示以上帮助二、最低要求每个包必须提供两个目标根据 contrib/src/README.md 的规定contrib 中的一个包foo在src/foo/rules.mak中至少必须提供两个 Makefile 目标.foo构建并安装该包.sum-foo获取或生成源码 tarball 并对其校验。其中foo即包名。这两个目标是整个 contrib 构建系统识别一个包的基本契约——main.mak中的PKGS_ALL : $(patsubst $(SRC)/%/rules.mak,%,$(wildcard $(SRC)/*/rules.mak))见 main.mak正是通过扫描所有rules.mak文件来自动发现包因此任何新增包都必须遵守这一命名约定。三、Tarball统一源码形态与校验机制3.1 为何总是使用源码 tarballREADME 特别强调了一个与旧版 VLC contrib 的关键差异即使源码来自版本控制系统VCS本系统也一律使用源码 tarball。这样做的目的有两个见 contrib/src/README.md支持离线构建或处于防火墙之后的环境满足源码合规要求source code requirements compliance。因此即使是 x264、rav1e 这类以 git 仓库为源头的项目最终也会被打包成.tar.xz放入contrib/tarballs/。3.2.sum-foo只负责校验.sum-foo通常依赖一个独立的下载目标。下载完成后.sum-foo只需校验 tarball 正确即可。README 给出的模板如下$(TARBALLS)/libfoo-$(FOO_VERSION).tar.bz2: $(call download,$(FOO_URL)) # This will use the default rule: check SHA-512 .sum-foo: libfoo-$(FOO_VERSION).tar.bz2注意两个细节download 宏main.mak中根据系统可用的下载工具自动选择实现见 main.mak——优先curl带-f -L --retry 3其次wget再次 FreeBSD 的fetch均不可用则直接报错Neither curl nor wget found。默认校验规则main.mak定义了默认的.sum-%模式规则见 main.mak.sum-%: $(SRC)/%/SHA512SUMS $(CHECK_SHA512) touch $也就是说只要包目录下存在SHA512SUMS文件.sum-foo就无需显式编写命令系统会调用sha512sum --check或shasum -a 512 --check、openssl dgst -sha512等后备实现见 main.mak完成 SHA-512 校验。README 中.sum-foo: libfoo-$(FOO_VERSION).tar.bz2一行没有写任何命令正是因为这个默认规则在生效。3.3 从 VCS 获取源码download_git 与 check_githash当源码来自 git 仓库时使用download_git宏生成 tarball。以 x264/rules.mak 为例$(TARBALLS)/x264-$(X264_VERSION).tar.xz: $(call download_git,$(X264_GITURL),,$(X264_HASH)) .sum-x264: x264-$(X264_VERSION).tar.xz $(call check_githash,$(X264_VERSION)) touch $download_git见 main.mak的实现是初始化一个 bare git 仓库 → fetch 指定分支 →git archive导出 tar → 用 xz 压缩成.tar.xz并把 commit hash 写入.githash文件check_githash见 main.mak则从.githash中提取 40 位 commit hash 与期望值比对确保源码确实是指定版本。3.4 下载失败时的镜像兜底main.mak还提供了download_pkg宏见 main.mak当上游 URL 下载失败时会自动回退到 VideoLAN 官方的 contrib 镜像https://downloads.videolan.org/pub/contrib并在成功时提示上传缺失包。大多数真实包如 ffmpeg/rules.mak、dav1d/rules.mak都采用download_pkg而非裸的download。四、编译安装.foo目标的完整流程4.1 目录名即就绪标志README 强调了一个重要的工程细节.foo依赖的是源码目录名因此必须保证目录名只在该目录中源码完全就绪后才出现否则会破坏 Makefile 依赖对目录来说这不是小问题对文件则无碍。README 给出的标准模板libfoo: libfoo-$(FOO_VERSION).tar.bz2 .sum-foo $(UNPACK) # to libfoo-$(FOO_VERSION) ### apply patches here ### # last command: make the target directory $(MOVE) .foo: libfoo $(MAKEBUILDDIR) $(MAKECONFIGURE) $(MAKEBUILD) $(MAKEBUILD) install touch $4.2 关键宏的底层实现这些宏在 main.mak 中均有精确定义$(UNPACK)main.mak根据 tarball 后缀调用tar xfo支持.tar.gz、.tgz、.tar.bz2、.tar.xz、.tar.zst或unzip解包并保证目录被清理重建。$(APPLY)main.mak在解包目录内以patch -fp1应用补丁用法为$(APPLY) $(SRC)/foo/xxx.patch。$(MOVE)main.makmv $(UNPACK_DIR) $ touch $把解包目录改名为目标目录名并 touch从而“制造”出依赖目录——这正是上一节所述“目录名即就绪标志”的实现手段。$(MAKEBUILDDIR)main.mak创建独立的vlc_build构建目录out-of-tree 构建并处理旧 config.status/distclean。$(MAKECONFIGURE)/$(MAKECONFDIR)main.mak进入构建目录、携带HOSTVARS编译器、CFLAGS/CXXFLAGS/LDFLAGS 等见 main.mak执行上游configure并追加HOSTCONFmain.mak——统一注入--prefix$(PREFIX)、--build--host--target、--enable-static --disable-shared以及--with-pic非 Windows等全局参数。$(MAKEBUILD)main.makmake -C $(BUILD_DIR)行首的让 make 即使在-n等模式下也执行该递归 make。touch $目标完成后留下时间戳文件供后续依赖判断增量状态。4.3 真实示例FFmpegautotools 补丁链ffmpeg/rules.mak 是体系中最复杂的包之一它的下载、校验、解包、打补丁、编译各环节都有代表性$(TARBALLS)/ffmpeg-$(FFMPEG_VERSION).tar.xz: $(call download_pkg,$(FFMPEG_URL),ffmpeg) .sum-ffmpeg: ffmpeg-$(FFMPEG_VERSION).tar.xz ffmpeg: ffmpeg-$(FFMPEG_VERSION).tar.xz .sum-ffmpeg $(UNPACK) $(APPLY) $(SRC)/ffmpeg/dxva_vc1_crash.patch $(APPLY) $(SRC)/ffmpeg/h264_early_SAR.patch # ... 其余 7 个补丁略 ... $(MOVE) .ffmpeg: ffmpeg $(MAKEBUILDDIR) $(MAKECONFDIR)/configure $(FFMPEGCONF) $(MAKEBUILD) $(MAKEBUILD) install-libs install-headers touch $可以看到.ffmpeg并不是使用$(MAKECONFIGURE)通用宏而是直接$(MAKECONFDIR)/configure $(FFMPEGCONF)把包特有的配置追加在通用配置之后。FFMPEGCONF中包含了大量针对 VLC 场景的裁剪例如--enable-static --disable-shared、--disable-doc、--disable-avdevice、--disable-bsfs、--enable-bsfvp9_superframe用于把 VP9 封装进 webm/mkv等并针对 Linux/Windows/macOS/Android/WatchOS/交叉编译分别追加架构与平台参数。安装时特意用install-libs install-headers而不是全量install避免安装无用的程序与文档。五、条件化构建PKGS、PKGS_FOUND 与 need_pkg5.1 PKGS决定“要构建哪些包”README 规定构建规则应尽可能自动判断某个包对 VLC 是否有用有用的包应列入PKGS特殊变量。例如# FFmpeg is always useful PKGS ffmpeg # DirectX headers are useful only on Windows ifdef HAVE_WIN32 PKGS directx endif # x264 is only useful when stream output is enabled ifdef BUILD_ENCODERS PKGS x264 endif真实包中ffmpeg/rules.mak 的PKGS ffmpeg与 dav1d/rules.mak 的PKGS dav1d都是无条件添加VLC 永远需要它们而 x264/rules.mak 则把PKGS x264放在ifdef BUILD_ENCODERS与ifdef GPL双重条件内同时排除 WinRTHAVE_WINSTORE。5.2 间接依赖不进 PKGSREADME 特别强调如果一个包只是另一个包的依赖、而非 VLC 的直接依赖不应加入PKGS——构建系统会自动通过依赖机制见下一节把它构建出来。5.3 PKGS_FOUND系统已提供则跳过当目标系统如原生 Linux/BSD 发行版已经提供了该包时应把包名加入PKGS_FOUND构建系统将跳过该包的编译。典型写法是通过need_pkg宏查询 pkg-config# Asks pkg-config if foo version 1.2.3 or later is present: ifeq ($(call need_pkg,foo 1.2.3),) PKGS_FOUND foo endifneed_pkgmain.mak执行pkg-config交叉编译时优先$(HOST)-pkg-config并输出版本为空表示满足要求。真实案例ffmpeg/rules.makPKGS_FOUND ffmpeg的前提是系统提供libavcodec 57.37.100、libavformat 53.21.0与libswscalex264/rules.mak按x264 0.148与x264 0.153分别判定普通 x264 与 10-bit x264x26410b。5.4 顶层如何合并选择main.mak中main.mak完整展现了选择算法# Apply automatic selection ( remove distro packages): PKGS_AUTOMATIC : $(filter-out $(PKGS_FOUND),$(PKGS)) # Apply manual selection (from bootstrap): PKGS_MANUAL : $(sort $(PKGS_ENABLE) $(filter-out $(PKGS_DISABLE),$(PKGS_AUTOMATIC))) # Resolve dependencies: PKGS_DEPS : $(filter-out $(PKGS_FOUND) $(PKGS_MANUAL),$(sort $(foreach p,$(PKGS_MANUAL),$(DEPS_$(p))))) PKGS : $(sort $(PKGS_MANUAL) $(PKGS_DEPS))流程为先剔除已被系统提供的包PKGS_FOUND再叠加用户通过 bootstrap 手动启用PKGS_ENABLE与禁用PKGS_DISABLE的选择最后把所选包的依赖DEPS_*补齐得到最终的PKGS。若同一个包同时被启用与禁用会直接报错main.mak。make list目标main.mak会把上述每一类包分别打印出来是调试包选择状态的利器。六、依赖声明DEPS_* 变量与依赖传播机制6.1 非递归依赖与显式声明若包bar依赖包foo需定义DEPS_barDEPS_bar foo $(DEPS_foo)README 明确提醒依赖解析是非递归的因此必须显式写出$(DEPS_foo)。这在实践中几乎无影响因为目前不存在纯粹的二级嵌套依赖——例如 libass 依赖 FontConfigFontConfig 依赖 FreeType但 libass 本身也直接依赖 FreeType所以链式展开总能覆盖全。真实案例 ass/rules.makDEPS_ass freetype2 $(DEPS_freetype2) fribidi $(DEPS_fribidi) iconv $(DEPS_iconv) harfbuzz $(DEPS_harfbuzz)以及 ffmpeg/rules.makDEPS_ffmpeg zlib $(DEPS_zlib) gsm $(DEPS_gsm) openjpeg $(DEPS_openjpeg)FFmpeg 的依赖还会随条件变化而追加开启编码器时DEPS_ffmpeg lame $(DEPS_lame)Windows 下追加mingw12-fixes、d3d12启用了amf包时追加DEPS_ffmpeg amf $(DEPS_amf)见 ffmpeg/rules.mak。6.2 为何用而非:DEPS_bar必须用递归展开而非立即展开:这样即使DEPS_foo定义在DEPS_bar之后$(DEPS_foo)也能在真正使用时正确展开。这是 contrib 规则文件编写中最容易踩的坑之一。6.3 底层的 .dep-* 机制README 给出了实现说明而 main.mak 中的代码完整呈现了这套机制# Dummy dependency on found packages $(patsubst %,.dep-%,$(PKGS_FOUND)): .dep-%: touch $ # Real dependency on missing packages $(patsubst %,.dep-%,$(filter-out $(PKGS_FOUND),$(PKGS_ALL))): .dep-%: .% touch -r $ $ .SECONDEXPANSION: # Dependency propagation (convert DEPS_foo bar to .foo: .bar) $(foreach p,$(PKGS_ALL),.$(p)): .%: $$(foreach d,$$(DEPS_$$*),.dep-$$(d))其效果正如 README 所述.bar: .dep-foo .dep-foo: .foo # 若 foo 需要构建 .dep-foo: # 若 foo 已被系统提供空目标直接 touch因此规则作者只需要设置DEPS_bar其余全部交给main.mak自动完成。注意依赖传播使用了.SECONDEXPANSION与$$延迟展开保证DEPS_*在第二遍展开时已被全部定义。七、构建系统的多后端支持不只有 autotools除传统的 autotools 流程外main.mak还为 CMake、Meson 与 Rust 项目提供了等价宏CMakeCMAKECONFIG使用独立构建目录与 Ninja 生成器CMAKE_GENERATOR ? Ninja统一注入-DBUILD_SHARED_LIBS:BOOLOFF静态链接、-DBUILD_TESTING:BOOLOFF、安装前缀等main.mak交叉编译时通过gen-cmake-toolchain.py生成toolchain.cmakemain.mak。MesonMESON/MESONBUILD/MESONCLEANmain.mak固定使用--default-library static与 ninja 后端交叉编译时通过gen-meson-machinefile.py生成crossfile.meson。真实案例见 ass/rules.mak 与 dav1d/rules.mak二者都把crossfile.meson作为.ass/.dav1d的依赖。Rustmain.mak通过include $(SRC)/main-rust.makmain.mak引入 Rust 专属规则对应 contrib/src/main-rust.mak并配合 contrib/src/rust-toolchain.toml 锁定工具链版本。以 libass 为例其 Meson 规则同时展示了“条件配置随平台变化”的典型写法.ass: libass crossfile.meson $(MESONCLEAN) $(MESON) $(ASS_CONF) $(MESONBUILD) touch $其中ASS_CONF在非 Windows/macOS/Emscripten 平台追加-Dfontconfigenabled并依赖 fontconfigWindows 追加-DdirectwriteenabledmacOS 追加-Dcoretextenabled支持汇编优化的架构追加-Dasmenabledass/rules.mak。八、真实包案例速览包构建后端依赖DEPS_*平台/功能条件参考文件ffmpegautotoolszlib、gsm、openjpeglame/amf/Windows 补丁包BUILD_ENCODERS、BUILD_NETWORK、HAVE_WIN32/DARWIN/ANDROID…ffmpeg/rules.maklibassmesonfreetype2、fribidi、iconv、harfbuzzfontconfigHAVE_WIN32/DARWIN_OS、架构 ASM 支持ass/rules.makdav1dmeson无无dav1d/rules.makx264autotoolsgit 源码无BUILD_ENCODERS、GPL、非 WinRTx26410b 别名目标x264/rules.makx264 的规则还展示了两个进阶技巧一是别名包——x26410b不重复下载/编译只是.sum-x26410b: .sum-x264与.x26410b: .x264的轻量别名x264/rules.mak二是许可证门禁——$(REQUIRE_GPL)宏main.mak在未开启GPL选项时直接报错退出用于保护 GPL 许可证的包。九、编写 rules.mak 的检查清单综合 README 与main.mak的实现为 VLC contrib 新增一个包foo时建议按以下清单逐项核对目录与命名在contrib/src/foo/下创建rules.mak包目录名必须与.foo、.sum-foo中的名称一致以便被PKGS_ALL自动发现。源码获取定义 tarball 目标$(TARBALLS)/...使用download_pkg优先带 VideoLAN 镜像兜底VCS 源码用download_gitcheck_githash。校验提供SHA512SUMS文件并让.sum-foo依赖 tarball默认规则会自动做 SHA-512 校验或显式调用check_githash。解包与补丁$(UNPACK)后按需$(APPLY)应用补丁最后必须$(MOVE)生成依赖目录。构建安装.foo中先$(MAKEBUILDDIR)再按项目类型选择$(MAKECONFIGURE)autotools、$(CMAKE)CMake或$(MESON)Meson随后$(MAKEBUILD)或$(CMAKEBUILD)/$(MESONBUILD)并install最后touch $。条件选择直接有用的包PKGS foo系统可提供时通过need_pkg判定并PKGS_FOUND foo间接依赖绝不加入PKGS。依赖声明DEPS_foo ...使用递归赋值并显式展开二级依赖。平台与许可用HAVE_WIN32、HAVE_DARWIN_OS、BUILD_ENCODERS、GPL等变量做条件分支必要时用$(REQUIRE_GPL)做许可证门禁。遵循上述规范新包即可无缝融入 VLC 的依赖解析、离线构建与交叉编译体系这也是上游 contrib/src/README.md 要求每个 contrib 包作者遵守的最低契约。【免费下载链接】vlcVLC media player - plays everything, runs anywhere. Code here: https://code.videolan.org/videolan/vlc项目地址: https://gitcode.com/gh_mirrors/vl/vlc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表