建立一个简单干净的 gn+ninja 工具链
背景
事情的起因是,想找个跨 Windows 和 Mac 的构建方案。第一考虑自然是 CMake,毕竟基本上是事实标准了。
但是研究了一下 Modern CMake,也就是以 target 为核心的理念。但发现看了好几天文档,也折腾出了可用的东西,但仍然是没梳理清楚什么理念、原理。然后 CMake 本身语法就很复杂,再加上搞 target 一套概念,要给 target 设置各种属性之类的,有点强行 OOP 的感觉……但其实我们只是需要一个 include_dir 和 lib_dir 而已,其他都是浮云~
但如果退回到传统模式,不用 Modern 概念呢,好像可以将就,但第一不去用一个工具的最新模式,好像有点不上进的感觉(python 2 除外);第二,CMake 的两大痛点——语法特立独行、文档晦涩难懂——还是让人有点不爽。
那跳出来看别的选择呢?目前相对成熟的也只有 Google 的 gn+ninja 方案了。gn 这套东西在 Chromium 里是完全配置好的,用起来还算顺手,但要是独立拿出来呢,就没那么便宜了。关键是它的 toolchain 是要自己定义的。
之前还在公司搞客户端的时候,大家就从 Chromium 里面把 build、build_overrides 等等东西全部拷出来,好家伙,几百 MB 甚至上 G。但是公司里嘛,没人管干不干净,怎么快怎么来。后来又看到 Google 自家的 Crashpad 里面也用了这套构建,但工具链被简化了一下,叫 mini_chromium。这个比 Chromium 里的小多了,是可以拿过来直接用的,缺少一些配置可以自己加。但是呢,像我们这种洁癖,仍然是受不鸟的。所以呢,我们要干干净净的建立一套工具链。
构建系统安装
首先,我们明确定位。gn 和 ninja 都是开发机上需要预装的,不是软件提供的。Chromium 的搞法是自己提供,gn 的文档也说让开发者提供工具。但这套思路跟传统的理念是冲突的。同时,自己安装工具成本是比较低的:
- linux
- ninja 在主流包管理系统里已经有了,包名可能是 ninja 或 ninja-build,直接安装就可以
- gn 在部分包管理系统有,尝试包名 gn 或 gn-build 等,没有的话可以下载二进制版本,或者从源代码自行编译
- mac
- ninja 在 brew 里包名叫 ninja,在 MacPorts 里包名叫 ninja-build
- gn 在 brew 里没有,可以下载二进制版本;在 MacPorts 里叫 gn-devel
- win
自己下载的设置到 PATH,测试 gn --version 以及 ninja --version,能运行即可
目标
希望做到提供一个 git repo,使用者 clone 到自己项目的 build 目录,然后使用者只要在 .gn 文件里配置
buildconfig = "//build/BUILDCONFIG.gn"
就可以使用我们提供的工具链,在 PC 三端进行构建。
使用者的唯一负担就是编写自己的 BUILD.gn
工具链搭建
首先我们看 gn 的文档,以及它的例程 simple_build 里的工具链配置:
https://gn.googlesource.com/gn/+/HEAD/examples/simple_build/build/toolchain/BUILD.gn
这个是可以直接用的,只不过只有 linux 端(当然 mac 也能用)。我们再结合 chrome 里的工具链配置,进行一些完善。
基础概念
首先我们了解 gn 体系需要的最小配置是什么。
第一,它需要在根目录写一个 .gn 文件,在里面定义 buildconfig,指到另一个文件,一般是
buildconfig = "//build/BUILDCONFIG.gn"
第二、BUILDCONFIG.gn 里面需要设置默认工具链,也就是写一行
set_default_toolchain("//build/toolchain:gcc")
第三、定义工具链,如上例的 //build/toolchain:gcc。
需要在 build/toolchain 下建立 BUILD.gn 文件,内容是
toolchain("gcc") {
# ...
}
最后在 toolchain 里定义各种 tool。
工具链中的工具
这部分文档在这里:https://gn.googlesource.com/gn/+/main/docs/reference.md#func_tool
简单复述一下,可被定义的工具有:
- 编译工具:
"cc": C 编译器
"cxx": C++ 编译器
"cxx_module": 支持 module 的 C++ 编译器
"objc": Objective C 编译器
"objcxx": Objective C++ 编译器
"rc": Windows 资源脚本编译器
"asm": 汇编器
"swift": Swift 编译器 - 链接工具:
"alink": 静态库链接器
"solink": 动态库链接器
"link": 可执行文件链接器
(其他的就先不看了)
我们来看一下 https://gn.googlesource.com/gn/+/HEAD/examples/simple_build/build/toolchain/BUILD.gn 的一些关键配置:
toolchain("gcc") {
tool("cc") {
command = "gcc -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_c}} -c {{source}} -o {{output}}"
outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
# ...
}
tool("cxx") {
command = "g++ -MMD -MF $depfile {{defines}} {{include_dirs}} {{cflags}} {{cflags_cc}} -c {{source}} -o {{output}}"
outputs = [ "{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o" ]
# ...
}
tool("alink") {
command = "rm -f {{output}} && ar rcs {{output}} {{inputs}}"
outputs = [ "{{target_out_dir}}/{{target_output_name}}{{output_extension}}" ]
# ...
}
tool("solink") {
command = "g++ -shared {{ldflags}} -o $sofile $os_specific_option @$rspfile"
outputs = [ sofile ]
# ...
}
tool("link") {
command = "g++ {{ldflags}} -o $outfile -Wl,--start-group @$rspfile {{solibs}} -Wl,--end-group {{libs}}"
outputs = [ outfile ]
# ...
}
tool("stamp") {
command = "touch {{output}}"
}
tool("copy") {
command = "cp -af {{source}} {{output}}"
}
}
可以看到,cc 和 cxx 执行 command 后,生成 .o 文件,然后这些 .o 文件可以作为 alink、solink、link 的 inputs,被它们 command 继续使用,最后输出静态库、动态库以及可执行文件。
其余属性可以查文档了解含义。
对比 Chromium 中的配置
Linux
主要配置在这里:https://source.chromium.org/chromium/chromium/src/+/main:build/toolchain/gcc_toolchain.gni
也是 gcc 的,与 simple_build 里的大同小异,没有特别的。
Mac
主要配置在这里:https://source.chromium.org/chromium/chromium/src/+/main:build/toolchain/apple/toolchain.gni
区别有:
- 用 clang 系列编译工具,而不是 gcc
- alink 不是用 ar,而使用 libtool
- solink 的默认扩展名改成了 dylib
- 用了一个 linker_driver.py 来支持生成 dSYM,在里面调用了 dsymutil 和 strip
Win
- 编译用 cl,静态库链接用 lib,动态库和可执行文件的链接用 link
- lib_switch = "",lib_dir_switch = "/LIBPATH:";前两者 lib_switch = "-l",lib_dir_switch = "-L"
建立我们的工具链
基本上是根据上面分析的要点配置,最终结果在此:https://github.com/Streamlet/gn_toolchain
新增的一些差异有:
增加全局参数 is_debug,可以在
gn gen out --args="is_debug=true"传入。默认 is_debug 为 false,开启所有优化。mac 下生成 dSYM 不使用 python 脚本,直接是
$ld ... && dsymutil ... && stripmac 下加了一个 template:app_bundle,用来生成 xxx.app,主要配置来自于 create_bundle 文档里的例子
win 下增加了一些配置集
动态/静态链接 CRT://build/config/win:console_subsystem、//build/config/win:static_runtime
控制台程序、Win32 程序://build/config/win:console_subsystem、//build/config/win:windows_subsystem
这个其实一般情况下用不着,只要入口函数是 main/WinMain,link 默认就是控制台程序/Win32 程序
XP 支持://build/config/win:console_subsystem_xp、//build/config/win:windows_subsystem_xp
具体实现是链接参数加 /SUBSYSTEM:CONSOLE,5.01 或 /SUBSYSTEM:WINDOWS,5.01。关键是后面的版本号 5.01,为了加版本号则必须指定子系统名称,所以分了 console_subsystem_xp 和 windows_subsystem_xp。又,xp 这里提供了两个 subsystem 的配置集,非 xp 也提供两个。
但是我们没有加 _WIN32_WINNT=0x0501、WINVER=0x0501、_USING_V110_SDK71_,也没有指定必须使用 7.0 版本的 SDK,这些都是非必须的,只要不用到 XP 以后添加的 API 即可。使用者可以在自己的 target 里面定义这些宏。
使用案例
提供一个使用案例:https://github.com/Streamlet/gn_toolchain_sample
因为它以 git submodule 形式引用了 https://github.com/Streamlet/gn_toolchain,所以 git clone 以后,需要 git submodule update --init一下。
然后在根目录执行:(确保 gn 和 ninja 已经在 PATH 中)
gn gen out
ninja -C out
即可。
Mac 下会额外生成一个 objc 项目 objc_console_application 以及一个 app_bundle:ns_application.app。
Win 会额外生成一个 Win32 项目 win32_application。
Win 下需要先执行一下 Visual Studio 带的命令行环境,如 VS 2022 Community 的 “x64 Native Tools Command Prompt for VS 2022”,cl 等工具才会可用。
如果要测试 XP(32位),用“x86 Native Tools Command Prompt for VS 2022”进入,CD 到项目目录,执行:
gn gen out --args="target_cpu=\"x86\""
ninja -C out
总结
我们只用几十 KB 的大小完成了跨端支持,是很轻量的一个配置。如果您觉得实用并认可这种方式,欢迎一起来维护、完善。
建立一个简单干净的 gn+ninja 工具链的更多相关文章
- 通过myclipse建立一个简单的Hibernate项目(PS:在单元测试中实现数据的向表的插入)
Hibernate的主要功能及用法: Ⅰ.Hibernate封装了JDBC,使Java程序员能够以面向对象的思想对数据库进行操作 Ⅱ.Hibernate可以应用于EJB的J2EE架构,完成数据的持久化 ...
- idea破解版安装、配置jdk以及建立一个简单的maven工程
idea破解版安装.配置jdk,配置jdk环境变量以及建立一个简单的maven工程 一.idea破解版以及配置文件下载 下载网址:https://pan.baidu.com/s/1yojA51X1RU ...
- Django 学习笔记之六 建立一个简单的博客应用程序
最近在学习django时建立了一个简单的博客应用程序,现在把简单的步骤说一下.本人的用的版本是python 2.7.3和django 1.10.3,Windows10系统 1.首先通过命令建立项目和a ...
- Hyperledger Fabric 建立一个简单网络
Building you first network 网络结构: 2个Orgnizations(每个Org包含2个peer节点)+1个solo ordering service 打开fabric-sa ...
- django 建立一个简单的应用
本人的用的版本是python 2.7.3和django 1.10.5,Windows10系统 1.首先通过命令建立项目和app 找到django的安装路径,我的路径是:C:\Python27\Lib\ ...
- 学习用node.js建立一个简单的web服务器
一.建立简单的Web服务器涉及到Node.js的一些基本知识点: 1.请求模块 在Node.js中,系统提供了许多有用的模块(当然你也可以用JavaScript编写自己的模块,以后的章节我们将详细讲解 ...
- SmartSql使用教程(1)——初探,建立一个简单的CURD接口服务
一.引言 最近SmartSql被正式引入到了NCC,借着这个契机写一个使用教程系列 二.SmartSql简介[摘自官方文档] 1. SmartSql是什么? SmartSql = MyBatis + ...
- 【Java编程】建立一个简单的JDBC连接-Drivers, Connection, Statement and PreparedStatement
本blog提供了一个简单的通过JDBC驱动建立JDBC连接例程.并分别通过Statement和PreparedStatement实现对数据库的查询. 在下一篇blog中将重点比較Statement与P ...
- 搭建Vue.js环境,建立一个简单的Vue项目
基于vue-cli快速构建 Vue是近年来比较火的一个前端框架,所以搭建Vue.js环境,要装webpack,vue-cli,Vue 安装webpack命令如下 $ cnpm install webp ...
- Linux简单程序实例(GNU工具链,进程,线程,无名管道pipe,基于fd的文件操作,信号,scoket)
一, GNU工具链简介: (1)编译代码步骤: 预处理 -> 编译 -> 汇编 -> 链接: 预处理:去掉注释,进行宏替换,头文件包含等工作: gcc -E test.c -o te ...
随机推荐
- java多线程的两种创建方式
方式一:继承Thread类 1.创建一个继承于Thread类的子类 2.重写Thread类的run()方法---> 将此线程执行的操作声明在run()中 3.创建Thread类的子类的对象 4. ...
- jsp中使用Servlet查询SQLSERVER数据库中的表的信息,并且打印在屏幕上
jsp中使用Servlet查询SQLSERVER数据库中的表的信息,并且打印在屏幕上 1.JavaBean的使用 package com.zheng; public class BookBean { ...
- 日志处理logging
前言 什么是日志?有什么作用?日志是跟踪软件运行时所发生的事件的一种方法,简单来说它可以记录某时某刻运行了什么代码,当出现问题时可以方便我们进行定位. 由python内置了一个logging模块,用户 ...
- sentinel的四种流控规则介绍
sentinel的四种流控规则介绍 今天的内容我们主要围绕四个点进行展开介绍. 流控模式 :关联.链路 流控效果 :Warm Up.排队等待 这四点具体是什么意思呢? 首先启动项目:cloud-ali ...
- 基于BP神经网络的手MNIST写数字识别
import numpy import math import scipy.special#特殊函数模块 import matplotlib.pyplot as plt #创建神经网络类,以便于实例化 ...
- 手记系列之二 ----- 关于IDEA的一些使用方法经验
前言 本篇文章主要介绍的关于本人在使用IDEA的一些使用方法,一些常用设置,一些插件推荐和使用.请注意,本文特长,2w多字加上几十张图片,建议收藏观看~ 前提准备 idea官网: https://ww ...
- 如何通过Java导出带格式的 Excel 数据到 Word 表格
在Word中制作报表时,我们经常需要将Excel中的数据复制粘贴到Word中,这样则可以直接在Word文档中查看数据而无需打开另一个Excel文件.但是如果表格比较长,内容就会存在一定程度的丢失,无法 ...
- 如何使用webgl(three.js)实现3D储能,3D储能站,3D智慧储能、储能柜的三维可视化解决方案——第十七课
前言 上节课我们讲了<3D光伏发电>,与之配套的就是能量存储 这节课我们主要讲讲储能,储能站,在分布式能源系统中起到调节用对电的尖峰平谷进行削峰填谷的作用.特别是小型储能站,更加灵活,因地 ...
- 10 | Kubernetes一键部署利器:kubeadm
你好,我是张磊.今天我和你分享的主题是:Kubernetes一键部署利器之kubeadm. 通过前面几篇文章的内容,我其实阐述了这样一个思想:要真正发挥容器技术的实力,你就不能仅仅局限于对Linux容 ...
- Java-数组工具类Arrays
java.util.Arrays是一个与数组相关的工具类,里面提供了大量静态方法,用来实现数组常见的操作. toSting方法 public static String toString(数组):将参 ...