miniblink49 中 Skia 的集成构建教程:gclient + DEPS + GYP + ninja 从零搭建 📅 发布时间:2026/9/16 11:08:40 👁 浏览次数: miniblink49 中 Skia 的集成构建教程gclient DEPS GYP ninja 从零搭建【免费下载链接】miniblink49a lighter, faster browser kernel of blink to integrate HTML UI in your app. 一个小巧、轻量的浏览器内核用来取代wke和libcef项目地址: https://gitcode.com/GitHub_Trending/mi/miniblink49本文基于 miniblink49 仓库中收录的 Skia 官方教程 building.md完整讲解如何为一个使用 Skia 的应用搭建 gclient 依赖管理、DEPS 版本锁定、GYP 构建配置与 ninja 编译的全套工程基础设施并跑通第一个能打印SkPaint的最小程序。miniblink49 作为取代 wke 和 libcef 的轻量级 Blink 浏览器内核本身就通过 gclient GYP 体系集成 Skia见 third_party/skia/DEPS 与 third_party/skia/skia.gyp因此这篇教程描述的正是该仓库所采用的工具链方法论。读完本文你将掌握编写.gclient与DEPS文件锁定第三方库版本、用 GYP 生成 ninja 构建文件、以及理解skia_lib目标背后的组件库构成。前置条件四件套工具链与总体工作流教程假设开发环境已具备以下四个工具git—— 版本控制与代码检出gclient—— Chromium 生态的客户端依赖管理工具负责解析DEPS文件并检出全部依赖仓库GYPGenerate Your Projects—— 构建系统生成器本项目用它生成 ninja 配置文件ninja—— 实际执行编译的构建工具。教程的目标是搭出一个能编译并运行一个打印SkPaint的简单应用总体流程为六步创建远程仓库配置 gclient 并执行 sync创建DEPS文件以拉取第三方仓库Skia为DEPS拉取的目录配置 gitignore配置 GYP让gclient sync后自动运行 GYP。第一步gclient 配置 —— .gclient 文件与远程仓库同步第一步是搭建一个远程 git 仓库教程作者使用的是一个名为 UsingSkia 的仓库托管在任意 git 服务商上均可。仓库建好后用gclient config生成.gclient配置文件命令形如$ gclient config --namesrc 你的远程仓库地址.git该命令会写出如下.gclient文件solutions [ { name : src, url : 你的远程仓库地址.git, deps_file : DEPS, managed : True, custom_deps : { }, safesync_url: , }, ] cache_dir None其中name字段配置的就是仓库将被检出的目录名检出动作由gclient sync完成。gclient 会围绕 url 做一些魔法判断来确定仓库是 SVN 还是 GIT教程作者的经验是使用ssh://前缀、或保留 url 末尾的.git后缀能帮忙正确识别 SCM 类型$ gclient syncsync 会执行一串命令若远程仓库还是空的这里可能以报错结束这没有影响。完成后你应得到一个包含已检出 git 仓库的src目录。第二步DEPS 文件 —— 用 vars 与 deps 锁定 Skia 版本仓库创建好后即可创建src/DEPS文件。DEPS 文件被 gclient 用来检出应用的依赖仓库这里就是 Skiavars { skia_revision: a6a8f00a3977e71dbce9da50a32c5e9a51c49285, } deps { src/third_party/skia/: skia仓库地址.git Var(skia_revision), }DEPS文件此时有两个部分vars定义变量后续通过Var()访问器引用。这里定义了 Skia 的根目录约定、googlecode 仓库的短名以及将要使用的具体 Skia revision。作者特意将版本钉死pin在特定 revision上目的是让应用与 Skia 主干的变动隔离——任何检出该仓库的人使用的都是作者构建、测试过的同一版本 Skiadeps定义依赖。当前只有一个依赖检出到src/third_party/skia目录。DEPS 文件创建后需提交并推送到远程仓库然后$ gclient sync此时会看到大量文件被加入项目的输出。这也是创建.gitignore的好时机third_party/skia目录由 gclient 管理不应提交进你的仓库。对照miniblink49 仓库中的真实 DEPS 写法当前仓库的 third_party/skia/DEPS 就是这套机制的现成实例首行use_relative_paths True启用相对路径deps字典把每个依赖钉在精确的 commit hash 上例如third_party/externals/freetype锁定在VER-2-5-0-1标签、third_party/externals/zlib锁定在特定 commit。文件末尾的recursedeps [ common ]则声明要递归处理common这个共享依赖仓库——这与教程锁版本 管理第三方目录的意图完全一致只是依赖数量更多。第三步嵌套依赖问题 —— 第二个 solution此时会碰到一个问题Skia 自己也有一个DEPS文件定义了它构建所需的third_party库这些依赖没有被告检出来Skia 将构建失败。教程给出的解法是在.gclient文件中添加第二个 solution明确告诉 gclient 关于 Skia 的信息让它连带拉入所需依赖solutions [ { name : src, url : 你的远程仓库地址.git, deps_file : DEPS, managed : True, custom_deps : { }, safesync_url: , }, { name : src/third_party/skia, url : skia仓库地址.gita6a8f00a3977e71dbce9da50a32c5e9a51c49285, deps_file : DEPS, managed : True, custom_deps : { }, safesync_url: , }, ] cache_dir None教程作者指出这里有一点小麻烦Skia 的 revision 号在.gclient文件中被重复写了一遍与DEPS中各一份他当时希望找到通过DEPS文件统一管理的方式但此方案可用。配置完成后重新执行gclient sync会看到更多的仓库被检出src/third_party/skia/third_party/externals目录应已被填充。第四步GYP —— 包装脚本与目标文件包装脚本 gyp_using_skia最后一块基础设施是 GYP。本项目用它生成 ninja 配置文件。首先把 GYP 本身加进依赖即在DEPS的deps段新增一条检出到src/tools/gyp教程当时锁定 revision 1700——写作时的 head revision作者认为在自己的 DEPS 中使用 tip-of-tree revision 也是安全的src/tools/gyp: (Var(googlecode_url) % gyp) /trunk1700,注意此处用到的googlecode_url变量也需在vars段中定义。执行gclient sync后即可检出 GYP。为了运行 GYP教程创建了一个包装脚本src/build/gyp_using_skia#!/usr/bin/python import os import sys script_dir os.path.dirname(__file__) using_skia_src os.path.abspath(os.path.join(script_dir, os.pardir)) sys.path.insert(0, os.path.join(using_skia_src, tools, gyp, pylib)) import gyp if __name__ __main__: args sys.argv[1:] if not os.environ.get(GYP_GENERATORS): os.environ[GYP_GENERATORS] ninja args.append(--check) args.append(-I%s/third_party/skia/gyp/common.gypi % using_skia_src) args.append(os.path.join(script_dir, .., using_skia.gyp)) print Updating projects from gyp files... sys.stdout.flush() sys.exit(gyp.main(args))大部分是初始化代码真正关键的是两处args.append(-I%s/third_party/skia/gyp/common.gypi % using_skia_src)—— 告诉 GYP 用-I包含 common.gypi该文件定义 Skia 编译所需的必要变量args.append(os.path.join(script_dir, .., using_skia.gyp))—— 指明应用的主配置文件是src/using_skia.gyp。另外脚本默认把GYP_GENERATORS设为ninja并追加--check参数校验 gyp 文件语法。using_skia.gyp目标文件逐字段讲解应用目标定义在src/using_skia.gyp{ targets: [ { configurations: { Debug: { }, Release: { } }, target_name: using_skia, type: executable, dependencies: [ third_party/skia/gyp/skia_lib.gyp:skia_lib ], include_dirs: [ third_party/skia/include/config, third_party/skia/include/core, ], sources: [ app/main.cpp ], ldflags: [ -lskia, -stdliblibc, -stdc11 ], cflags: [ -Werror, -W, -Wall, -Wextra, -Wno-unused-parameter, -g, -O0 ] } ] }各字段含义configurations允许为Debug和Release构建配置不同标志本例中两者相同但作者仍显式定义因为这两个 configuration 名将直接决定out/下的子目录名target_name构建目标名将传递给 ninja同时也就是生成的可执行文件名typeexecutable产出可执行程序dependencies构建依赖先于本目标的源码构建。这里依赖 third_party/skia/gyp/skia_lib.gyp 中的skia_lib目标include_dirs编译时加入头文件搜索路径。应用需要引用 Skia 的config与core两个目录的头文件sources/ldflags/cflags源文件、链接器标志与编译器标志。注意cflags里-Werror把警告变成错误-g -O0则保留调试信息且不优化。佐证skia_lib 目标实际依赖什么结合仓库源码看 skia_lib.gypskia_lib并非单一目标而是聚合了一组组件静态库基础组件为core、codec、effects、images、opts、ports、sfnt、utils再按条件追加——x86 且非 Android 时加入opts_ssse3与opts_sse41优化库arm_neon 1时加入opts_neonskia_gpu开启时加入gpu.gyp:skgpu。也就是说教程中那行third_party/skia/gyp/skia_lib.gyp:skia_lib依赖实际拉入的是整个 Skia 核心静态库集合。而-I引入的 common.gypi 会在target_defaults中为所有 Skia 相关目标注入一组编译宏SK_INTERNAL、SK_GAMMA_SRGB、SK_GAMMA_APPLY_TO_A8、SK_SCALAR_TO_FLOAT_EXCLUDED并按skia_os/skia_arch_width等变量做交叉校验例如skia_os必须与OS兼容、skia_arch_width只能是 32 或 64其下层层包含的 common_variables.gypi 则定义了skia_os、skia_arch_type、skia_gpu、skia_shared_lib、skia_fast等数十个可通过GYP_DEFINES覆盖的构建变量——这正是教程强调必须用-I包含 common.gypi的原因没有它Skia 源码缺宏、缺变量无法通过 GYP 校验。第五步main.cpp —— 第一个 Skia 程序应用本身定义在src/app/main.cpp#include SkPaint.h #include SkString.h int main(int argc, char** argv) { SkPaint paint; paint.setColor(SK_ColorRED); SkString* str new SkString(); paint.toString(str); fprintf(stdout, %s\n, str-c_str()); return 0; }程序只做一件事创建一个SkPaint、把颜色设为红色、再把它toString输出到标准输出用以证明一切链接正确。当前仓库中对应的头文件确实存在于 third_party/skia/include/core/SkPaint.h与include_dirs中声明的include/core路径吻合。第六步首次 GYP 报错与 find_mac_sdk.py 的修复首次运行$ ./build/gyp_using_skia会得到一个报错Skia 在相对路径的tools目录下寻找一个名为find_mac_sdk.py的文件但它不存在。修复方式很直接——在DEPS文件中再补一条用 gclient 的File()函数检出单个文件src/tools/: File((Var(googlecode_url) % skia) /trunk/tools/find_mac_sdk.py Var(skia_revision)),File()声明这里检出一个单独的文件而不是整个仓库gclient sync后该文件会落进src/tools。需要说明这是当年 Skia 托管在 googlecode 时期的历史细节从当前仓库的third_party/skia/tools目录看find_mac_sdk.py已不复存在说明后续版本重构了该工具链但用 DEPS 补单个缺失文件的思路仍然通用。第七步ninja 生成与编译补齐文件后build/gyp_using_skia应能成功结束此时应生成一个out/目录内含Debug/和Release/两个子目录——对应using_skia.gyp里声明的两个 configurations。接着执行$ ninja -C out/Debug using_skia构建跑完后out/Debug/using_skia可执行文件被生成运行它即打印出那个SkPaint的字符串描述。至此从空仓库到可运行 Skia 应用的完整链路打通gclient 管依赖检出、DEPS 管版本与单文件补丁、GYP 管构建描述、ninja 管实际编译。第八步Autorun GYP —— DEPS 的 hooks 机制每次 sync 之后都要手动跑一遍build/gyp_using_skia有点烦。解法是在DEPS文件末尾追加hooks段它声明一组在gclient sync完成后执行的钩子动作hooks [ { # A change to a .gyp, .gypi or to GYP itself should run the generator. name: gyp, pattern: ., action: [python, src/build/gyp_using_skia] } ]其中pattern匹配变化范围此处为整棵树action是要执行的命令列表。加上该段后再次gclient sync你会看到 sync 流程结束时自动打印 GYP 文件更新的过程——依赖检出与构建文件再生成一个动作完成。小结这套工作流在 miniblink49 中的映射教程描述的六步工作流在 miniblink49 仓库中都有真实落点可作为延伸阅读版本锁定third_party/skia/DEPS 用 commit hash / 标签锁定 freetype、zlib、libpng、harfbuzz 等全部外部依赖GYP 目标体系third_party/skia/skia.gyp 与 third_party/skia/gyp/ 目录下的core.gyp、codec.gyp、effects.gyp、ports.gyp、skia_lib.gyp等目标文件构成 Skia 的组件化构建GYP 生成 VS 工程仓库中大量.gyp/.sln/.vcxproj文件共存如 gin.gyp、gin.sln、gin.vcxproj说明该工程同时用 GYP 生成 ninja/VS 两套构建产物与教程gyp 作为构建系统生成器的定位一致。需要提醒的适用前提本教程文本成文于 Skia 托管在 googlecode 的时期其中的仓库地址、find_mac_sdk.py细节属于历史内容按当前仓库的 googlesource 托管方式理解即可而 gclient DEPS 锁版本 GYP 生成 ninja 编译的总体方法论仍是理解 miniblink49 这类 Chromium 系内核集成工程的核心钥匙。【免费下载链接】miniblink49a lighter, faster browser kernel of blink to integrate HTML UI in your app. 一个小巧、轻量的浏览器内核用来取代wke和libcef项目地址: https://gitcode.com/GitHub_Trending/mi/miniblink49创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考