了解更多开源请访问:开源基础软件社区https://ost.51cto.comOpenHarmony使用gn+ninja为开源项目维护构建。之前没接触过gn+ninja,是时候系统学习一下了。边学习边记录学习过程,希望对同样需要学习gn+ninja的朋友有所帮助。在本文中,我们将了解GN的编写规范、风格指南或最佳实践。也可以阅读官方英文原版内容docs/standalone.md1、NamingandorderingwithinthefileNamingandorderinginthefile(1)Locationofbuildfilesbuildfile的位置createsmorenearthecodebuildfiles比在顶层创建更少的构建文件更有意义;这与我们对GYP所做的形成鲜明对比。这使得查找构建文件、源文件和需要检查的内容变得更加容易,因为更改集中在特定的子目录中。--注意:所以如果你使用gn,你会看到更多的BUILD.gn。根目录下有BUILD.gn,每个子模块都有BUILD.gn,通过依赖调用。(2)Targets大多数BUILD.gn文件应该有一个与目录同名的目标。这个目标应该是第一个目标。其他的目标应该有一定的逻辑顺序,通常是比较重要的目标在前,单元测试目标在相应功能模块的目标之后。如果没有明确的顺序,请考虑按字母顺序排列。测试支持库应该是一个名为“test_support”的静态库,例如“//ui/compositor:test_support”。测试支持库应该包含库的非测试支持版本作为公共依赖项,以便测试程序只需要依赖test_support目标(而不是两者)。命名建议目标和配置应使用小写字母和下划线分隔单词命名,除非有充分的理由不这样做。源代码集source_set、组group、静态库static_library不需要有全局唯一的名字。为此类目标选择简短、无冗余的名称,而不必担心全局唯一性。例如,编写依赖项“//mojo/public/bindings”看起来比编写依赖项“//mojo/public/bindings:mojo_bindings”要好得多。共享库shared_library(以及扩展组件组件——注意:Chrome项目中有这样一个组件目标)必须有一个全局唯一的输出名称。为此类目标指定上面的非唯一短名称,然后为该目标指定全局唯一名称output_name。应为可执行文件和测试指定一个全局唯一的名称。从技术上讲,只有输出名称必须是唯一的,但由于只有输出名称出现在shell和bot中,如果名称与可执行文件出现的其他地方匹配,就不会那么混乱。(3)Configs配置单个target关联的config配置要和target同名,后面跟_config。如果目标名称是foo,则相应的配置名称是foo_config。config配置应该出现在使用它的相应目标之前。(4)示例src/foo/BUILD.gn如下:#Copyright2016TheChromiumAuthors。版权所有。#此源代码的使用受BSD样式许可证的约束,该许可证可以在LICENSE文件中找到。#foo的配置被命名为foo_config并且在file.config("foo_config")中紧跟在它之前{}#目标匹配路径名是第一个target.executable("foo"){}#foo的测试在它之后。test("foo_unittests"){}config("bar_config"){}source_set("bar"){}2.target内排序target内的排序推荐使用如下排序,outputfirst,dependenciessecond,构建涉及的内容在中间。output_name/visibility/testonlysourcescflags,include_dirs,defines,configs,这些可以根据情况排序public_depsdeps(1)Conditions只影响一个变量的简单条件(例如,添加单个源或为特定操作系统添加标志)可以位于它们影响的变量下。具有更大影响范围的更复杂的条件应该在底部。编写条件时,应尽量减少条件块的数量。3.格式化和缩进格式化和缩进GN包含一个用于定义格式化样式的内置代码格式化程序。一些额外的注意事项:变量是带下划线的小写字母splitwordslower_case_with_underscores注释应该是完整的句子,末尾有句点。编译器选项标志等应始终注释它们的作用以及需要该标志的原因。(1)Sources源文件优先只列出一次。您可以有条件地包含源文件,然后在它们不适用时有条件地排除它们,而不是将它们全部列在顶部。条件包含通常更清晰,因为文件只列出一次并且在阅读时更容易推理。sources=["main.cc",]if(use_aura){sources+=["thing_aura.cc"]}if(use_gtk){sources+=["thing_gtk.cc"]}(2)应该按下Deps依赖项按字母顺序。当前文件中的deps应该先写,不能用文件名限定(只需要:foo)。除非出于某种原因需要相对路径名,否则其他部门应始终使用完全限定的路径名??。deps=[":a_thing",":mystatic","//foo/bar:other_thing","//foo/baz:that_thing",](3)导入使用完全限定路径进行导入:import("//foo/bar/baz.gni")#即使这个文件在foo/bar目录下4、Usage本节介绍如何选择使用不同的target类型,主要是source_set、shared_library、loadable_module和Components(这是一个由Chrome项目定义的模板,可以忽略)。(1)源集与静态库的比较source_set和static_library在大多数情况下,源集和静态库可以互换使用。如果您不确定使用什么,source_set几乎永远不会出错并且不太可能导致问题,但是在大型项目中使用正确类型的目标可能很重要,因此您应该注意以下权衡。静态库static_library遵循不同的链接规则。链接静态库时,只有包含未解析符号的目标文件才会包含在构建中。source_set链接每个目标文件,添加到最终的二进制文件中。如果最终将代码链接到组件、共享库或可加载模块中,通常需要使用source_set。这是因为从共享库中引用的没有符号的目标文件根本不会链接到最终库中。即使目标文件具有标记为已导出的符号,其目标取决于该共享库的要求,也会发生此遗漏。这将导致在链接后续目标时出现未定义的符号。单元测试(以及任何其他具有副作用的静态初始值设定项)必须使用source_set。gtestTEST宏创建注册测试的静态初始值设定项。但是,由于没有代码引用目标文件中的符号,将测试链接到静态库然后链接到测试可执行文件意味着测试将被剥离。在某些平台上,静态库可能涉及复制构成它的目标文件中的所有数据。这会消耗更多的磁盘空间,并且对于具有非常大目标文件的配置中的一些非常大的库,可能会导致超过静态库大小的内部限制。源集没有此限制。一些目标根据构建配置在源集和静态库之间切换以避免此问题。一些平台(或工具链)可能支持所谓的“瘦档案”,没有这个问题;但您不能将其作为便携式解决方案。source_set可以没有源文件,如果没有,静态库将给出奇怪的特定于平台的错误。如果目标只有头文件(用于包含检查目的)或在某些平台上有条件地没有源文件,则使用source_set。在特定链接不需要大量符号的情况下(尤其是链接测试二进制文件时),将该代码放在static_library中可以显着提高链接性能。这是因为首先不考虑链接不需要的目标文件,而不是因为没有代码引用它而强制链接器在以后的传递中删除未使用的代码。(2)ComponentsvssharedlibrariesvssourcesetsComponents、sharedlibraries、source_set比较组件都是ComponentsChrome模板(而不是内置的GN概念),这里不再赘述。就像源代码集与静态库的权衡一样,对于何时应该使用组件没有硬性规定。使用组件可以显着加快链接速度,从而加快增量构建,但它们需要您考虑需要从目标中导出哪些符号。(3)Loadablemodulesversussharedlibraries可加载模块和共享库的比较共享库shared_library会列在依赖它的targettarget的链接行上,在应用程序启动和运行时由操作系统自动加载符号会自动解析。可加载模块loadable_module不会被直接链接,应用程序必须手动加载它。在Windows和Linux上,共享库和可加载模块生成相同类型的文件(分别为.dll和.so)。唯一的区别是它们如何链接到依赖于它的目标。在这些平台上,可加载模块的deps依赖与不会链接的共享data_deps依赖相同。在Mac上,这些目标有不同的格式:共享库将生成.dylib文件,可加载模块将生成.so文件。将loadable_module用于插件等。对于类似插件的库,最好同时使用目标类型的可加载模块(即使对于无关紧要的平台)和依赖它的目标的data_deps,这样从两个地方都清楚库将如何链接和加载。5.构建参数(1)范围构建参数的范围应限制在一个行为单元内,例如使能函数。通常,将在导入文件中声明一个参数,以与可以使用它的构建子集共享它。Chrome项目相关的一些styleguidelines可以查看原文,基本不用看懂。(2)Type参数支持所有GN语言类型,例如字符串、列表、条件、循环、范围等。在绝大多数情况下,布尔类型boolean是首选类型,因为大多数参数启用或禁用功能或包含.String字符串类型通常用于文件路径。字符串也用于枚举,尽管有时也使用整数。(3)命名约定命名约定尽管参数命名没有硬性规定,但有许多通用约定。如果要查看当前构建目录的带有参数名称和默认值的参数列表,请使用gnargsout/Debug--list--short。use_foo-指示要包含的依赖项或主要代码路径(例如use_open_ssl、use_ozone、use_cups)enable_foo-指示要启用的功能或工具(例如enable_google_now、enable_nacl、enable_remoting、enable_pdf)值is_foo-通常是全局状态描述符(例如is_chrome_branded、is_desktop_linux);非全局变量已弃用foo_use_bar-前缀可用于指示参数的有限范围(例如,rtc_use_h264、v8_use_snapshot)(4)变量变量命名约定在.gni文件中使用下划线作为顶级局部变量的前缀。此前缀可防止将变量导入其他构建文件。_this_var_will_not_be_exported=1but_this_one_will=26。总结这篇文章,我们学习了GN写作规范,风格指南,包括命名,排序,格式化和缩进的推荐用法,用法,格式参数等。了解更多关于open源码请访问:开源基础软件社区https://ost.51cto.com。
