1# 开源构建规范
2
3## 概述
4
5为指导OpenHarmony的开发者开展构建工作,提升构建系统的可重复性、可维护性,提高产品构建质量,构建规范工作组分析总结了各种典型的构建问题,提炼相应的构建规则和建议,制订了本规范。
6
7## 构建总体原则
8
9**P01 构建过程自动化,从构建启动开始到构建最终结束,中间过程不能手工干预。**
10手工操作容易出错,且浪费时间。将所有的构建操作变成自动化的,从而使构建变得高效、可靠。
11
12**P02 构建工程和构建环境代码化。**
13使用高阶构建框架CMake/Maven/Gradle等描述构建工程;使用Ansible/Dockerfile等描述构建环境。
14使用高阶构建框架的目的是向构建人员隐藏构建系统的复杂性。
15
16**P03 构建过程可重复、可追溯。**
17管理构建依赖,始终显式指定固定依赖版本号,确保构建依赖版本一致;将构建环境信息/构建工程作为配置项纳入配置管理,确保构建工程可追溯。
18
19**P04 构建脚本简洁清晰,易于维护。**
20构建脚本也是代码,构建脚本首先是为阅读它的人而编写的,好的脚本应当可以像故事一样发声朗诵出来。
21
22**P05 构建标准化**
23构建目录结构、构建依赖、构建初始化、构建入口、命名等进行标准化约束,使得公司所有产品、平台和组件的构建风格一致,便于构建管理和维护。
24
25## 构建工程
26
27### 公共规则
28
29#### 一键式构建
30
31##### G.COM.01 采用构建脚本,按照交付单元实现一键式自动化构建。
32
33一键式自动化构建是指同一个构建环境下,从构建启动开始到最终结束(最终交付的包生成),中间过程禁止人工干预。
34人工干预活动包括但不限于:构建过程中,使用IDE界面进行手工设置、创建或者删除文件目录、创建文件、复制文件、移动文件、删除或者重命名文件、手工设置文件属性、压缩/解压缩文件等。
35交付单元是指可独立编译、加载、部署和运行的产品/平台/组件。
36
37【级别】要求
38
39【描述】一键式构建大幅降低构建人员操作复杂度。
40
41【错误示例】某组件的一键式构建只能通过CI系统触发,没有一键式的本地构建。
42
43【错误示例】某组件需要在Xplorer IDE界面手工设置内存映射地址后,再手工编译。
44
45【错误示例】某组件需要手工创建r6c03_view\r6c03_client_view目录。
46
47【正确示例】使用python脚本自动化创建目录:
48
49```python
50dir_src = os.getcwd()
51dir_client_view = r"r6c03_client_view"
52# 处理路径使用os.path可以屏蔽系统差异
53dir_mk = os.path.join(dir_src, dir_client_view)
54
55cmd = "{0} {1}".format("mkdir", dir_mk)
56cmd_re = subprocess.run(cmd)
57```
58
59#### 构建目录
60
61##### G.COM.02 构建过程中禁止删除或修改源代码文件及其目录结构。
62
63【级别】禁止
64
65【描述】
66
67- 构建过程中删除或修改源代码目录结构,会导致构建过程不可重复。
68
69- 构建过程中,构建输出(包括目标文件、临时文件和构建日志)不能污染源码目录;
70
71- 构建过程中,避免修改源文件,包括但不限于拷贝、移动、执行dos2unix进行了源代码的格式转换等,源文件的修改应该在构建前的代码准备阶段完成;
72
73- 工具自动生成源代码应该在构建前的准备阶段完成,如果构建过程中使用工具自动生成源代码,工具自动生成的源代码必须和已有源代码目录隔离,以便区分高价值的源代码和低价值的可重新生成的代码,降低构建系统的复杂性。
74
75【例外】构建补丁时,可能会新增或调整部分源代码。
76
77##### G.COM.12 构建过程中创建的文件和目录应提供合适的权限。
78
79【级别】要求
80
81【描述】构建过程中可能需要创建目标系统的目录或文件,这些目录和文件应符合权限最小化的设计。
82例如,构建过程中应尽量避免在Linux系统中创建“777”权限的目录或文件。
83
84Linux系统中常见的目录文件和权限可以参考《Linux安全配置操作规范》。
85
86#### 构建初始化
87
88##### G.COM.03 每个组件提供clean命令。
89
90- 当clean不带任何参数时,清除该层级构建工程下的所有目标文件、临时文件和构建日志,并递归调用下层构建工程的clean,使该构建工程恢复到初始状态;
91- 当clean带参数时,只清除与之对应的构建生成的目标文件、临时文件和构建日志。
92
93【级别】要求
94
95【描述】构建前进行clean是为了避免本次构建受到历史构建残留文件和构建日志的影响,确保构建可重复。必须支持不带任何参数clean;带参数的clean,是为了满足日常交付过程和开发人员本地构建的诉求,不作强制要求。
96
97【正确示例】
98
99```
100base_dir
101 |---build.suffix
102 |---logs
103 |---component_depository_1
104    |---build.suffix
105    |---logs
106 |---component_depository_2
107    |---build.suffix
108    |---logs
109
110#不带参数
111base_dir/build.suffix clean
112#....分别调用component_depository_1和component_depository_2的clean
113
114#带参数:组件名
115base_dir/build.suffix clean component_depository_1
116#....调用component_depository_1的clean
117
118#带参数
119component_depository_1/build.suffix clean makebin hert umpt
120#....调用component_depository_1的umpt单板链接任务的clean,支持详细参数的clean主要应用于内部开发和构建。
121```
122
123##### G.COM.04 每个组件发布构建,必须保证构建环境中没有历史构建遗留件。
124
125【级别】要求
126
127【描述】首次下载代码,构建环境已经初始化,构建环境本身确保没有历史构建遗留件,可以不用执行clean命令;如果执行过构建的,必须使用clean命令清除历史构建遗留件。
128
129#### 全量构建
130
131##### G.COM.05 对于版本发布构建,归档的产品全量交付件(含所依赖的所有平台和组件)必须全部重新编译,禁止使用增量编译,禁止使用手工替换文件等方式修改安装盘。
132
133版本发布构建是指产品(含所依赖的所有平台和组件)对外正式发布版本的构建。
134
135【级别】要求
136
137【描述】修改文件后增量编译,会导致部分二进制文件没有更新,造成新的安全编译选项未集成到版本,编译结果不一致。手工替换文件可能会造成构建不可重复、不一致。
138
139
140#### 构建配置
141
142构建配置数据和构建脚本分离,避免构建工程架构腐化。源码路径、编译选项、目标文件路径等配置与构建脚本放到不同的文件,降低构建脚本维护成本。
143
144##### G.COM.06 禁止使用与操作系统强绑定的文件(如excel)作为构建配置文件。建议使用跨平台的标准配置文件(如XML)来存放配置选项。
145
146【级别】要求
147
148【描述】使用excel作为配置文件带来的问题:
149
150- 产品和平台编译过程中,使用excel作为配置文件,都将调用微软的OfficeAPI,每次访问excel表格都会在后台打开excel,处理速度慢。
151
152- 大量的excel配置需要手动点界面进行操作,可管理性差。
153
154#### 构建日志
155
156##### G.COM.07 构建输出的日志简洁明晰,构建日志的格式为时间戳+模块名(可选)+日志信息等级+日志内容。
157【级别】要求
158
159【描述】建议时间戳格式采取“日期和时间”,如"MM/dd/yyyy HH:mm:ss"。
160
161日志信息等级分为error/warning/informational,级别可以全写,也可以简写;对应的简写为:
162
163| 级别(大小写都可以)| 简写(大小写都可以)|
164| :---------: | :--------------------------: |
165| error | ERROR |
166| warning | WARN |
167| information | INFO |
168
169建议使用“[]”作分隔符。
170
171【正确示例】
172[05/21/2020 00:12:40] [ERROR] mkdir: cannot create directory Permission denied.
173
174【例外】整个日志由工具自动输出的,可用使用以下方式跳过整个日志文件:在日志的最前方(尽可能靠前)输出"This project is built using "+工具名,如"This project is built using CMake."。
175
176##### G.COM.08 构建日志出现error信息表示构建失败,必须终止构建。
177
178【级别】要求
179
180【描述】出现error信息一般是需要人工干预的构建错误,例如配置的环境变量错误,工具的版本错误,操作系统错误等等;或者软件源代码不对。对于版本发布构建,必须消除构建过程中所有的error消息,不允许屏蔽构建error信息。
181
182【错误示例】某组件构建成功,但构建日志中包含大量的fail、Critical、cannot、not found、missing、no input files等异常信息,令人困惑。
183
184##### G.COM.09 构建日志文件只保留本次构建的日志,避免本次构建的日志与历史构建的日志混淆。
185
186【级别】要求
187
188【描述】构建日志文件保留历史构建日志会导致混淆错误,比如:最新构建是失败的,由于保留有历史成功构建日志,会误认为最新这次构建是成功的。
189
190##### G.COM.10 每条日志建议增加对应的模块名,用于问题的快速定界。
191
192【级别】建议
193
194【描述】在日志量较大时,很难快速锁定问题责任模块,需要在日志上加以区分。
195
196【例外】CMake等工具的原生日志,因为输出带有对应模块路径,可以界定问题边界,不用特殊增加模块名维测信息。
197
198#### 构建用户
199
200##### G.COM.11 禁止使用超级管理员用户root和系统用户执行构建,应该使用普通user账户执行构建。
201
202【级别】要求
203
204【描述】超级管理员用户root和系统用户具有比较高的系统权限,使用此类账户执行构建可能导致构建环境被篡改。
205
206安装态可以使用root用户;执行态使用普通user账户,如果需要使用sudo提升权限的,请遵守《身份和访问管理安全设计规范》。
207
208#### 构建输出文件
209
210##### G.COM.12 构建输出文件命名后缀遵守业界约定。
211
212【级别】要求
213
214【描述】错误的后缀命名令人误解。
215
216对lib库、obj等构建输出文件的文件缀,应遵从构建工具默认的命名规则。
217
218【错误示例】某文本文件命名为XXX.lib219
220【错误示例】某object文件命名为XXX.a221
222【错误示例】某静态库命名无后缀,命名为libxxx。
223
224【正确示例】业界如下网址可以查询常见的文件后缀命名约定:http://www.fileextension.org/ ,  https://fileinfo.com/ , https://www.file-extensions.org/, http://file-extension.net/225
226下面是一些常见的文件后缀的命名约定:
227
228| 文件后缀名 | 类型约定             | 文件后缀名 | 类型约定        |
229| ---------- | -------------------- | ---------- | --------------- |
230| .a         | 静态库               | .so        | 动态库          |
231| .o         | object文件           | .7z        | 7zip压缩文件    |
232| .tar       | tar存档文件          | .gz/.gzip  | GNU压缩存档文件 |
233| .pack      | java pack200压缩文件 | .rar/.rar5 | rar压缩包       |
234
235### C/C++构建工程
236
237#### 构建目录
238
239##### G.C&C++.01 构建目录结构标准化。
240
241构建目录按用途分为源树Source Tree、构建中间件树Build Tree、构建安装树Install Tree三种。
242
243- Source Tree是保存源码和构建脚本的目录。
244- Build Tree是保存构建中间件的目录,目录名称一般为"build"。
245- Install Tree是保存构建发布件的目录,目录名称固定为"output"。
246
247Source Tree、Build Tree和Install Tree目录隔离,互相不重叠,没有交集,即不允许一个目录同时承担两种及以上的用途,譬如一个目录既作为Source Tree存放源码,又作为Build Tree存放编译中间件,这是不允许的。
248
249Source Tree包含下列文件和目录:
250
251- 构建工具入口文件,如CMakeLists.txtCMakeLists.txt中通过add_subdirectory()命令添加子目录,CMake将自动迭代调用子目录中的CMakeLists.txt,并逐级向下展开。
252- build.suffix脚本文件,该文件是一键式构建入口,仅调用该脚本即可完成构建。".suffix"表示对应的构建脚本语言后缀,譬如".bat",".sh",".py"等。
253- config.suffix配置文件,该文件用于存放构建配置项,是唯一的配置文件入口。
254- 构建脚本目录,可选,如cmake目录,用于保存CMake脚本文件。CMake脚本文件包括宏、函数、toolchain等, CMakeLists.txt通过include()命令包含CMake脚本文件,并调用其中的宏、函数等。
255- 组件代码目录,用于存放各组件的源码及构建脚本。
256- 上述文件和目录,只有CMakeLists.txtbuild.suffixconfig.suffix这三个文件是必需的,其它文件或者目录仅用作示例,不强制要求。
257
258Build Tree包含下列目录:
259
260- build目录,用于存放构建中间件。该目录可能在构建过程中创建,在git库上可能没有该目录。
261- 有的工程已经将build目录用于保存构建脚本,可以创建别的目录作为Build Tree。
262
263Install Tree包含下列目录:
264
265- output目录,用于存放交付件。该目录可能在构建过程中创建,在git库上可能没有该目录。
266
267【级别】要求
268
269【描述】
270
271典型目录结构如下:
272
273```
274base_dir
275 |---CMakeLists.txt      ---|
276 |---build.suffix           |
277 |---config.suffix          |
278 |---cmake                  |--> Source Tree
279 |---component_1            |
280 |---component_2            |
281 |---......                 |
282 |---component_n         ---|
283 |---build               ------> Build Tree
284 |---output              ------> Install Tree
285```
286
287各组件的目录结构与顶层的目录结构类似,譬如:
288
289```
290component_1
291 |---CMakeLists.txt      ---|
292 |---build.suffix           |
293 |---config.suffix          |
294 |---cmake                  |--> Source Tree
295 |---module_1               |
296 |---module_2               |
297 |---......                 |
298 |---module_n            ---|
299 |---build               ------> Build Tree
300 |---output              ------> Install Tree
301```
302
303##### G.C&C++.02 构建过程中禁止以任何形式修改Source Tree。
304
305【级别】建议
306
307【描述】构建过程中修改Source Tree会导致构建过程不可重复。
308
309常见的修改Source Tree的操作有:
3101)打补丁
3112)打点
3123)裁剪
3134)自动生成源码
3145)先修改源码然后还原
3156)增加/修改/删除临时文件或者目录
3167)修改文件/目录属性或者格式,譬如修改文件可执行权限、dos2unix等
317
318建议解决方案如下:
3191)将代码拷贝到Build Tree,然后打补丁,编译。
3202)打点工具修改源码,使得构建过程不可信,因此禁止在构建过程中使用打点工具。应将打点后的代码上传到代码库,使用打点后的代码进行构建。
3213)裁剪是独立的源码交付需求,裁剪可以看做是代码准备阶段。裁剪前的版本和裁剪后的版本都必须满足在构建过程中不修改Source Tree。
3224)自动生成的源码应放在Build Tree下。
3235)先修改源码然后还原是掩耳盗铃,构建过程中源码已经发生了变更。
3246)临时文件或者目录都应该放在Build Tree下。
3257)必须保证代码库中的文件属性和格式是正确的,而不是构建时修改。
326
327检验Source Tree是否发生变化的方法之一:编译完成后在源码目录下执行git status命令,不能有任何变更。先修改后还原导致的Source Tree变更,通过git status可能检测不出来。
328
329【例外】
3301)git status检测到Build Tree和Install Tree这两个目录的变更是允许的。
3312)git status检测到由于裁剪导致的变更是允许的。
332
333##### G.C&C++.03 Windows构建根目录建议为D:\交付单元的名称+版本号(可选);Linux构建根目录建议为/usr1/交付单元的名称+版本号(可选)。
334
335【级别】建议
336
337【描述】构建根目录按交付单元的名称+版本号命名,禁止使用build或code等无法区分交付单元的目录名称。
338清晰的构建目录结构,便于测试人员配置构建参数、执行一键式构建入口和对比构建结果。
339根目录示例如下:
340
341```
342D:\Offering [Version,可选]或/usr1/Offering [Version,可选]
343```
344
345##### G.C&C++.04 构建过程中生成的所有中间件保存在Build Tree中。
346
347【级别】要求
348
349【描述】构建过程中产生的中间件包括构建工具CMake自动生成的makefile、构建脚本自动生成的源码、构建脚本拷贝的源码及补丁、编译产生的object文件、库文件、可执行程序、构建日志等等。如果中间件放在Build Tree以外的目录,势必污染Source Tree或者Install Tree。因此,所有中间件都要保存在Build Tree中。Build Tree仅用于保存构建中间件,不能将Source Tree下某个放置源码或者构建脚本的目录用作Build Tree。
350Build Tree下创建构建日志子目录logs,构建日志后缀文件命名为.log。
351
352##### G.C&C++.05 支持指定Source Tree和Install Tree以外的任意目录作为Build Tree。
353
354【级别】要求
355
356【描述】支持指定Source Tree和Install Tree以外的任意目录作为Build Tree,做到构建过程与目录无关。在哪个目录下执行构建,哪个目录就是Build Tree,编译中间件就保存在哪个目录下。Build Tree的目录名称一般为“build”,也可以使用其它名称。
357
358【正确示例】使用CMake系统变量CMAKE_BINARY_DIR和CMAKE_CURRENT_BINARY_DIR访问Build Tree,避免Build Tree与Source Tree产生耦合。
359
360##### G.C&C++.06 所有发布件保存在Install Tree中。
361
362【级别】要求
363
364【描述】本地编译场景下,发布件直接"install"到HOST Computer上并运行。交叉编译场景下,发布件并不在HOST Computer上运行,而是在TARGET Computer上运行。
365
366发布件包括库文件、可执行程序、包文件、头文件等,是组件对外的二进制接口。所有发布件都保存在Install Tree中,不应将发布件放在Install Tree以外的目录下。
367
368Install Tree只用于保存发布件,不应将编译中间件放在Install Tree中。
369
370##### G.C&C++.07 支持指定Source Tree和Build Tree以外的任意目录作为Install Tree。
371
372【级别】要求
373
374【描述】支持指定Source Tree和Build Tree以外的任意目录作为Install Tree,做到构建过程与目录无关。Install Tree的目录名称固定为“output”。
375
376【正确示例】CMake构建工程应支持通过系统变量CMAKE_INSTALL_PREFIX指定Install Tree的根目录。
377
378#### 构建入口
379
380##### G.C&C++.08 每个交付单元的构建入口单一。构建脚本入口名称统一命名为build.suffix,并且路径要求在构建根目录下。
381
382【级别】要求
383
384【描述】通过使用一致的构建入口点,构建过程可以变得更加高效和可自动执行。每个交付单元只有单一构建入口,便于一键式自动构建。
385
386【错误示例】如下构建有多个入口点,如果没有说明文档,无法确认哪一个入口是正确的,造成选择困难。
387build.bat
388build_all.sh
389build_v6.sh
390
391【正确示例】一键式构建脚本build.sh的典型写法如下:
392
393```bash
394#!/bin/bash
395
396if [ -d "build" ]; then
397    rm -fr build/*
398else
399    mkdir build
400fi
401
402if [ -d "output" ]; then
403    rm -fr output/*
404else
405    mkdir output
406fi
407
408cd build
409cmake ..
410
411cpu_processor_num=$(grep processor /proc/cpuinfo | wc -l)
412job_num=$(expr "$cpu_processor_num" \* 2)
413echo Parallel job num is "$job_num"
414make -j"$job_num"
415```
416
417##### G.C&C++.09 支持指定target进行构建。
418
419【级别】要求
420
421【描述】日常开发场景下,通过指定target编译,开发人员只需要编译修改了的代码,不需要编译全部代码,达到快速验证的目的。编译工程应支持指定target进行构建,从而满足灵活多变的编译调试需求。
422
423【正确示例】典型命令如下:
424
425```
426base_dir # cd build
427base_dir/build # cmake ..
428# 编译全部目标
429base_dir/build # make
430# 编译特定目标
431base_dir/build # make target_name
432```
433
434##### G.C&C++.10 支持重复编译。
435
436【级别】要求
437
438【描述】编译成功后,不对源代码做任何修改,不清理上次编译的中间件和发布件,不修改编译环境,再次执行编译,必须能重复编译成功。
439
440##### G.C&C++.11 支持增量编译。
441
442【级别】建议
443
444【描述】日常开发场景下,增量编译可以缩短编译时间,提高开发效率,因此建议支持增量编译。
445
446##### G.C&C++.12 支持并行编译。
447
448【级别】要求
449
450【描述】通过"make -jN"命令进行并行编译,可以提高编译速度。本规则仅适用于使用make工具的工程。
451
452支持jobserver统一调度,使整个工程的负载最优。不能出现下面两个告警:
453
454```
455warning: jobserver unavailable: using -j1.  Add '+' to parent make rule.
456warning: -jN forced in submake: disabling jobserver mode.
457```
458
459支持jobserver的方法如下:
460
4611. 通过$(MAKE)直接调用make命令
462
463    ```cmake
464    ExternalProject_Add(foo
465        SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/foo
466        CONFIGURE_COMMAND sh configure_ext.sh
467        BUILD_COMMAND $(MAKE)
468    )
469    ```
470
4712. 通过shell脚本调用make命令
472
473    ```cmake
474    ExternalProject_Add(foo
475        SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/foo
476        CONFIGURE_COMMAND sh configure_ext.sh
477        BUILD_COMMAND sh build_ext.sh $(MAKE)
478    )
479    ```
480
481   build_ext.sh内容如下:
482
483    ```bash
484    #!/bin/bash
485
486    make
487    ```
488
489   注意:build_ext.sh不需要解析和使用参数$(MAKE)。
490
4913. 通过python脚本调用make命令
492
493    ```cmake
494    ExternalProject_Add(foo
495        SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/foo
496        CONFIGURE_COMMAND sh configure_ext.sh
497        BUILD_COMMAND python build_ext.py $(MAKE)
498    )
499    ```
500
501   build_ext.py内容如下:
502
503    ```bash
504    #!/usr/bin/python
505    # -*- coding: UTF-8 -*-
506
507    import subprocess
508
509    def main():
510        child = subprocess.Popen("make", close_fds=False)
511        ret = child.wait()
512        return
513
514    if __name__ == '__main__':
515        main()
516    ```
517
518    注意:build_ext.py不需要解析和使用参数$(MAKE)。
519
520
521#### 构建依赖
522
523##### G.C&C++.13 定义一个构建依赖文件dependence.xml,文件中描述构建依赖的所有组件。构建脚本自动读取该依赖文件,用于制作最终的软件包。
524
525【级别】建议
526
527【描述】按照依赖文件进行软件包制作,避免在构建脚本中定义依赖组件,提高构建过程可维护性。
528
529#### 构建配置
530
531##### G.C&C++.14 构建根目录的config.suffix配置文件是整个交付项目唯一的配置入口。
532
533【级别】要求
534
535【描述】顶层的config.suffix中,应暴露最少的配置项,只需要用户配置的构建环境、构建工具相关的信息。
536
537【例外】如果构建配置的内容非常少,采取系统键值对配置项,配置文件可以命名成config.conf538
539### GN 编写规范
540
541#### 编译规范
542
543##### 规则1.1 禁止在gn中调用外部编译工具编译软件模块
544
545【级别】禁止
546
547【描述】需要将外部组件移植成gn的编译形式,避免编译过程对环境产生不必要的依赖,而且可获得编译框架提供的公共能力,包括不限于:安全编译选项,ASAN等。
548
549【反例】在gn中使用action调用automake和Make来编译三方组件。
550
551【例外】Linux Kernel 编译框架实际完成的用户态程序编译,内核完全可以在编译框架之外完成独立编译。某些平台实现为了实现一键编译,使用gn将内核编译加在编译过程中,是可以接受的。
552
553##### 规则1.2 禁止在模块的gn文件中,再次添加编译系统已经添加的安全编译选项
554
555【级别】禁止
556
557【描述】对于全局已经添加的默认选项,模块开发者应当知晓,不需要为了满足内外部规则再次添加。
558
559| 编译选项 | 编译参数    | 默认值     |
560|---------|------------|------------|
561| 栈保护   | -fstack-protector-strong| 开 |
562| Fortify Source | -D_FORTIFY_SOURCE=2 -O2	| 开 |
563
564【反例】在模块的编译添加 -fstack-protector-strong
565
566##### 规则1.3 禁止在gn中添加和默认编译选项相反的编译选项
567
568【级别】禁止
569
570【描述】默认的编译选项代表了系统的默认能力,自研模块有特殊情况需要去掉部分能力,必须有足有的理由。
571
572【反例】在自研模块中添加 -wno-unused 以消除编译告警。
573
574【例外】移植三方组件,或者使用因为三方组件时,可根据三方组件的要求覆盖默认的编译选项。
575
576##### 规则 2.1 使用gn format 对添加或者修改的gn文件进行格式化,满足格式和排版的需求
577
578【级别】要求
579
580##### 规则 2.2 编写action时,使用python而不是shell
581
582【级别】建议
583
584【描述】python 环境更容易保持统一,可以比较容易的多重操作系统上运行,并且扩展性可读性可测试更好。
585
586##### 规则 2.3 禁止在gn和ninja执行过程修改源码目录的内容
587
588【级别】禁止
589
590【描述】包括但不限于给源码目录打patch,向源码目录中拷贝,在源码目录中执行编译,在源码目录生成中间文件等。
591
592##### 规则 2.4 编译脚本的编码格式设置为utf-8,换行符设置为unix格式
593
594【级别】要求
595
596【反例】在windows上编写脚本后,使用了中文注释并保存为本地编码。
597
598