??xml version="1.0" encoding="utf-8" standalone="yes"?> 事情的v因是Q想找个?Windows ?Mac 的构建方案。第一考虑自然?CMakeQ毕竟基本上是事实标准了?/p>
但是研究了一?Modern CMakeQ也是?target 为核心的理念。但发现看了好几天文,也折腑և了可用的东西Q但仍然是没梳理清楚什么理c原理。然?CMake 本n语法很复杂Q再加上?target 一套概念,要给 target 讄各种属性之cȝQ有点强?OOP 的感觉……但其实我们只是需要一?include_dir ?lib_dir 而已Q其他都是Q云~ 但如果退回到传统模式Q不?Modern 概念呢,好像可以就Q但W一不去用一个工L最新模式,好像有点不上q的感觉Qpython 2 除外Q;W二QCMake 的两大痛点——语法特立独行、文档晦涩难懂——还是让人有点不爽?/p>
那蟩出来看别的选择呢?目前相对成熟的也只有 Google ?gn+ninja Ҏ(gu)了。gn q套东西?Chromium 里是完全配置好的Q用hq算手Q但要是独立拿出来呢Q就没那么便宜了。关键是它的 toolchain 是要自己定义的?/p>
之前q在公司搞客L的时候,大家׃ Chromium 里面?build、build_overrides {等东西全部拷出来,好家伙,几百 MB 甚至?G。但是公叔R嘛,没h干不干净Q怎么快怎么来。后来又看到 Google 自家?Crashpad 里面也用了这套构建,但工具链被简化了一下,?mini_chromium。这个比 Chromium 里的多了,是可以拿q来直接用的Q缺一些配|可以自己加。但是呢Q像我们q种z癖Q仍然是受不鸟的。所以呢Q我们要q干净净的徏立一套工具链?/p>
首先Q我们明定位。gn ?ninja 都是开发机上需要预装的Q不是Y件提供的。Chromium 的搞法是自己提供Qgn 的文也说让开发者提供工兗但q套思\跟传l的理念是冲H的。同Ӟ自己安装工具成本是比较低的: 自己下蝲的设|到 PATHQ测? 希望做到提供一?git repoQ用?clone 到自己项目的 build 目录Q然后用者只要在 .gn 文g里配|?/p>
可以用我们提供的工具链,?PC 三端q行构徏?/p>
使用者的唯一负担是~写自己?BUILD.gn 首先我们?gn 的文,以及它的例程 simple_build 里的工具N|: https://gn.googlesource.com/gn/+/HEAD/examples/simple_build/build/toolchain/BUILD.gn q个是可以直接用的,只不q只?linux 端(当然 mac 也能用)。我们再l合 chrome 里的工具N|,q行一些完善?/p>
首先我们了解 gn 体系需要的最配|是什么?/p>
W一Q它需要在根目录写一?.gn 文gQ在里面定义 buildconfigQ指到另一个文Ӟ一般是 W二、BUILDCONFIG.gn 里面需要设|默认工具链Q也是写一?/p>
W三、定义工具链Q如上例?//build/toolchain:gcc?/p>
需要在 build/toolchain 下徏?BUILD.gn 文gQ内Ҏ(gu) 最后在 toolchain 里定义各U?tool?/p>
q部分文在q里Qhttps://gn.googlesource.com/gn/+/main/docs/reference.md#func_tool 单复qC下,可被定义的工hQ?/p>
Q其他的先不看了) 我们来看一?https://gn.googlesource.com/gn/+/HEAD/examples/simple_build/build/toolchain/BUILD.gn 的一些关键配|: 可以看到Qcc ?cxx 执行 command 后,生成 .o 文gQ然后这?.o 文g可以作ؓ alink、solink、link ?inputsQ被它们 command l箋使用Q最后输出静态库、动态库以及可执行文件?/p>
其余属性可以查文档了解含义?/p>
主要配置在这里:https://source.chromium.org/chromium/chromium/src/+/main:build/toolchain/gcc_toolchain.gni 也是 gcc 的,?simple_build 里的大同异Q没有特别的?/p>
主要配置在这里:https://source.chromium.org/chromium/chromium/src/+/main:build/toolchain/apple/toolchain.gni 区别有: 基本上是Ҏ(gu)上面分析的要炚w|,最l结果在此:https://github.com/Streamlet/gn_toolchain 新增的一些差异有Q?/p>
增加全局参数 is_debugQ可以在 mac 下生?dSYM 不?python 脚本Q直接是 mac 下加了一?templateQapp_bundleQ用来生?xxx.appQ主要配|来自于 create_bundle 文里的例子 win 下增加了一些配|集 动?静态链?CRTQ?/build/config/win:console_subsystem?/build/config/win:static_runtime 控制台程序、Win32 E序Q?/build/config/win:console_subsystem?/build/config/win:windows_subsystem q个其实一般情况下用不着Q只要入口函数是 main/WinMainQlink 默认是控制台程?Win32 E序 XP 支持Q?/build/config/win:console_subsystem_xp?/build/config/win:windows_subsystem_xp 具体实现是链接参数加 /SUBSYSTEM:CONSOLE,5.01 ?/SUBSYSTEM:WINDOWS,5.01。关键是后面的版本号 5.01Qؓ了加版本号则必须指定子系l名Uͼ所以分?console_subsystem_xp ?windows_subsystem_xp。又Qxp q里提供了两?subsystem 的配|集Q非 xp 也提供两个?/p>
但是我们没有?_WIN32_WINNT=0x0501、WINVER=0x0501、_USING_V110_SDK71_Q也没有指定必须使用 7.0 版本?SDKQ这些都是非必须的,只要不用?XP 以后d?API 卛_。用者可以在自己?target 里面定义q些宏?/p>
提供一个用案例:https://github.com/Streamlet/gn_toolchain_sample 因ؓ它以 git submodule 形式引用?https://github.com/Streamlet/gn_toolchainQ所?git clone 以后Q需? 然后在根目录执行Q(保 gn ?ninja 已经?PATH 中) 卛_?/p>
Mac 下会额外生成一?objc 目 objc_console_application 以及一?app_bundleQns_application.app?/p>
Win 会额外生成一?Win32 目 win32_application?/p>
Win 下需要先执行一?Visual Studio 带的命o行环境,?VS 2022 Community ?“x64 Native Tools Command Prompt for VS 2022”,cl {工h会可用?/p>
如果要测?XPQ?2位)Q用“x86 Native Tools Command Prompt for VS 2022”进入,CD 到项目目录,执行Q?/p>
我们只用几十 KB 的大完成了跨端支持Q是很轻量的一个配|。如果?zhn)觉得实用q认可这U方式,Ƣ迎一hl护、完善?/p>
起先准备搞的?LFS 6.1Q因为只?6.1 有官方中文手册。但是我的宿ȝl是 Arch Linux 2010.05Q也许太CQ刚开始编?gcc 4.0.3 p不了。后来就攑ּ了,?6.7 的玩?/p> 说到底这是g很无聊的事情。打q的最多的命o是 q么一套操作重复个百来下,加上无休止的{待Q就成了?/p> 以ؓ成了Q结果出状况了: g好像大概可能它找不到盘Q而且我明明要 sda2 的,它却找了 sdb2?/p> W一Q在 8.4.2 grub-mkconfig -o /boot/grub/grub.cfg 的时候,grub的配|文件是利用它的命o自动生成的,l果它找错了。可能是因ؓ我一开始装的时候拿块硬盘是sdbQ它?yu)psdb了。或者是之前那条命o grub-install --grub-setup=/bin/true /dev/sda 我自作聪明地以ؓ它要实际操作Q把最后的sda换成了sdb的缘故吧?/p> W二是因为我?VMWare 上跑Q虚拟硬盘是 SCSI 的,~译内核之前没配|对。后来看C http://www.cnblogs.com/benben7466/archive/2009/04/01/1427404.htmlQ于是把 fusion mpt 中的全选上了(文章中的 Fusion MPT (base + ScsiHost) drivers 我没扑ֈQ于是全选了= =Q,重新~译内核Q启动成功?/p> 谨以此截囄念: 水账结束了。正文开始?/p> 我想谈谈?LFS 中的工具铑ֈ换的理解。请允许我把 binutil ?gcc UCؓ~译pȝQ把 glibc UCؓq行库。用下面q张囄单表CZ下: 首先Q利用宿ȝl的~译pȝ~译Z个依赖于宿主q行库的新的~译pȝQPass1Q,和独立的新的q行库(Pass1Q。然后再利用q行在宿主运行库上的新的~译pȝQPass1Q编译出依赖于新的运行库QPass1Q的新的~译pȝQPass2Q。这P产生了一个脱dȝ~译环境Q利用这个编译环境编译出其他工具Q一起作Z时系l用?/p> 再在临时pȝ中,~译出目标系l中要用的运行库QPass2Q和依赖于目标运行库QPass2Q的~译pȝQPass3Q。目标系l中的编译环境搭建完毕。最后用这个编译环境编译出目标pȝ上的其他软g?/p> 不知道这个陈q有没有问题Q如果没说错的话Q问题来了。其实,得到的时系l,已经是一个不依赖于宿ȝpȝ了,何不把这个作?LFS 的目标系l呢Q理׃乎只有“它更U净”之cȝ了。如果追求纯净Q多搞一遍是不够的,q是不纯净的;既然反正不纯净Qؓ啥多做一遍呢Q?/p> 由此Q我惛_了挺久以前我一直压抑在心里的问题:同一个环境下的编译器的升U问题。加入已l有?1.0 版的~译器执行文件和 2.0 版的~译器源代码Q要如何产生 2.0 版的~译器的执行文g呢?是拿 1.0 版的ȝ?2.0 的源代码Q然后直接发布?q是再用新的 2.0 版的~译器再~译一遍(两遍、三遍)Q?.1 版的 LFS 手册部分解决了这个疑问,它提C?gcc pass1 的时候做 bootstrapQ即~译一ơ后用生的新编译器~译W二遍,再用产生的新的编译器~译W三遍,比较W二遍与W三遍结果是否相同。(LFS 6.7无此要求。)不知道这里的相同是指逐字节相同吗Q如果是Q这在理Z可能吗?我的x是,已有?.0版可能存在一个固有问题(或者不UCؓ问题Q叫“特征”吧Q,它可能将影响到后面的一切,2.0 的编译器不管自D几遍Q或许L无法完全消灭来自 1.0 的某些媄响? 不知道现在理Z是怎样回答q个问题的。工E上又是如何对待q个问题的呢Q?/p> q也许是个比较深层次的问题。抑或只是一个很肤浅的问题,只是我心生执Ş了。期待解?~_~构徏pȝ安装
gn --version 以及 ninja --versionQ能q行卛_目标
buildconfig = "//build/BUILDCONFIG.gn"
工具链搭?/h2>
基础概念
buildconfig = "//build/BUILDCONFIG.gn"
set_default_toolchain("//build/toolchain:gcc")
toolchain("gcc") {
# ...
}
工具链中的工?/h3>
"cc": C ~译?br />
"cxx": C++ ~译?br />
"cxx_module": 支持 module ?C++ ~译?br />
"objc": Objective C ~译?br />
"objcxx": Objective C++ ~译?br />
"rc": Windows 资源脚本~译?br />
"asm": 汇编?br />
"swift": Swift ~译?/li>
"alink": 静态库链接?br />
"solink": 动态库链接?br />
"link": 可执行文仉接器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}}"
}
}
Ҏ(gu) Chromium 中的配置
Linux
Mac
Win
建立我们的工具链
gn gen out --args="is_debug=true"传入。默?is_debug ?falseQ开启所有优化?/p>
$ld ... && dsymutil ... && strip
使用案例
git submodule update --init一下?/p>
gn gen out
ninja -C out
gn gen out --args="target_cpu=\"x86\""
ninja -C out
ȝ
tar -xzvf ...
tar -xvjf ...
./configure ...
make
make install
rm -rf ...
]]>